Merge "Stub out compat classes for Zoom and EvComp" into androidx-master-dev
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index c80cea4..baf38e0 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -3,6 +3,9 @@
   - name: Activity
     url: https://issuetracker.google.com/issues/new?component=527362&template=1189829
     about: File a bug or feature request for Activity
+  - name: Biometric
+    url: https://issuetracker.google.com/issues/new?component=559537&template=1214425
+    about: File a bug or feature request for Biometric
   - name: Fragment
     url: https://issuetracker.google.com/issues/new?component=460964&template=1182267
     about: File a bug or feature request for Fragment
diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml
index 3fd78ec..c1dee53d 100644
--- a/.github/workflows/presubmit.yml
+++ b/.github/workflows/presubmit.yml
@@ -85,6 +85,59 @@
         if: always()
         run: echo ::set-output name=status::${{ job.status }}
 
+  build-biometric:
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [macos-latest]
+    runs-on: ${{ matrix.os }}
+    needs: [setup, lint]
+    outputs:
+      status: ${{ steps.output-status.outputs.status }}
+    env:
+      group-id: "biometric"
+    steps:
+      - name: "Checkout androidx repo"
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 1
+
+      - name: "Setup JDK 11"
+        id: setup-java
+        uses: actions/setup-java@v1
+        with:
+          java-version: "11"
+
+      - 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:
+          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
+        if: always()
+        uses: actions/upload-artifact@v2
+        with:
+          name: artifacts_${{ env.group-id }}
+          path: ~/dist
+
+      - name: "Report job status"
+        id: output-status
+        if: always()
+        run: echo ::set-output name=status::${{ job.status }}
+
   build-fragment:
     strategy:
       fail-fast: false
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ba9d647..4fc8df1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,6 +10,7 @@
 
 You can start contributing to any of the following library groups from GitHub:
   - [Activity](https://developer.android.com/guide/components/activities/intro-activities)
+  - [Biometric](https://developer.android.com/training/sign-in/biometric-auth)
   - [Fragment](https://developer.android.com/guide/components/fragments)
   - [Navigation](https://developer.android.com/guide/navigation)
   - [Paging](https://developer.android.com/topic/libraries/architecture/paging)
@@ -45,6 +46,7 @@
 ```
 androidx
   -- activity
+  -- biometric
   -- fragment
   -- navigation
   -- paging
@@ -52,7 +54,7 @@
   -- work
 ```
 
-**Note:** For other projects, you will still need to use the Gerrit workflow used by the Android Open Source Project (AOSP). For more information, please look at the [README](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:README.md).
+**Note:** For other projects, you will still need to use the Gerrit workflow used by the Android Open Source Project (AOSP). For more information, please look at the [README](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:README.md).
 
 Fork the [androidx/androidx](https://github.com/androidx/androidx) repository.
 
@@ -120,7 +122,7 @@
 ./gradlew updateApi
 ```
 
-If you are adding new APIs, then you might **additionally need to update** [LibraryVersions.kt](https://github.com/androidx/androidx/blob/androidx-master-dev/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt) as well, before running the updateApi task. This is **relevant when the library’s API is frozen** (betas, rc’s and stable versions). For alpha versions, you don’t have to update this file.
+If you are adding new APIs, then you might **additionally need to update** [LibraryVersions.kt](https://github.com/androidx/androidx/blob/androidx-main/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt) as well, before running the updateApi task. This is **relevant when the library’s API is frozen** (betas, rc’s and stable versions). For alpha versions, you don’t have to update this file.
 
 This helps the AndroidX project keep track of API changes and avoid inadvertently adding APIs or introduce backwards incompatible changes.
 
@@ -153,17 +155,17 @@
 
 ### The Pull Request Workflow
 
-AndroidX is primarily developed in [AOSP](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev). This flow simply mirrors pull requests from GitHub into Gerrit, For all intents and purposes, AOSP is the **single** **source of truth**, all changes will be merged in Gerrit and mirrored back to GitHub.
+AndroidX is primarily developed in [AOSP](https://android.googlesource.com/platform/frameworks/support/+/androidx-main). This flow simply mirrors pull requests from GitHub into Gerrit, For all intents and purposes, AOSP is the **single** **source of truth**, all changes will be merged in Gerrit and mirrored back to GitHub.
 
 Here is what a typical pull request workflow looks like:
 
-1.  Create a GitHub pull request from **your forked repository** to the androidx-master-dev branch on GitHub.
+1.  Create a GitHub pull request from **your forked repository** to the androidx-main branch on GitHub.
 2.  Sign the Contributor’s License Agreement at https://cla.developers.google.com/ to get @googlebot to give you the `cla: yes` label.
 3.  Your PR will be reviewed using the GitHub pull request flow. You can address the comments / suggestions in your forked repository and update the pull request as normal.
 4.  Once the changes look good, a Googler will Approve your pull request on GitHub.
 5.  Your PR will be **tested using GitHub workflows**. You can monitor these GitHub workflows by using the Actions tab in your forked repository.
 6.  Once your **pull request has been approved** by a Googler, it will also be **mirrored to AOSP Gerrit**. You can find the link to Gerrit under the status check, `import/copybara` left by `@copybara-service`, by clicking details.
-7.  Once **all** the checks in **Gerrit and GitHub** pass, your change will get merged in androidx-master-dev in AOSP and mirrored back to androidx-master-dev in GitHub. Congratulations, your change landed in AOSP!
+7.  Once **all** the checks in **Gerrit and GitHub** pass, your change will get merged in androidx-main in AOSP and mirrored back to androidx-main in GitHub. Congratulations, your change landed in AOSP!
 8.  Currently, your pull request will not get automatically closed when your changes are merged. So you will have to close the pull request manually. We are working on improving the workflow to address this.
 
 ### Running into problems?
diff --git a/README.md b/README.md
index d0a8c3a..c9828cb9 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,7 @@
 
 Note: The contributions workflow via GitHub is currently experimental - only contributions to the following projects are being accepted at this time:
 * [Activity](activity)
+* [Biometric](biometric)
 * [Fragment](fragment)
 * [Navigation](navigation)
 * [Paging](paging)
@@ -55,14 +56,14 @@
 3. Create a directory for your checkout (it can be any name)
 
 ```bash
-mkdir androidx-master-dev
-cd androidx-master-dev
+mkdir androidx-main
+cd androidx-main
 ```
 
 4. Use `repo` command to initialize the repository.
 
 ```bash
-repo init -u https://android.googlesource.com/platform/manifest -b androidx-master-dev --partial-clone --clone-filter=blob:limit=10M
+repo init -u https://android.googlesource.com/platform/manifest -b androidx-main --partial-clone --clone-filter=blob:limit=10M
 ```
 
 5. Now your repository is set to pull only what you need for building and running AndroidX libraries. Download the code (and grab a coffee while we pull down the files):
@@ -113,7 +114,7 @@
 ```
 
 ### Continuous integration
-[Our continuous integration system](https://ci.android.com/builds/branches/aosp-androidx-master-dev/grid?) builds all in progress (and potentially unstable) libraries as new changes are merged. You can manually download these AARs and JARs for your experimentation.
+[Our continuous integration system](https://ci.android.com/builds/branches/aosp-androidx-main/grid?) builds all in progress (and potentially unstable) libraries as new changes are merged. You can manually download these AARs and JARs for your experimentation.
 
 ## Running Tests
 
diff --git a/activity/README.md b/activity/README.md
index 764187e..559daab 100644
--- a/activity/README.md
+++ b/activity/README.md
@@ -7,7 +7,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/activity)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/activity/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/activity/)
 
 [Reference documentation](https://developer.android.com/reference/androidx/classes.html)
 
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index e683bb6..1159550 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -27,6 +27,7 @@
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel"))
     api(projectOrArtifact(":savedstate:savedstate"))
     api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-savedstate"))
+    implementation("androidx.tracing:tracing:1.0.0")
 
     androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
     androidTestImplementation(KOTLIN_STDLIB)
diff --git a/activity/activity/src/androidTest/AndroidManifest.xml b/activity/activity/src/androidTest/AndroidManifest.xml
index 8ad02b8..5176eb8 100644
--- a/activity/activity/src/androidTest/AndroidManifest.xml
+++ b/activity/activity/src/androidTest/AndroidManifest.xml
@@ -23,6 +23,7 @@
         <activity android:name="androidx.activity.LifecycleComponentActivity"/>
         <activity android:name="androidx.activity.EagerOverrideLifecycleComponentActivity"/>
         <activity android:name="androidx.activity.LazyOverrideLifecycleComponentActivity"/>
+        <activity android:name="androidx.activity.ReportFullyDrawnActivity"/>
         <activity android:name="androidx.activity.ViewModelActivity"/>
         <activity android:name="androidx.activity.SavedStateActivity"/>
         <activity android:name="androidx.activity.ContentViewActivity"/>
diff --git a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityReportFullyDrawnTest.kt b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityReportFullyDrawnTest.kt
new file mode 100644
index 0000000..70e6672
--- /dev/null
+++ b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityReportFullyDrawnTest.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.activity
+
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.testutils.withActivity
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class ComponentActivityReportFullyDrawnTest {
+
+    @Test
+    fun testReportFullyDrawn() {
+        with(ActivityScenario.launch(ReportFullyDrawnActivity::class.java)) {
+            withActivity {
+                // This test makes sure that this method does not throw an exception on devices
+                // running API 19 (without UPDATE_DEVICE_STATS permission) and earlier
+                // (regardless or permissions).
+                reportFullyDrawn()
+            }
+        }
+    }
+}
+
+class ReportFullyDrawnActivity : ComponentActivity()
diff --git a/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt b/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt
index 0b2f103..afb2ff0 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt
@@ -310,6 +310,41 @@
     }
 
     @Test
+    fun testUnregisterAfterSavedState() {
+        val lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+        var resultReturned = false
+        val activityResult = registry.register("key", lifecycleOwner, StartActivityForResult()) { }
+
+        activityResult.launch(null)
+
+        val savedState = Bundle()
+        registry.onSaveInstanceState(savedState)
+
+        registry.unregister("key")
+
+        val restoredRegistry = object : ActivityResultRegistry() {
+            override fun <I : Any?, O : Any?> onLaunch(
+                requestCode: Int,
+                contract: ActivityResultContract<I, O>,
+                input: I,
+                options: ActivityOptionsCompat?
+            ) {
+                dispatchResult(requestCode, RESULT_OK, Intent())
+            }
+        }
+
+        restoredRegistry.onRestoreInstanceState(savedState)
+
+        restoredRegistry.register("key", lifecycleOwner, StartActivityForResult()) {
+            resultReturned = true
+        }
+
+        lifecycleOwner.currentState = Lifecycle.State.STARTED
+
+        assertThat(resultReturned).isTrue()
+    }
+
+    @Test
     fun testOnRestoreInstanceState() {
         registry.register("key", StartActivityForResult()) {}
 
diff --git a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
index 068fe6e..2c20d93 100644
--- a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
+++ b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
@@ -29,6 +29,7 @@
 import static androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult.EXTRA_INTENT_SENDER_REQUEST;
 import static androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult.EXTRA_SEND_INTENT_EXCEPTION;
 
+import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Context;
@@ -62,6 +63,7 @@
 import androidx.annotation.Nullable;
 import androidx.core.app.ActivityCompat;
 import androidx.core.app.ActivityOptionsCompat;
+import androidx.core.content.ContextCompat;
 import androidx.lifecycle.HasDefaultViewModelProviderFactory;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleEventObserver;
@@ -78,6 +80,7 @@
 import androidx.savedstate.SavedStateRegistryController;
 import androidx.savedstate.SavedStateRegistryOwner;
 import androidx.savedstate.ViewTreeSavedStateRegistryOwner;
+import androidx.tracing.Trace;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -685,4 +688,29 @@
     public final ActivityResultRegistry getActivityResultRegistry() {
         return mActivityResultRegistry;
     }
+
+    @Override
+    public void reportFullyDrawn() {
+        try {
+            if (Trace.isEnabled()) {
+                // TODO: Ideally we'd include getComponentName() (as later versions of platform
+                //  do), but b/175345114 needs to be addressed.
+                Trace.beginSection("reportFullyDrawn() for ComponentActivity");
+            }
+
+            if (Build.VERSION.SDK_INT > 19) {
+                super.reportFullyDrawn();
+            } else if (Build.VERSION.SDK_INT == 19 && ContextCompat.checkSelfPermission(this,
+                    Manifest.permission.UPDATE_DEVICE_STATS) == PackageManager.PERMISSION_GRANTED) {
+                // On API 19, the Activity.reportFullyDrawn() method requires the
+                // UPDATE_DEVICE_STATS permission, otherwise it throws an exception. Instead of
+                // throwing, we fall back to a no-op call.
+                super.reportFullyDrawn();
+            }
+            // The Activity.reportFullyDrawn() got added in API 19, fall back to a no-op call if
+            // this method gets called on devices with an earlier version.
+        } finally {
+            Trace.endSection();
+        }
+    }
 }
diff --git a/activity/activity/src/main/java/androidx/activity/result/ActivityResultRegistry.java b/activity/activity/src/main/java/androidx/activity/result/ActivityResultRegistry.java
index d491bf6..36d959f 100644
--- a/activity/activity/src/main/java/androidx/activity/result/ActivityResultRegistry.java
+++ b/activity/activity/src/main/java/androidx/activity/result/ActivityResultRegistry.java
@@ -272,7 +272,8 @@
                 new ArrayList<>(mRcToKey.keySet()));
         outState.putStringArrayList(KEY_COMPONENT_ACTIVITY_REGISTERED_KEYS,
                 new ArrayList<>(mRcToKey.values()));
-        outState.putBundle(KEY_COMPONENT_ACTIVITY_PENDING_RESULTS, mPendingResults);
+        outState.putBundle(KEY_COMPONENT_ACTIVITY_PENDING_RESULTS,
+                (Bundle) mPendingResults.clone());
         outState.putSerializable(KEY_COMPONENT_ACTIVITY_RANDOM_OBJECT, mRandom);
     }
 
diff --git a/annotation/annotation-experimental-lint/build.gradle b/annotation/annotation-experimental-lint/build.gradle
index 6c42ad7..bc810c7 100644
--- a/annotation/annotation-experimental-lint/build.gradle
+++ b/annotation/annotation-experimental-lint/build.gradle
@@ -51,9 +51,3 @@
     description = "Lint checks for the Experimental annotation library. Also enforces the " +
             "semantics of Kotlin @Experimental APIs from within Android Java source code."
 }
-
-tasks.withType(KotlinCompile).configureEach {
-    kotlinOptions {
-        freeCompilerArgs += ["-XXLanguage:-NewInference"]
-    }
-}
diff --git a/annotation/annotation/README.md b/annotation/annotation/README.md
index e65290d..dab54a9 100644
--- a/annotation/annotation/README.md
+++ b/annotation/annotation/README.md
@@ -7,7 +7,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/annotation)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/annotation/annotation/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/annotation/annotation/)
 
 [Reference documentation](https://developer.android.com/reference/androidx/classes.html)
 
diff --git a/annotation/annotation/api/1.2.0-beta01.txt b/annotation/annotation/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..a33e7601
--- /dev/null
+++ b/annotation/annotation/api/1.2.0-beta01.txt
@@ -0,0 +1,256 @@
+// Signature format: 4.0
+package androidx.annotation {
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AnimRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AnimatorRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AnyRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface AnyThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface ArrayRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AttrRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface BinderThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface BoolRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface CallSuper {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface CheckResult {
+    method public abstract String suggest() default "";
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD}) public @interface ChecksSdkIntAtLeast {
+    method public abstract int api() default -1;
+    method public abstract String codename() default "";
+    method public abstract int lambda() default -1;
+    method public abstract int parameter() default -1;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.FIELD}) public @interface ColorInt {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.FIELD}) public @interface ColorLong {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface ColorRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface ContentView {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface DimenRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Dimension {
+    method @DimensionUnit public abstract int unit() default androidx.annotation.Dimension.PX;
+    field public static final int DP = 0; // 0x0
+    field public static final int PX = 1; // 0x1
+    field public static final int SP = 2; // 0x2
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface DoNotInline {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface DrawableRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface FloatRange {
+    method public abstract double from() default java.lang.Double.NEGATIVE_INFINITY;
+    method public abstract boolean fromInclusive() default true;
+    method public abstract double to() default java.lang.Double.POSITIVE_INFINITY;
+    method public abstract boolean toInclusive() default true;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface FontRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface FractionRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface GuardedBy {
+    method public abstract String value();
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.FIELD}) public @interface HalfFloat {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface IdRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface InspectableProperty {
+    method public abstract int attributeId() default 0;
+    method public abstract androidx.annotation.InspectableProperty.EnumEntry[] enumMapping() default {};
+    method public abstract androidx.annotation.InspectableProperty.FlagEntry[] flagMapping() default {};
+    method public abstract boolean hasAttributeId() default true;
+    method public abstract String name() default "";
+    method public abstract androidx.annotation.InspectableProperty.ValueType valueType() default androidx.annotation.InspectableProperty.ValueType.INFERRED;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public static @interface InspectableProperty.EnumEntry {
+    method public abstract String name();
+    method public abstract int value();
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public static @interface InspectableProperty.FlagEntry {
+    method public abstract int mask() default 0;
+    method public abstract String name();
+    method public abstract int target();
+  }
+
+  public enum InspectableProperty.ValueType {
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType COLOR;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType GRAVITY;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType INFERRED;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType INT_ENUM;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType INT_FLAG;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType NONE;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType RESOURCE_ID;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface IntDef {
+    method public abstract boolean flag() default false;
+    method public abstract boolean open() default false;
+    method public abstract int[] value() default {};
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface IntRange {
+    method public abstract long from() default java.lang.Long.MIN_VALUE;
+    method public abstract long to() default java.lang.Long.MAX_VALUE;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface IntegerRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface InterpolatorRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD}) public @interface Keep {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface LayoutRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface LongDef {
+    method public abstract boolean flag() default false;
+    method public abstract boolean open() default false;
+    method public abstract long[] value() default {};
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface MainThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface MenuRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface NavigationRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE}) public @interface NonNull {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE}) public @interface Nullable {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface PluralsRes {
+  }
+
+  @Dimension(unit=androidx.annotation.Dimension.PX) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface Px {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface RawRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface RequiresApi {
+    method @IntRange(from=1) public abstract int api() default 1;
+    method @IntRange(from=1) public abstract int value() default 1;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface RequiresFeature {
+    method public abstract String enforcement();
+    method public abstract String name();
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PARAMETER}) public @interface RequiresPermission {
+    method public abstract String[] allOf() default {};
+    method public abstract String[] anyOf() default {};
+    method public abstract boolean conditional() default false;
+    method public abstract String value() default "";
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER}) public static @interface RequiresPermission.Read {
+    method public abstract androidx.annotation.RequiresPermission value() default @androidx.annotation.RequiresPermission;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER}) public static @interface RequiresPermission.Write {
+    method public abstract androidx.annotation.RequiresPermission value() default @androidx.annotation.RequiresPermission;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface RestrictTo {
+    method public abstract androidx.annotation.RestrictTo.Scope[] value();
+  }
+
+  public enum RestrictTo.Scope {
+    enum_constant @Deprecated public static final androidx.annotation.RestrictTo.Scope GROUP_ID;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP_PREFIX;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope SUBCLASSES;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope TESTS;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Size {
+    method public abstract long max() default java.lang.Long.MAX_VALUE;
+    method public abstract long min() default java.lang.Long.MIN_VALUE;
+    method public abstract long multiple() default 1;
+    method public abstract long value() default -1;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface StringDef {
+    method public abstract boolean open() default false;
+    method public abstract String[] value() default {};
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface StringRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface StyleRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface StyleableRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public @interface TransitionRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface UiThread {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface VisibleForTesting {
+    method @ProductionVisibility public abstract int otherwise() default androidx.annotation.VisibleForTesting.PRIVATE;
+    field public static final int NONE = 5; // 0x5
+    field public static final int PACKAGE_PRIVATE = 3; // 0x3
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface WorkerThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface XmlRes {
+  }
+
+}
+
diff --git a/annotation/annotation/api/public_plus_experimental_1.2.0-beta01.txt b/annotation/annotation/api/public_plus_experimental_1.2.0-beta01.txt
new file mode 100644
index 0000000..a33e7601
--- /dev/null
+++ b/annotation/annotation/api/public_plus_experimental_1.2.0-beta01.txt
@@ -0,0 +1,256 @@
+// Signature format: 4.0
+package androidx.annotation {
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AnimRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AnimatorRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AnyRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface AnyThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface ArrayRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AttrRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface BinderThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface BoolRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface CallSuper {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface CheckResult {
+    method public abstract String suggest() default "";
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD}) public @interface ChecksSdkIntAtLeast {
+    method public abstract int api() default -1;
+    method public abstract String codename() default "";
+    method public abstract int lambda() default -1;
+    method public abstract int parameter() default -1;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.FIELD}) public @interface ColorInt {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.FIELD}) public @interface ColorLong {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface ColorRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface ContentView {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface DimenRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Dimension {
+    method @DimensionUnit public abstract int unit() default androidx.annotation.Dimension.PX;
+    field public static final int DP = 0; // 0x0
+    field public static final int PX = 1; // 0x1
+    field public static final int SP = 2; // 0x2
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface DoNotInline {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface DrawableRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface FloatRange {
+    method public abstract double from() default java.lang.Double.NEGATIVE_INFINITY;
+    method public abstract boolean fromInclusive() default true;
+    method public abstract double to() default java.lang.Double.POSITIVE_INFINITY;
+    method public abstract boolean toInclusive() default true;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface FontRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface FractionRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface GuardedBy {
+    method public abstract String value();
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.FIELD}) public @interface HalfFloat {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface IdRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface InspectableProperty {
+    method public abstract int attributeId() default 0;
+    method public abstract androidx.annotation.InspectableProperty.EnumEntry[] enumMapping() default {};
+    method public abstract androidx.annotation.InspectableProperty.FlagEntry[] flagMapping() default {};
+    method public abstract boolean hasAttributeId() default true;
+    method public abstract String name() default "";
+    method public abstract androidx.annotation.InspectableProperty.ValueType valueType() default androidx.annotation.InspectableProperty.ValueType.INFERRED;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public static @interface InspectableProperty.EnumEntry {
+    method public abstract String name();
+    method public abstract int value();
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public static @interface InspectableProperty.FlagEntry {
+    method public abstract int mask() default 0;
+    method public abstract String name();
+    method public abstract int target();
+  }
+
+  public enum InspectableProperty.ValueType {
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType COLOR;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType GRAVITY;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType INFERRED;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType INT_ENUM;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType INT_FLAG;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType NONE;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType RESOURCE_ID;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface IntDef {
+    method public abstract boolean flag() default false;
+    method public abstract boolean open() default false;
+    method public abstract int[] value() default {};
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface IntRange {
+    method public abstract long from() default java.lang.Long.MIN_VALUE;
+    method public abstract long to() default java.lang.Long.MAX_VALUE;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface IntegerRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface InterpolatorRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD}) public @interface Keep {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface LayoutRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface LongDef {
+    method public abstract boolean flag() default false;
+    method public abstract boolean open() default false;
+    method public abstract long[] value() default {};
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface MainThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface MenuRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface NavigationRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE}) public @interface NonNull {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE}) public @interface Nullable {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface PluralsRes {
+  }
+
+  @Dimension(unit=androidx.annotation.Dimension.PX) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface Px {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface RawRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface RequiresApi {
+    method @IntRange(from=1) public abstract int api() default 1;
+    method @IntRange(from=1) public abstract int value() default 1;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface RequiresFeature {
+    method public abstract String enforcement();
+    method public abstract String name();
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PARAMETER}) public @interface RequiresPermission {
+    method public abstract String[] allOf() default {};
+    method public abstract String[] anyOf() default {};
+    method public abstract boolean conditional() default false;
+    method public abstract String value() default "";
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER}) public static @interface RequiresPermission.Read {
+    method public abstract androidx.annotation.RequiresPermission value() default @androidx.annotation.RequiresPermission;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER}) public static @interface RequiresPermission.Write {
+    method public abstract androidx.annotation.RequiresPermission value() default @androidx.annotation.RequiresPermission;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface RestrictTo {
+    method public abstract androidx.annotation.RestrictTo.Scope[] value();
+  }
+
+  public enum RestrictTo.Scope {
+    enum_constant @Deprecated public static final androidx.annotation.RestrictTo.Scope GROUP_ID;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP_PREFIX;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope SUBCLASSES;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope TESTS;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Size {
+    method public abstract long max() default java.lang.Long.MAX_VALUE;
+    method public abstract long min() default java.lang.Long.MIN_VALUE;
+    method public abstract long multiple() default 1;
+    method public abstract long value() default -1;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface StringDef {
+    method public abstract boolean open() default false;
+    method public abstract String[] value() default {};
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface StringRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface StyleRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface StyleableRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public @interface TransitionRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface UiThread {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface VisibleForTesting {
+    method @ProductionVisibility public abstract int otherwise() default androidx.annotation.VisibleForTesting.PRIVATE;
+    field public static final int NONE = 5; // 0x5
+    field public static final int PACKAGE_PRIVATE = 3; // 0x3
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface WorkerThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface XmlRes {
+  }
+
+}
+
diff --git a/annotation/annotation/api/restricted_1.2.0-beta01.txt b/annotation/annotation/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..a33e7601
--- /dev/null
+++ b/annotation/annotation/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,256 @@
+// Signature format: 4.0
+package androidx.annotation {
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AnimRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AnimatorRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AnyRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface AnyThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface ArrayRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface AttrRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface BinderThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface BoolRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface CallSuper {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface CheckResult {
+    method public abstract String suggest() default "";
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD}) public @interface ChecksSdkIntAtLeast {
+    method public abstract int api() default -1;
+    method public abstract String codename() default "";
+    method public abstract int lambda() default -1;
+    method public abstract int parameter() default -1;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.FIELD}) public @interface ColorInt {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.FIELD}) public @interface ColorLong {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface ColorRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface ContentView {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface DimenRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Dimension {
+    method @DimensionUnit public abstract int unit() default androidx.annotation.Dimension.PX;
+    field public static final int DP = 0; // 0x0
+    field public static final int PX = 1; // 0x1
+    field public static final int SP = 2; // 0x2
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface DoNotInline {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface DrawableRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface FloatRange {
+    method public abstract double from() default java.lang.Double.NEGATIVE_INFINITY;
+    method public abstract boolean fromInclusive() default true;
+    method public abstract double to() default java.lang.Double.POSITIVE_INFINITY;
+    method public abstract boolean toInclusive() default true;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface FontRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface FractionRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface GuardedBy {
+    method public abstract String value();
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.FIELD}) public @interface HalfFloat {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface IdRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface InspectableProperty {
+    method public abstract int attributeId() default 0;
+    method public abstract androidx.annotation.InspectableProperty.EnumEntry[] enumMapping() default {};
+    method public abstract androidx.annotation.InspectableProperty.FlagEntry[] flagMapping() default {};
+    method public abstract boolean hasAttributeId() default true;
+    method public abstract String name() default "";
+    method public abstract androidx.annotation.InspectableProperty.ValueType valueType() default androidx.annotation.InspectableProperty.ValueType.INFERRED;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public static @interface InspectableProperty.EnumEntry {
+    method public abstract String name();
+    method public abstract int value();
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public static @interface InspectableProperty.FlagEntry {
+    method public abstract int mask() default 0;
+    method public abstract String name();
+    method public abstract int target();
+  }
+
+  public enum InspectableProperty.ValueType {
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType COLOR;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType GRAVITY;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType INFERRED;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType INT_ENUM;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType INT_FLAG;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType NONE;
+    enum_constant public static final androidx.annotation.InspectableProperty.ValueType RESOURCE_ID;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface IntDef {
+    method public abstract boolean flag() default false;
+    method public abstract boolean open() default false;
+    method public abstract int[] value() default {};
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface IntRange {
+    method public abstract long from() default java.lang.Long.MIN_VALUE;
+    method public abstract long to() default java.lang.Long.MAX_VALUE;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface IntegerRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface InterpolatorRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD}) public @interface Keep {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface LayoutRes {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface LongDef {
+    method public abstract boolean flag() default false;
+    method public abstract boolean open() default false;
+    method public abstract long[] value() default {};
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface MainThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface MenuRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface NavigationRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE}) public @interface NonNull {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE}) public @interface Nullable {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface PluralsRes {
+  }
+
+  @Dimension(unit=androidx.annotation.Dimension.PX) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface Px {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface RawRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface RequiresApi {
+    method @IntRange(from=1) public abstract int api() default 1;
+    method @IntRange(from=1) public abstract int value() default 1;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface RequiresFeature {
+    method public abstract String enforcement();
+    method public abstract String name();
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PARAMETER}) public @interface RequiresPermission {
+    method public abstract String[] allOf() default {};
+    method public abstract String[] anyOf() default {};
+    method public abstract boolean conditional() default false;
+    method public abstract String value() default "";
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER}) public static @interface RequiresPermission.Read {
+    method public abstract androidx.annotation.RequiresPermission value() default @androidx.annotation.RequiresPermission;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER}) public static @interface RequiresPermission.Write {
+    method public abstract androidx.annotation.RequiresPermission value() default @androidx.annotation.RequiresPermission;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface RestrictTo {
+    method public abstract androidx.annotation.RestrictTo.Scope[] value();
+  }
+
+  public enum RestrictTo.Scope {
+    enum_constant @Deprecated public static final androidx.annotation.RestrictTo.Scope GROUP_ID;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP_PREFIX;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope SUBCLASSES;
+    enum_constant public static final androidx.annotation.RestrictTo.Scope TESTS;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Size {
+    method public abstract long max() default java.lang.Long.MAX_VALUE;
+    method public abstract long min() default java.lang.Long.MIN_VALUE;
+    method public abstract long multiple() default 1;
+    method public abstract long value() default -1;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface StringDef {
+    method public abstract boolean open() default false;
+    method public abstract String[] value() default {};
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface StringRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface StyleRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface StyleableRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public @interface TransitionRes {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface UiThread {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface VisibleForTesting {
+    method @ProductionVisibility public abstract int otherwise() default androidx.annotation.VisibleForTesting.PRIVATE;
+    field public static final int NONE = 5; // 0x5
+    field public static final int PACKAGE_PRIVATE = 3; // 0x3
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface WorkerThread {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface XmlRes {
+  }
+
+}
+
diff --git a/appcompat/README.md b/appcompat/README.md
index 646e462..a0f70f9 100644
--- a/appcompat/README.md
+++ b/appcompat/README.md
@@ -4,7 +4,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/appcompat)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/appcompat/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/appcompat/)
 
 [Reference documentation](https://developer.android.com/reference/androidx/classes.html)
 
diff --git a/appcompat/appcompat-resources/api/1.3.0-beta01.txt b/appcompat/appcompat-resources/api/1.3.0-beta01.txt
new file mode 100644
index 0000000..b0256cf
--- /dev/null
+++ b/appcompat/appcompat-resources/api/1.3.0-beta01.txt
@@ -0,0 +1,35 @@
+// Signature format: 4.0
+package androidx.appcompat.content.res {
+
+  public final class AppCompatResources {
+    method public static android.content.res.ColorStateList! getColorStateList(android.content.Context, @ColorRes int);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+  }
+
+}
+
+package androidx.appcompat.graphics.drawable {
+
+  public class AnimatedStateListDrawableCompat extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public AnimatedStateListDrawableCompat();
+    method public void addState(int[], android.graphics.drawable.Drawable, int);
+    method public void addState(int[]!, android.graphics.drawable.Drawable!);
+    method public <T extends android.graphics.drawable.Drawable & android.graphics.drawable.Animatable> void addTransition(int, int, T, boolean);
+    method public static androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat? create(android.content.Context, @DrawableRes int, android.content.res.Resources.Theme?);
+    method public static androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat! createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.drawable.Drawable.ConstantState! getConstantState();
+    method public int getOpacity();
+    method public void inflate(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setDither(boolean);
+    method public void setEnterFadeDuration(int);
+    method public void setExitFadeDuration(int);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, Runnable);
+  }
+
+}
+
diff --git a/appcompat/appcompat-resources/api/public_plus_experimental_1.3.0-beta01.txt b/appcompat/appcompat-resources/api/public_plus_experimental_1.3.0-beta01.txt
new file mode 100644
index 0000000..fdef64c
--- /dev/null
+++ b/appcompat/appcompat-resources/api/public_plus_experimental_1.3.0-beta01.txt
@@ -0,0 +1,35 @@
+// Signature format: 4.0
+package androidx.appcompat.content.res {
+
+  public final class AppCompatResources {
+    method public static android.content.res.ColorStateList! getColorStateList(android.content.Context, @ColorRes int);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+  }
+
+}
+
+package androidx.appcompat.graphics.drawable {
+
+  public class AnimatedStateListDrawableCompat extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback androidx.core.graphics.drawable.TintAwareDrawable {
+    ctor public AnimatedStateListDrawableCompat();
+    method public void addState(int[], android.graphics.drawable.Drawable, int);
+    method public void addState(int[]!, android.graphics.drawable.Drawable!);
+    method public <T extends android.graphics.drawable.Drawable & android.graphics.drawable.Animatable> void addTransition(int, int, T, boolean);
+    method public static androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat? create(android.content.Context, @DrawableRes int, android.content.res.Resources.Theme?);
+    method public static androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat! createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.drawable.Drawable.ConstantState! getConstantState();
+    method public int getOpacity();
+    method public void inflate(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setDither(boolean);
+    method public void setEnterFadeDuration(int);
+    method public void setExitFadeDuration(int);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, Runnable);
+  }
+
+}
+
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutDirections.kt b/appcompat/appcompat-resources/api/res-1.3.0-beta01.txt
similarity index 100%
copy from compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutDirections.kt
copy to appcompat/appcompat-resources/api/res-1.3.0-beta01.txt
diff --git a/appcompat/appcompat-resources/api/restricted_1.3.0-beta01.txt b/appcompat/appcompat-resources/api/restricted_1.3.0-beta01.txt
new file mode 100644
index 0000000..c13b344
--- /dev/null
+++ b/appcompat/appcompat-resources/api/restricted_1.3.0-beta01.txt
@@ -0,0 +1,90 @@
+// Signature format: 4.0
+package androidx.appcompat.content.res {
+
+  public final class AppCompatResources {
+    method public static android.content.res.ColorStateList! getColorStateList(android.content.Context, @ColorRes int);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+  }
+
+}
+
+package androidx.appcompat.graphics.drawable {
+
+  public class AnimatedStateListDrawableCompat extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback androidx.core.graphics.drawable.TintAwareDrawable {
+    ctor public AnimatedStateListDrawableCompat();
+    method public void addState(int[], android.graphics.drawable.Drawable, int);
+    method public void addState(int[]!, android.graphics.drawable.Drawable!);
+    method public <T extends android.graphics.drawable.Drawable & android.graphics.drawable.Animatable> void addTransition(int, int, T, boolean);
+    method public static androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat? create(android.content.Context, @DrawableRes int, android.content.res.Resources.Theme?);
+    method public static androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat! createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.drawable.Drawable.ConstantState! getConstantState();
+    method public int getOpacity();
+    method public void inflate(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setDither(boolean);
+    method public void setEnterFadeDuration(int);
+    method public void setExitFadeDuration(int);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, Runnable);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DrawableWrapper extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public DrawableWrapper(android.graphics.drawable.Drawable!);
+    method public void draw(android.graphics.Canvas!);
+    method public int getOpacity();
+    method public android.graphics.drawable.Drawable! getWrappedDrawable();
+    method public void invalidateDrawable(android.graphics.drawable.Drawable!);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable!, Runnable!, long);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setDither(boolean);
+    method public void setWrappedDrawable(android.graphics.drawable.Drawable!);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable!, Runnable!);
+  }
+
+}
+
+package androidx.appcompat.widget {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DrawableUtils {
+    method public static boolean canSafelyMutateDrawable(android.graphics.drawable.Drawable);
+    method public static android.graphics.Rect! getOpticalBounds(android.graphics.drawable.Drawable!);
+    method public static android.graphics.PorterDuff.Mode! parseTintMode(int, android.graphics.PorterDuff.Mode!);
+    field public static final android.graphics.Rect! INSETS_NONE;
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ResourceManagerInternal {
+    ctor public ResourceManagerInternal();
+    method public static androidx.appcompat.widget.ResourceManagerInternal! get();
+    method public android.graphics.drawable.Drawable! getDrawable(android.content.Context, @DrawableRes int);
+    method public static android.graphics.PorterDuffColorFilter! getPorterDuffColorFilter(int, android.graphics.PorterDuff.Mode!);
+    method public void onConfigurationChanged(android.content.Context);
+    method public void setHooks(androidx.appcompat.widget.ResourceManagerInternal.ResourceManagerHooks!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TintContextWrapper extends android.content.ContextWrapper {
+    method public static android.content.Context! wrap(android.content.Context);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TintInfo {
+    ctor public TintInfo();
+    field public boolean mHasTintList;
+    field public boolean mHasTintMode;
+    field public android.content.res.ColorStateList! mTintList;
+    field public android.graphics.PorterDuff.Mode! mTintMode;
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class VectorEnabledTintResources extends android.content.res.Resources {
+    ctor public VectorEnabledTintResources(android.content.Context, android.content.res.Resources);
+    method public android.graphics.drawable.Drawable! getDrawable(int) throws android.content.res.Resources.NotFoundException;
+    method public static boolean isCompatVectorFromResourcesEnabled();
+    method public static void setCompatVectorFromResourcesEnabled(boolean);
+    method public static boolean shouldBeUsed();
+    field public static final int MAX_SDK_WHERE_REQUIRED = 20; // 0x14
+  }
+
+}
+
diff --git a/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompatTest.java b/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompatTest.java
index 10c86dc..86560c0 100644
--- a/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompatTest.java
+++ b/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompatTest.java
@@ -228,6 +228,7 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     public class MockDrawable extends Drawable {
         @Override
         public void draw(Canvas canvas) {
diff --git a/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/graphics/drawable/MyDrawable.java b/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/graphics/drawable/MyDrawable.java
index b0be5f9..28aa645 100644
--- a/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/graphics/drawable/MyDrawable.java
+++ b/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/graphics/drawable/MyDrawable.java
@@ -25,6 +25,7 @@
 /**
  * Simple custom drawable.
  */
+@SuppressWarnings("deprecation")
 public class MyDrawable extends Drawable {
     private final Paint mPaint;
 
diff --git a/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/widget/TintResourcesTest.java b/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/widget/TintResourcesTest.java
index 5c3e673..313399e 100644
--- a/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/widget/TintResourcesTest.java
+++ b/appcompat/appcompat-resources/src/androidTest/java/androidx/appcompat/widget/TintResourcesTest.java
@@ -25,18 +25,18 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+@SuppressWarnings("deprecation")
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class TintResourcesTest {
     @Rule
-    public final ActivityTestRule<Activity> mActivityTestRule =
-            new ActivityTestRule<>(Activity.class);
+    public final androidx.test.rule.ActivityTestRule<Activity> mActivityTestRule =
+            new androidx.test.rule.ActivityTestRule<>(Activity.class);
 
     @Test
     public void testTintResourcesDelegateBackToOriginalResources() {
diff --git a/appcompat/appcompat/api/1.3.0-beta01.txt b/appcompat/appcompat/api/1.3.0-beta01.txt
new file mode 100644
index 0000000..6b91606
--- /dev/null
+++ b/appcompat/appcompat/api/1.3.0-beta01.txt
@@ -0,0 +1,981 @@
+// Signature format: 4.0
+package androidx.appcompat.app {
+
+  public abstract class ActionBar {
+    ctor public ActionBar();
+    method public abstract void addOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, boolean);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, int);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, int, boolean);
+    method public abstract android.view.View! getCustomView();
+    method public abstract int getDisplayOptions();
+    method public float getElevation();
+    method public abstract int getHeight();
+    method public int getHideOffset();
+    method @Deprecated public abstract int getNavigationItemCount();
+    method @Deprecated public abstract int getNavigationMode();
+    method @Deprecated public abstract int getSelectedNavigationIndex();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab? getSelectedTab();
+    method public abstract CharSequence? getSubtitle();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! getTabAt(int);
+    method @Deprecated public abstract int getTabCount();
+    method public android.content.Context! getThemedContext();
+    method public abstract CharSequence? getTitle();
+    method public abstract void hide();
+    method public boolean isHideOnContentScrollEnabled();
+    method public abstract boolean isShowing();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! newTab();
+    method @Deprecated public abstract void removeAllTabs();
+    method public abstract void removeOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+    method @Deprecated public abstract void removeTab(androidx.appcompat.app.ActionBar.Tab!);
+    method @Deprecated public abstract void removeTabAt(int);
+    method @Deprecated public abstract void selectTab(androidx.appcompat.app.ActionBar.Tab!);
+    method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public abstract void setCustomView(android.view.View!);
+    method public abstract void setCustomView(android.view.View!, androidx.appcompat.app.ActionBar.LayoutParams!);
+    method public abstract void setCustomView(int);
+    method public abstract void setDisplayHomeAsUpEnabled(boolean);
+    method public abstract void setDisplayOptions(int);
+    method public abstract void setDisplayOptions(int, int);
+    method public abstract void setDisplayShowCustomEnabled(boolean);
+    method public abstract void setDisplayShowHomeEnabled(boolean);
+    method public abstract void setDisplayShowTitleEnabled(boolean);
+    method public abstract void setDisplayUseLogoEnabled(boolean);
+    method public void setElevation(float);
+    method public void setHideOffset(int);
+    method public void setHideOnContentScrollEnabled(boolean);
+    method public void setHomeActionContentDescription(CharSequence?);
+    method public void setHomeActionContentDescription(@StringRes int);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable?);
+    method public void setHomeAsUpIndicator(@DrawableRes int);
+    method public void setHomeButtonEnabled(boolean);
+    method public abstract void setIcon(@DrawableRes int);
+    method public abstract void setIcon(android.graphics.drawable.Drawable!);
+    method @Deprecated public abstract void setListNavigationCallbacks(android.widget.SpinnerAdapter!, androidx.appcompat.app.ActionBar.OnNavigationListener!);
+    method public abstract void setLogo(@DrawableRes int);
+    method public abstract void setLogo(android.graphics.drawable.Drawable!);
+    method @Deprecated public abstract void setNavigationMode(int);
+    method @Deprecated public abstract void setSelectedNavigationItem(int);
+    method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable!);
+    method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable!);
+    method public abstract void setSubtitle(CharSequence!);
+    method public abstract void setSubtitle(int);
+    method public abstract void setTitle(CharSequence!);
+    method public abstract void setTitle(@StringRes int);
+    method public abstract void show();
+    field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
+    field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
+    field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
+    field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
+    field public static final int DISPLAY_USE_LOGO = 1; // 0x1
+    field @Deprecated public static final int NAVIGATION_MODE_LIST = 1; // 0x1
+    field @Deprecated public static final int NAVIGATION_MODE_STANDARD = 0; // 0x0
+    field @Deprecated public static final int NAVIGATION_MODE_TABS = 2; // 0x2
+  }
+
+  public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet!);
+    ctor public ActionBar.LayoutParams(int, int);
+    ctor public ActionBar.LayoutParams(int, int, int);
+    ctor public ActionBar.LayoutParams(int);
+    ctor public ActionBar.LayoutParams(androidx.appcompat.app.ActionBar.LayoutParams!);
+    ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    field public int gravity;
+  }
+
+  public static interface ActionBar.OnMenuVisibilityListener {
+    method public void onMenuVisibilityChanged(boolean);
+  }
+
+  @Deprecated public static interface ActionBar.OnNavigationListener {
+    method @Deprecated public boolean onNavigationItemSelected(int, long);
+  }
+
+  @Deprecated public abstract static class ActionBar.Tab {
+    ctor @Deprecated public ActionBar.Tab();
+    method @Deprecated public abstract CharSequence! getContentDescription();
+    method @Deprecated public abstract android.view.View! getCustomView();
+    method @Deprecated public abstract android.graphics.drawable.Drawable! getIcon();
+    method @Deprecated public abstract int getPosition();
+    method @Deprecated public abstract Object! getTag();
+    method @Deprecated public abstract CharSequence! getText();
+    method @Deprecated public abstract void select();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setContentDescription(@StringRes int);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setContentDescription(CharSequence!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setCustomView(android.view.View!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setCustomView(int);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setIcon(android.graphics.drawable.Drawable!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setIcon(@DrawableRes int);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setTabListener(androidx.appcompat.app.ActionBar.TabListener!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setTag(Object!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setText(CharSequence!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setText(int);
+    field @Deprecated public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  @Deprecated public static interface ActionBar.TabListener {
+    method @Deprecated public void onTabReselected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+    method @Deprecated public void onTabSelected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+    method @Deprecated public void onTabUnselected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+  }
+
+  public class ActionBarDrawerToggle implements androidx.drawerlayout.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity!, androidx.drawerlayout.widget.DrawerLayout!, @StringRes int, @StringRes int);
+    ctor public ActionBarDrawerToggle(android.app.Activity!, androidx.drawerlayout.widget.DrawerLayout!, androidx.appcompat.widget.Toolbar!, @StringRes int, @StringRes int);
+    method public androidx.appcompat.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
+    method public android.view.View.OnClickListener! getToolbarNavigationClickListener();
+    method public boolean isDrawerIndicatorEnabled();
+    method public boolean isDrawerSlideAnimationEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration!);
+    method public void onDrawerClosed(android.view.View!);
+    method public void onDrawerOpened(android.view.View!);
+    method public void onDrawerSlide(android.view.View!, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem!);
+    method public void setDrawerArrowDrawable(androidx.appcompat.graphics.drawable.DrawerArrowDrawable);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setDrawerSlideAnimationEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable!);
+    method public void setHomeAsUpIndicator(int);
+    method public void setToolbarNavigationClickListener(android.view.View.OnClickListener!);
+    method public void syncState();
+  }
+
+  public static interface ActionBarDrawerToggle.Delegate {
+    method public android.content.Context! getActionBarThemedContext();
+    method public android.graphics.drawable.Drawable! getThemeUpIndicator();
+    method public boolean isNavigationVisible();
+    method public void setActionBarDescription(@StringRes int);
+    method public void setActionBarUpIndicator(android.graphics.drawable.Drawable!, @StringRes int);
+  }
+
+  public static interface ActionBarDrawerToggle.DelegateProvider {
+    method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+  }
+
+  public class AlertDialog extends androidx.appcompat.app.AppCompatDialog implements android.content.DialogInterface {
+    ctor protected AlertDialog(android.content.Context);
+    ctor protected AlertDialog(android.content.Context, @StyleRes int);
+    ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener?);
+    method public android.widget.Button! getButton(int);
+    method public android.widget.ListView! getListView();
+    method public void setButton(int, CharSequence!, android.os.Message!);
+    method public void setButton(int, CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public void setButton(int, CharSequence!, android.graphics.drawable.Drawable!, android.content.DialogInterface.OnClickListener!);
+    method public void setCustomTitle(android.view.View!);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setIconAttribute(int);
+    method public void setMessage(CharSequence!);
+    method public void setView(android.view.View!);
+    method public void setView(android.view.View!, int, int, int, int);
+  }
+
+  public static class AlertDialog.Builder {
+    ctor public AlertDialog.Builder(android.content.Context);
+    ctor public AlertDialog.Builder(android.content.Context, @StyleRes int);
+    method public androidx.appcompat.app.AlertDialog create();
+    method public android.content.Context getContext();
+    method public androidx.appcompat.app.AlertDialog.Builder! setAdapter(android.widget.ListAdapter!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setCancelable(boolean);
+    method public androidx.appcompat.app.AlertDialog.Builder! setCursor(android.database.Cursor!, android.content.DialogInterface.OnClickListener!, String!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setCustomTitle(android.view.View?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setIcon(@DrawableRes int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setIcon(android.graphics.drawable.Drawable?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setIconAttribute(@AttrRes int);
+    method @Deprecated public androidx.appcompat.app.AlertDialog.Builder! setInverseBackgroundForced(boolean);
+    method public androidx.appcompat.app.AlertDialog.Builder! setItems(@ArrayRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setItems(CharSequence![]!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMessage(@StringRes int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMessage(CharSequence?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(@ArrayRes int, boolean[]!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(CharSequence![]!, boolean[]!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(android.database.Cursor!, String!, String!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButtonIcon(android.graphics.drawable.Drawable!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButtonIcon(android.graphics.drawable.Drawable!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnCancelListener(android.content.DialogInterface.OnCancelListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnDismissListener(android.content.DialogInterface.OnDismissListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnKeyListener(android.content.DialogInterface.OnKeyListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButtonIcon(android.graphics.drawable.Drawable!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(@ArrayRes int, int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(android.database.Cursor!, int, String!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(CharSequence![]!, int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(android.widget.ListAdapter!, int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setTitle(@StringRes int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setTitle(CharSequence?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setView(int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setView(android.view.View!);
+    method public androidx.appcompat.app.AlertDialog! show();
+  }
+
+  public class AppCompatActivity extends androidx.fragment.app.FragmentActivity implements androidx.appcompat.app.ActionBarDrawerToggle.DelegateProvider androidx.appcompat.app.AppCompatCallback androidx.lifecycle.LifecycleOwner androidx.core.app.TaskStackBuilder.SupportParentable {
+    ctor public AppCompatActivity();
+    ctor @ContentView public AppCompatActivity(@LayoutRes int);
+    method public androidx.appcompat.app.AppCompatDelegate getDelegate();
+    method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+    method public androidx.appcompat.app.ActionBar? getSupportActionBar();
+    method public android.content.Intent? getSupportParentActivityIntent();
+    method public void onCreateSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder);
+    method public final boolean onMenuItemSelected(int, android.view.MenuItem);
+    method protected void onNightModeChanged(int);
+    method public void onPrepareSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder);
+    method @CallSuper public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode);
+    method @CallSuper public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode);
+    method @Deprecated public void onSupportContentChanged();
+    method public boolean onSupportNavigateUp();
+    method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+    method public void setSupportActionBar(androidx.appcompat.widget.Toolbar?);
+    method @Deprecated public void setSupportProgress(int);
+    method @Deprecated public void setSupportProgressBarIndeterminate(boolean);
+    method @Deprecated public void setSupportProgressBarIndeterminateVisibility(boolean);
+    method @Deprecated public void setSupportProgressBarVisibility(boolean);
+    method public androidx.appcompat.view.ActionMode? startSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+    method public void supportInvalidateOptionsMenu();
+    method public void supportNavigateUpTo(android.content.Intent);
+    method public boolean supportRequestWindowFeature(int);
+    method public boolean supportShouldUpRecreateTask(android.content.Intent);
+  }
+
+  public interface AppCompatCallback {
+    method public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode!);
+    method public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode!);
+    method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback!);
+  }
+
+  public abstract class AppCompatDelegate {
+    method public abstract void addContentView(android.view.View!, android.view.ViewGroup.LayoutParams!);
+    method public abstract boolean applyDayNight();
+    method @Deprecated public void attachBaseContext(android.content.Context!);
+    method @CallSuper public android.content.Context attachBaseContext2(android.content.Context);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.app.Activity, androidx.appcompat.app.AppCompatCallback?);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.app.Dialog, androidx.appcompat.app.AppCompatCallback?);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.content.Context, android.view.Window, androidx.appcompat.app.AppCompatCallback?);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.content.Context, android.app.Activity, androidx.appcompat.app.AppCompatCallback?);
+    method public abstract android.view.View! createView(android.view.View?, String!, android.content.Context, android.util.AttributeSet);
+    method public abstract <T extends android.view.View> T! findViewById(@IdRes int);
+    method public static int getDefaultNightMode();
+    method public abstract androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+    method public int getLocalNightMode();
+    method public abstract android.view.MenuInflater! getMenuInflater();
+    method public abstract androidx.appcompat.app.ActionBar? getSupportActionBar();
+    method public abstract boolean hasWindowFeature(int);
+    method public abstract void installViewFactory();
+    method public abstract void invalidateOptionsMenu();
+    method public static boolean isCompatVectorFromResourcesEnabled();
+    method public abstract boolean isHandleNativeActionModesEnabled();
+    method public abstract void onConfigurationChanged(android.content.res.Configuration!);
+    method public abstract void onCreate(android.os.Bundle!);
+    method public abstract void onDestroy();
+    method public abstract void onPostCreate(android.os.Bundle!);
+    method public abstract void onPostResume();
+    method public abstract void onSaveInstanceState(android.os.Bundle!);
+    method public abstract void onStart();
+    method public abstract void onStop();
+    method public abstract boolean requestWindowFeature(int);
+    method public static void setCompatVectorFromResourcesEnabled(boolean);
+    method public abstract void setContentView(android.view.View!);
+    method public abstract void setContentView(@LayoutRes int);
+    method public abstract void setContentView(android.view.View!, android.view.ViewGroup.LayoutParams!);
+    method public static void setDefaultNightMode(int);
+    method public abstract void setHandleNativeActionModesEnabled(boolean);
+    method @RequiresApi(17) public abstract void setLocalNightMode(int);
+    method public abstract void setSupportActionBar(androidx.appcompat.widget.Toolbar?);
+    method public void setTheme(@StyleRes int);
+    method public abstract void setTitle(CharSequence?);
+    method public abstract androidx.appcompat.view.ActionMode? startSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+    field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
+    field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+    field @Deprecated public static final int MODE_NIGHT_AUTO = 0; // 0x0
+    field public static final int MODE_NIGHT_AUTO_BATTERY = 3; // 0x3
+    field @Deprecated public static final int MODE_NIGHT_AUTO_TIME = 0; // 0x0
+    field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
+    field public static final int MODE_NIGHT_NO = 1; // 0x1
+    field public static final int MODE_NIGHT_UNSPECIFIED = -100; // 0xffffff9c
+    field public static final int MODE_NIGHT_YES = 2; // 0x2
+  }
+
+  public class AppCompatDialog extends android.app.Dialog implements androidx.appcompat.app.AppCompatCallback {
+    ctor public AppCompatDialog(android.content.Context!);
+    ctor public AppCompatDialog(android.content.Context!, int);
+    ctor protected AppCompatDialog(android.content.Context!, boolean, android.content.DialogInterface.OnCancelListener!);
+    method public androidx.appcompat.app.AppCompatDelegate! getDelegate();
+    method public androidx.appcompat.app.ActionBar! getSupportActionBar();
+    method public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode!);
+    method public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode!);
+    method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback!);
+    method public boolean supportRequestWindowFeature(int);
+  }
+
+  public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
+    ctor public AppCompatDialogFragment();
+  }
+
+  public class AppCompatViewInflater {
+    ctor public AppCompatViewInflater();
+    method protected androidx.appcompat.widget.AppCompatAutoCompleteTextView createAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatButton createButton(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatCheckBox createCheckBox(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatCheckedTextView createCheckedTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatEditText createEditText(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatImageButton createImageButton(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatImageView createImageView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView createMultiAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatRadioButton createRadioButton(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatRatingBar createRatingBar(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatSeekBar createSeekBar(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatSpinner createSpinner(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatTextView createTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatToggleButton createToggleButton(android.content.Context!, android.util.AttributeSet!);
+    method protected android.view.View? createView(android.content.Context!, String!, android.util.AttributeSet!);
+  }
+
+}
+
+package androidx.appcompat.graphics.drawable {
+
+  public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
+    ctor public DrawerArrowDrawable(android.content.Context!);
+    method public void draw(android.graphics.Canvas!);
+    method public float getArrowHeadLength();
+    method public float getArrowShaftLength();
+    method public float getBarLength();
+    method public float getBarThickness();
+    method @ColorInt public int getColor();
+    method public int getDirection();
+    method public float getGapSize();
+    method public int getOpacity();
+    method public final android.graphics.Paint! getPaint();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
+    method public boolean isSpinEnabled();
+    method public void setAlpha(int);
+    method public void setArrowHeadLength(float);
+    method public void setArrowShaftLength(float);
+    method public void setBarLength(float);
+    method public void setBarThickness(float);
+    method public void setColor(@ColorInt int);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setDirection(int);
+    method public void setGapSize(float);
+    method public void setProgress(@FloatRange(from=0.0, to=1.0) float);
+    method public void setSpinEnabled(boolean);
+    method public void setVerticalMirror(boolean);
+    field public static final int ARROW_DIRECTION_END = 3; // 0x3
+    field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
+    field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
+    field public static final int ARROW_DIRECTION_START = 2; // 0x2
+  }
+
+}
+
+package androidx.appcompat.view {
+
+  public abstract class ActionMode {
+    ctor public ActionMode();
+    method public abstract void finish();
+    method public abstract android.view.View! getCustomView();
+    method public abstract android.view.Menu! getMenu();
+    method public abstract android.view.MenuInflater! getMenuInflater();
+    method public abstract CharSequence! getSubtitle();
+    method public Object! getTag();
+    method public abstract CharSequence! getTitle();
+    method public boolean getTitleOptionalHint();
+    method public abstract void invalidate();
+    method public boolean isTitleOptional();
+    method public abstract void setCustomView(android.view.View!);
+    method public abstract void setSubtitle(CharSequence!);
+    method public abstract void setSubtitle(int);
+    method public void setTag(Object!);
+    method public abstract void setTitle(CharSequence!);
+    method public abstract void setTitle(int);
+    method public void setTitleOptionalHint(boolean);
+  }
+
+  public static interface ActionMode.Callback {
+    method public boolean onActionItemClicked(androidx.appcompat.view.ActionMode!, android.view.MenuItem!);
+    method public boolean onCreateActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+    method public void onDestroyActionMode(androidx.appcompat.view.ActionMode!);
+    method public boolean onPrepareActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+  }
+
+  @Deprecated public interface CollapsibleActionView {
+    method @Deprecated public void onActionViewCollapsed();
+    method @Deprecated public void onActionViewExpanded();
+  }
+
+  public class ContextThemeWrapper extends android.content.ContextWrapper {
+    ctor public ContextThemeWrapper();
+    ctor public ContextThemeWrapper(android.content.Context!, @StyleRes int);
+    ctor public ContextThemeWrapper(android.content.Context!, android.content.res.Resources.Theme!);
+    method public void applyOverrideConfiguration(android.content.res.Configuration!);
+    method public int getThemeResId();
+    method protected void onApplyThemeResource(android.content.res.Resources.Theme!, int, boolean);
+  }
+
+}
+
+package androidx.appcompat.widget {
+
+  public class ActionMenuView extends androidx.appcompat.widget.LinearLayoutCompat {
+    ctor public ActionMenuView(android.content.Context);
+    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet?);
+    method public void dismissPopupMenus();
+    method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public android.view.Menu! getMenu();
+    method public android.graphics.drawable.Drawable? getOverflowIcon();
+    method public int getPopupTheme();
+    method public boolean hideOverflowMenu();
+    method public boolean isOverflowMenuShowing();
+    method public void onConfigurationChanged(android.content.res.Configuration!);
+    method public void onDetachedFromWindow();
+    method public void setOnMenuItemClickListener(androidx.appcompat.widget.ActionMenuView.OnMenuItemClickListener!);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable?);
+    method public void setPopupTheme(@StyleRes int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class ActionMenuView.LayoutParams extends androidx.appcompat.widget.LinearLayoutCompat.LayoutParams {
+    ctor public ActionMenuView.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+    ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    ctor public ActionMenuView.LayoutParams(androidx.appcompat.widget.ActionMenuView.LayoutParams!);
+    ctor public ActionMenuView.LayoutParams(int, int);
+    field public int cellsUsed;
+    field public boolean expandable;
+    field public int extraPixels;
+    field public boolean isOverflowButton;
+    field public boolean preventEdgeOffset;
+  }
+
+  public static interface ActionMenuView.OnMenuItemClickListener {
+    method public boolean onMenuItemClick(android.view.MenuItem!);
+  }
+
+  public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatButton extends android.widget.Button implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+    ctor public AppCompatButton(android.content.Context);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public void setSupportAllCaps(boolean);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatCheckBox extends android.widget.CheckBox implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton {
+    ctor public AppCompatCheckBox(android.content.Context);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
+    ctor public AppCompatCheckedTextView(android.content.Context);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?, int);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.OnReceiveContentViewBehavior androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatEditText(android.content.Context);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatImageButton extends android.widget.ImageButton implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatImageButton(android.content.Context);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatImageView extends android.widget.ImageView implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatImageView(android.content.Context);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatRadioButton extends android.widget.RadioButton implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton {
+    ctor public AppCompatRadioButton(android.content.Context!);
+    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?);
+    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatRatingBar extends android.widget.RatingBar {
+    ctor public AppCompatRatingBar(android.content.Context);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class AppCompatSeekBar extends android.widget.SeekBar {
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class AppCompatSpinner extends android.widget.Spinner implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatSpinner(android.content.Context);
+    ctor public AppCompatSpinner(android.content.Context, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int, android.content.res.Resources.Theme!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatTextView extends android.widget.TextView implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+    ctor public AppCompatTextView(android.content.Context);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method public androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParamsCompat();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public void setPrecomputedText(androidx.core.text.PrecomputedTextCompat);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+    method public void setTextFuture(java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>?);
+    method public void setTextMetricsParamsCompat(androidx.core.text.PrecomputedTextCompat.Params);
+  }
+
+  public class AppCompatToggleButton extends android.widget.ToggleButton implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatToggleButton(android.content.Context);
+    ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class LinearLayoutCompat extends android.view.ViewGroup {
+    ctor public LinearLayoutCompat(android.content.Context);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?, int);
+    method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public int getBaselineAlignedChildIndex();
+    method public android.graphics.drawable.Drawable! getDividerDrawable();
+    method public int getDividerPadding();
+    method public int getGravity();
+    method public int getOrientation();
+    method public int getShowDividers();
+    method public float getWeightSum();
+    method public boolean isBaselineAligned();
+    method public boolean isMeasureWithLargestChildEnabled();
+    method public void setBaselineAligned(boolean);
+    method public void setBaselineAlignedChildIndex(int);
+    method public void setDividerDrawable(android.graphics.drawable.Drawable!);
+    method public void setDividerPadding(int);
+    method public void setGravity(int);
+    method public void setHorizontalGravity(int);
+    method public void setMeasureWithLargestChildEnabled(boolean);
+    method public void setOrientation(int);
+    method public void setShowDividers(int);
+    method public void setVerticalGravity(int);
+    method public void setWeightSum(float);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
+    field public static final int SHOW_DIVIDER_END = 4; // 0x4
+    field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
+    field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class LinearLayoutCompat.LayoutParams extends android.widget.LinearLayout.LayoutParams {
+    ctor public LinearLayoutCompat.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+    ctor public LinearLayoutCompat.LayoutParams(int, int);
+    ctor public LinearLayoutCompat.LayoutParams(int, int, float);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+  }
+
+  public class ListPopupWindow {
+    ctor public ListPopupWindow(android.content.Context);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?, @AttrRes int);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?, @AttrRes int, @StyleRes int);
+    method public void clearListSelection();
+    method public android.view.View.OnTouchListener! createDragToOpenListener(android.view.View!);
+    method public void dismiss();
+    method public android.view.View? getAnchorView();
+    method @StyleRes public int getAnimationStyle();
+    method public android.graphics.drawable.Drawable? getBackground();
+    method public android.graphics.Rect? getEpicenterBounds();
+    method public int getHeight();
+    method public int getHorizontalOffset();
+    method public int getInputMethodMode();
+    method public android.widget.ListView? getListView();
+    method public int getPromptPosition();
+    method public Object? getSelectedItem();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public android.view.View? getSelectedView();
+    method public int getSoftInputMode();
+    method public int getVerticalOffset();
+    method public int getWidth();
+    method public boolean isInputMethodNotNeeded();
+    method public boolean isModal();
+    method public boolean isShowing();
+    method public boolean onKeyDown(int, android.view.KeyEvent);
+    method public boolean onKeyPreIme(int, android.view.KeyEvent);
+    method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public boolean performItemClick(int);
+    method public void postShow();
+    method public void setAdapter(android.widget.ListAdapter?);
+    method public void setAnchorView(android.view.View?);
+    method public void setAnimationStyle(@StyleRes int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public void setContentWidth(int);
+    method public void setDropDownGravity(int);
+    method public void setEpicenterBounds(android.graphics.Rect?);
+    method public void setHeight(int);
+    method public void setHorizontalOffset(int);
+    method public void setInputMethodMode(int);
+    method public void setListSelector(android.graphics.drawable.Drawable!);
+    method public void setModal(boolean);
+    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener?);
+    method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener?);
+    method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener?);
+    method public void setPromptPosition(int);
+    method public void setPromptView(android.view.View?);
+    method public void setSelection(int);
+    method public void setSoftInputMode(int);
+    method public void setVerticalOffset(int);
+    method public void setWidth(int);
+    method public void setWindowLayoutType(int);
+    method public void show();
+    field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
+    field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
+    field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
+    field public static final int MATCH_PARENT = -1; // 0xffffffff
+    field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
+    field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
+    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
+  }
+
+  public class PopupMenu {
+    ctor public PopupMenu(android.content.Context, android.view.View);
+    ctor public PopupMenu(android.content.Context, android.view.View, int);
+    ctor public PopupMenu(android.content.Context, android.view.View, int, @AttrRes int, @StyleRes int);
+    method public void dismiss();
+    method public android.view.View.OnTouchListener getDragToOpenListener();
+    method public int getGravity();
+    method public android.view.Menu getMenu();
+    method public android.view.MenuInflater getMenuInflater();
+    method public void inflate(@MenuRes int);
+    method public void setGravity(int);
+    method public void setOnDismissListener(androidx.appcompat.widget.PopupMenu.OnDismissListener?);
+    method public void setOnMenuItemClickListener(androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener?);
+    method public void show();
+  }
+
+  public static interface PopupMenu.OnDismissListener {
+    method public void onDismiss(androidx.appcompat.widget.PopupMenu!);
+  }
+
+  public static interface PopupMenu.OnMenuItemClickListener {
+    method public boolean onMenuItemClick(android.view.MenuItem!);
+  }
+
+  public class SearchView extends androidx.appcompat.widget.LinearLayoutCompat implements androidx.appcompat.view.CollapsibleActionView {
+    ctor public SearchView(android.content.Context);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet?);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet?, int);
+    method public int getImeOptions();
+    method public int getInputType();
+    method public int getMaxWidth();
+    method public CharSequence! getQuery();
+    method public CharSequence? getQueryHint();
+    method public androidx.cursoradapter.widget.CursorAdapter! getSuggestionsAdapter();
+    method public boolean isIconfiedByDefault();
+    method public boolean isIconified();
+    method public boolean isQueryRefinementEnabled();
+    method public boolean isSubmitButtonEnabled();
+    method public void onActionViewCollapsed();
+    method public void onActionViewExpanded();
+    method public void setIconified(boolean);
+    method public void setIconifiedByDefault(boolean);
+    method public void setImeOptions(int);
+    method public void setInputType(int);
+    method public void setMaxWidth(int);
+    method public void setOnCloseListener(androidx.appcompat.widget.SearchView.OnCloseListener!);
+    method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener!);
+    method public void setOnQueryTextListener(androidx.appcompat.widget.SearchView.OnQueryTextListener!);
+    method public void setOnSearchClickListener(android.view.View.OnClickListener!);
+    method public void setOnSuggestionListener(androidx.appcompat.widget.SearchView.OnSuggestionListener!);
+    method public void setQuery(CharSequence!, boolean);
+    method public void setQueryHint(CharSequence?);
+    method public void setQueryRefinementEnabled(boolean);
+    method public void setSearchableInfo(android.app.SearchableInfo!);
+    method public void setSubmitButtonEnabled(boolean);
+    method public void setSuggestionsAdapter(androidx.cursoradapter.widget.CursorAdapter!);
+  }
+
+  public static interface SearchView.OnCloseListener {
+    method public boolean onClose();
+  }
+
+  public static interface SearchView.OnQueryTextListener {
+    method public boolean onQueryTextChange(String!);
+    method public boolean onQueryTextSubmit(String!);
+  }
+
+  public static interface SearchView.OnSuggestionListener {
+    method public boolean onSuggestionClick(int);
+    method public boolean onSuggestionSelect(int);
+  }
+
+  public class ShareActionProvider extends androidx.core.view.ActionProvider {
+    ctor public ShareActionProvider(android.content.Context!);
+    method public android.view.View! onCreateActionView();
+    method public void setOnShareTargetSelectedListener(androidx.appcompat.widget.ShareActionProvider.OnShareTargetSelectedListener!);
+    method public void setShareHistoryFileName(String!);
+    method public void setShareIntent(android.content.Intent!);
+    field public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+  }
+
+  public static interface ShareActionProvider.OnShareTargetSelectedListener {
+    method public boolean onShareTargetSelected(androidx.appcompat.widget.ShareActionProvider!, android.content.Intent!);
+  }
+
+  public class SwitchCompat extends android.widget.CompoundButton {
+    ctor public SwitchCompat(android.content.Context);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?, int);
+    method public boolean getShowText();
+    method public boolean getSplitTrack();
+    method public int getSwitchMinWidth();
+    method public int getSwitchPadding();
+    method public CharSequence! getTextOff();
+    method public CharSequence! getTextOn();
+    method public android.graphics.drawable.Drawable! getThumbDrawable();
+    method public int getThumbTextPadding();
+    method public android.content.res.ColorStateList? getThumbTintList();
+    method public android.graphics.PorterDuff.Mode? getThumbTintMode();
+    method public android.graphics.drawable.Drawable! getTrackDrawable();
+    method public android.content.res.ColorStateList? getTrackTintList();
+    method public android.graphics.PorterDuff.Mode? getTrackTintMode();
+    method public void onMeasure(int, int);
+    method public void setShowText(boolean);
+    method public void setSplitTrack(boolean);
+    method public void setSwitchMinWidth(int);
+    method public void setSwitchPadding(int);
+    method public void setSwitchTextAppearance(android.content.Context!, int);
+    method public void setSwitchTypeface(android.graphics.Typeface!, int);
+    method public void setSwitchTypeface(android.graphics.Typeface!);
+    method public void setTextOff(CharSequence!);
+    method public void setTextOn(CharSequence!);
+    method public void setThumbDrawable(android.graphics.drawable.Drawable!);
+    method public void setThumbResource(int);
+    method public void setThumbTextPadding(int);
+    method public void setThumbTintList(android.content.res.ColorStateList?);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTrackDrawable(android.graphics.drawable.Drawable!);
+    method public void setTrackResource(int);
+    method public void setTrackTintList(android.content.res.ColorStateList?);
+    method public void setTrackTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
+    method public android.content.res.Resources.Theme? getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme?);
+  }
+
+  public static final class ThemedSpinnerAdapter.Helper {
+    ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
+    method public android.view.LayoutInflater getDropDownViewInflater();
+    method public android.content.res.Resources.Theme? getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme?);
+  }
+
+  public class Toolbar extends android.view.ViewGroup {
+    ctor public Toolbar(android.content.Context);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet?);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet?, int);
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
+    method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.appcompat.widget.Toolbar.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public CharSequence? getCollapseContentDescription();
+    method public android.graphics.drawable.Drawable? getCollapseIcon();
+    method public int getContentInsetEnd();
+    method public int getContentInsetEndWithActions();
+    method public int getContentInsetLeft();
+    method public int getContentInsetRight();
+    method public int getContentInsetStart();
+    method public int getContentInsetStartWithNavigation();
+    method public int getCurrentContentInsetEnd();
+    method public int getCurrentContentInsetLeft();
+    method public int getCurrentContentInsetRight();
+    method public int getCurrentContentInsetStart();
+    method public android.graphics.drawable.Drawable! getLogo();
+    method public CharSequence! getLogoDescription();
+    method public android.view.Menu! getMenu();
+    method public CharSequence? getNavigationContentDescription();
+    method public android.graphics.drawable.Drawable? getNavigationIcon();
+    method public android.graphics.drawable.Drawable? getOverflowIcon();
+    method public int getPopupTheme();
+    method public CharSequence! getSubtitle();
+    method public CharSequence! getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
+    method public boolean hasExpandedActionView();
+    method public boolean hideOverflowMenu();
+    method public void inflateMenu(@MenuRes int);
+    method public boolean isOverflowMenuShowing();
+    method public void setCollapseContentDescription(@StringRes int);
+    method public void setCollapseContentDescription(CharSequence?);
+    method public void setCollapseIcon(@DrawableRes int);
+    method public void setCollapseIcon(android.graphics.drawable.Drawable?);
+    method public void setContentInsetEndWithActions(int);
+    method public void setContentInsetStartWithNavigation(int);
+    method public void setContentInsetsAbsolute(int, int);
+    method public void setContentInsetsRelative(int, int);
+    method public void setLogo(@DrawableRes int);
+    method public void setLogo(android.graphics.drawable.Drawable!);
+    method public void setLogoDescription(@StringRes int);
+    method public void setLogoDescription(CharSequence!);
+    method public void setNavigationContentDescription(@StringRes int);
+    method public void setNavigationContentDescription(CharSequence?);
+    method public void setNavigationIcon(@DrawableRes int);
+    method public void setNavigationIcon(android.graphics.drawable.Drawable?);
+    method public void setNavigationOnClickListener(android.view.View.OnClickListener!);
+    method public void setOnMenuItemClickListener(androidx.appcompat.widget.Toolbar.OnMenuItemClickListener!);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable?);
+    method public void setPopupTheme(@StyleRes int);
+    method public void setSubtitle(@StringRes int);
+    method public void setSubtitle(CharSequence!);
+    method public void setSubtitleTextAppearance(android.content.Context!, @StyleRes int);
+    method public void setSubtitleTextColor(@ColorInt int);
+    method public void setSubtitleTextColor(android.content.res.ColorStateList);
+    method public void setTitle(@StringRes int);
+    method public void setTitle(CharSequence!);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
+    method public void setTitleTextAppearance(android.content.Context!, @StyleRes int);
+    method public void setTitleTextColor(@ColorInt int);
+    method public void setTitleTextColor(android.content.res.ColorStateList);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class Toolbar.LayoutParams extends androidx.appcompat.app.ActionBar.LayoutParams {
+    ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet!);
+    ctor public Toolbar.LayoutParams(int, int);
+    ctor public Toolbar.LayoutParams(int, int, int);
+    ctor public Toolbar.LayoutParams(int);
+    ctor public Toolbar.LayoutParams(androidx.appcompat.widget.Toolbar.LayoutParams!);
+    ctor public Toolbar.LayoutParams(androidx.appcompat.app.ActionBar.LayoutParams!);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams!);
+  }
+
+  public static interface Toolbar.OnMenuItemClickListener {
+    method public boolean onMenuItemClick(android.view.MenuItem!);
+  }
+
+  public static class Toolbar.SavedState extends androidx.customview.view.AbsSavedState {
+    ctor public Toolbar.SavedState(android.os.Parcel!);
+    ctor public Toolbar.SavedState(android.os.Parcel!, ClassLoader!);
+    ctor public Toolbar.SavedState(android.os.Parcelable!);
+    field public static final android.os.Parcelable.Creator<androidx.appcompat.widget.Toolbar.SavedState!>! CREATOR;
+  }
+
+  public class TooltipCompat {
+    method public static void setTooltipText(android.view.View, CharSequence?);
+  }
+
+}
+
diff --git a/appcompat/appcompat/api/current.txt b/appcompat/appcompat/api/current.txt
index 5bbbed7..6b91606 100644
--- a/appcompat/appcompat/api/current.txt
+++ b/appcompat/appcompat/api/current.txt
@@ -513,15 +513,14 @@
     method public void setTextAppearance(android.content.Context!, int);
   }
 
-  public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.TintableBackgroundView {
+  public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.OnReceiveContentViewBehavior androidx.core.view.TintableBackgroundView {
     ctor public AppCompatEditText(android.content.Context);
     ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?);
     ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?, int);
-    method public androidx.core.widget.RichContentReceiverCompat<android.widget.TextView!>? getRichContentReceiverCompat();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
-    method public void setRichContentReceiverCompat(androidx.core.widget.RichContentReceiverCompat<android.widget.TextView!>?);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
     method public void setTextAppearance(android.content.Context!, int);
diff --git a/appcompat/appcompat/api/public_plus_experimental_1.3.0-beta01.txt b/appcompat/appcompat/api/public_plus_experimental_1.3.0-beta01.txt
new file mode 100644
index 0000000..8886644
--- /dev/null
+++ b/appcompat/appcompat/api/public_plus_experimental_1.3.0-beta01.txt
@@ -0,0 +1,989 @@
+// Signature format: 4.0
+package androidx.appcompat.app {
+
+  public abstract class ActionBar {
+    ctor public ActionBar();
+    method public abstract void addOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, boolean);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, int);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, int, boolean);
+    method public abstract android.view.View! getCustomView();
+    method public abstract int getDisplayOptions();
+    method public float getElevation();
+    method public abstract int getHeight();
+    method public int getHideOffset();
+    method @Deprecated public abstract int getNavigationItemCount();
+    method @Deprecated public abstract int getNavigationMode();
+    method @Deprecated public abstract int getSelectedNavigationIndex();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab? getSelectedTab();
+    method public abstract CharSequence? getSubtitle();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! getTabAt(int);
+    method @Deprecated public abstract int getTabCount();
+    method public android.content.Context! getThemedContext();
+    method public abstract CharSequence? getTitle();
+    method public abstract void hide();
+    method public boolean isHideOnContentScrollEnabled();
+    method public abstract boolean isShowing();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! newTab();
+    method @Deprecated public abstract void removeAllTabs();
+    method public abstract void removeOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+    method @Deprecated public abstract void removeTab(androidx.appcompat.app.ActionBar.Tab!);
+    method @Deprecated public abstract void removeTabAt(int);
+    method @Deprecated public abstract void selectTab(androidx.appcompat.app.ActionBar.Tab!);
+    method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public abstract void setCustomView(android.view.View!);
+    method public abstract void setCustomView(android.view.View!, androidx.appcompat.app.ActionBar.LayoutParams!);
+    method public abstract void setCustomView(int);
+    method public abstract void setDisplayHomeAsUpEnabled(boolean);
+    method public abstract void setDisplayOptions(int);
+    method public abstract void setDisplayOptions(int, int);
+    method public abstract void setDisplayShowCustomEnabled(boolean);
+    method public abstract void setDisplayShowHomeEnabled(boolean);
+    method public abstract void setDisplayShowTitleEnabled(boolean);
+    method public abstract void setDisplayUseLogoEnabled(boolean);
+    method public void setElevation(float);
+    method public void setHideOffset(int);
+    method public void setHideOnContentScrollEnabled(boolean);
+    method public void setHomeActionContentDescription(CharSequence?);
+    method public void setHomeActionContentDescription(@StringRes int);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable?);
+    method public void setHomeAsUpIndicator(@DrawableRes int);
+    method public void setHomeButtonEnabled(boolean);
+    method public abstract void setIcon(@DrawableRes int);
+    method public abstract void setIcon(android.graphics.drawable.Drawable!);
+    method @Deprecated public abstract void setListNavigationCallbacks(android.widget.SpinnerAdapter!, androidx.appcompat.app.ActionBar.OnNavigationListener!);
+    method public abstract void setLogo(@DrawableRes int);
+    method public abstract void setLogo(android.graphics.drawable.Drawable!);
+    method @Deprecated public abstract void setNavigationMode(int);
+    method @Deprecated public abstract void setSelectedNavigationItem(int);
+    method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable!);
+    method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable!);
+    method public abstract void setSubtitle(CharSequence!);
+    method public abstract void setSubtitle(int);
+    method public abstract void setTitle(CharSequence!);
+    method public abstract void setTitle(@StringRes int);
+    method public abstract void show();
+    field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
+    field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
+    field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
+    field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
+    field public static final int DISPLAY_USE_LOGO = 1; // 0x1
+    field @Deprecated public static final int NAVIGATION_MODE_LIST = 1; // 0x1
+    field @Deprecated public static final int NAVIGATION_MODE_STANDARD = 0; // 0x0
+    field @Deprecated public static final int NAVIGATION_MODE_TABS = 2; // 0x2
+  }
+
+  public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet!);
+    ctor public ActionBar.LayoutParams(int, int);
+    ctor public ActionBar.LayoutParams(int, int, int);
+    ctor public ActionBar.LayoutParams(int);
+    ctor public ActionBar.LayoutParams(androidx.appcompat.app.ActionBar.LayoutParams!);
+    ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    field public int gravity;
+  }
+
+  public static interface ActionBar.OnMenuVisibilityListener {
+    method public void onMenuVisibilityChanged(boolean);
+  }
+
+  @Deprecated public static interface ActionBar.OnNavigationListener {
+    method @Deprecated public boolean onNavigationItemSelected(int, long);
+  }
+
+  @Deprecated public abstract static class ActionBar.Tab {
+    ctor @Deprecated public ActionBar.Tab();
+    method @Deprecated public abstract CharSequence! getContentDescription();
+    method @Deprecated public abstract android.view.View! getCustomView();
+    method @Deprecated public abstract android.graphics.drawable.Drawable! getIcon();
+    method @Deprecated public abstract int getPosition();
+    method @Deprecated public abstract Object! getTag();
+    method @Deprecated public abstract CharSequence! getText();
+    method @Deprecated public abstract void select();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setContentDescription(@StringRes int);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setContentDescription(CharSequence!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setCustomView(android.view.View!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setCustomView(int);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setIcon(android.graphics.drawable.Drawable!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setIcon(@DrawableRes int);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setTabListener(androidx.appcompat.app.ActionBar.TabListener!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setTag(Object!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setText(CharSequence!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setText(int);
+    field @Deprecated public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  @Deprecated public static interface ActionBar.TabListener {
+    method @Deprecated public void onTabReselected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+    method @Deprecated public void onTabSelected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+    method @Deprecated public void onTabUnselected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+  }
+
+  public class ActionBarDrawerToggle implements androidx.drawerlayout.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity!, androidx.drawerlayout.widget.DrawerLayout!, @StringRes int, @StringRes int);
+    ctor public ActionBarDrawerToggle(android.app.Activity!, androidx.drawerlayout.widget.DrawerLayout!, androidx.appcompat.widget.Toolbar!, @StringRes int, @StringRes int);
+    method public androidx.appcompat.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
+    method public android.view.View.OnClickListener! getToolbarNavigationClickListener();
+    method public boolean isDrawerIndicatorEnabled();
+    method public boolean isDrawerSlideAnimationEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration!);
+    method public void onDrawerClosed(android.view.View!);
+    method public void onDrawerOpened(android.view.View!);
+    method public void onDrawerSlide(android.view.View!, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem!);
+    method public void setDrawerArrowDrawable(androidx.appcompat.graphics.drawable.DrawerArrowDrawable);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setDrawerSlideAnimationEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable!);
+    method public void setHomeAsUpIndicator(int);
+    method public void setToolbarNavigationClickListener(android.view.View.OnClickListener!);
+    method public void syncState();
+  }
+
+  public static interface ActionBarDrawerToggle.Delegate {
+    method public android.content.Context! getActionBarThemedContext();
+    method public android.graphics.drawable.Drawable! getThemeUpIndicator();
+    method public boolean isNavigationVisible();
+    method public void setActionBarDescription(@StringRes int);
+    method public void setActionBarUpIndicator(android.graphics.drawable.Drawable!, @StringRes int);
+  }
+
+  public static interface ActionBarDrawerToggle.DelegateProvider {
+    method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+  }
+
+  public class AlertDialog extends androidx.appcompat.app.AppCompatDialog implements android.content.DialogInterface {
+    ctor protected AlertDialog(android.content.Context);
+    ctor protected AlertDialog(android.content.Context, @StyleRes int);
+    ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener?);
+    method public android.widget.Button! getButton(int);
+    method public android.widget.ListView! getListView();
+    method public void setButton(int, CharSequence!, android.os.Message!);
+    method public void setButton(int, CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public void setButton(int, CharSequence!, android.graphics.drawable.Drawable!, android.content.DialogInterface.OnClickListener!);
+    method public void setCustomTitle(android.view.View!);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setIconAttribute(int);
+    method public void setMessage(CharSequence!);
+    method public void setView(android.view.View!);
+    method public void setView(android.view.View!, int, int, int, int);
+  }
+
+  public static class AlertDialog.Builder {
+    ctor public AlertDialog.Builder(android.content.Context);
+    ctor public AlertDialog.Builder(android.content.Context, @StyleRes int);
+    method public androidx.appcompat.app.AlertDialog create();
+    method public android.content.Context getContext();
+    method public androidx.appcompat.app.AlertDialog.Builder! setAdapter(android.widget.ListAdapter!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setCancelable(boolean);
+    method public androidx.appcompat.app.AlertDialog.Builder! setCursor(android.database.Cursor!, android.content.DialogInterface.OnClickListener!, String!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setCustomTitle(android.view.View?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setIcon(@DrawableRes int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setIcon(android.graphics.drawable.Drawable?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setIconAttribute(@AttrRes int);
+    method @Deprecated public androidx.appcompat.app.AlertDialog.Builder! setInverseBackgroundForced(boolean);
+    method public androidx.appcompat.app.AlertDialog.Builder! setItems(@ArrayRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setItems(CharSequence![]!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMessage(@StringRes int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMessage(CharSequence?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(@ArrayRes int, boolean[]!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(CharSequence![]!, boolean[]!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(android.database.Cursor!, String!, String!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButtonIcon(android.graphics.drawable.Drawable!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButtonIcon(android.graphics.drawable.Drawable!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnCancelListener(android.content.DialogInterface.OnCancelListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnDismissListener(android.content.DialogInterface.OnDismissListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnKeyListener(android.content.DialogInterface.OnKeyListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButtonIcon(android.graphics.drawable.Drawable!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(@ArrayRes int, int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(android.database.Cursor!, int, String!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(CharSequence![]!, int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(android.widget.ListAdapter!, int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setTitle(@StringRes int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setTitle(CharSequence?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setView(int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setView(android.view.View!);
+    method public androidx.appcompat.app.AlertDialog! show();
+  }
+
+  public class AppCompatActivity extends androidx.fragment.app.FragmentActivity implements androidx.appcompat.app.ActionBarDrawerToggle.DelegateProvider androidx.appcompat.app.AppCompatCallback androidx.core.app.TaskStackBuilder.SupportParentable {
+    ctor public AppCompatActivity();
+    ctor @ContentView public AppCompatActivity(@LayoutRes int);
+    method public androidx.appcompat.app.AppCompatDelegate getDelegate();
+    method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+    method public androidx.appcompat.app.ActionBar? getSupportActionBar();
+    method public android.content.Intent? getSupportParentActivityIntent();
+    method public void onCreateSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder);
+    method public final boolean onMenuItemSelected(int, android.view.MenuItem);
+    method protected void onNightModeChanged(int);
+    method public void onPrepareSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder);
+    method @CallSuper public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode);
+    method @CallSuper public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode);
+    method @Deprecated public void onSupportContentChanged();
+    method public boolean onSupportNavigateUp();
+    method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+    method public void setSupportActionBar(androidx.appcompat.widget.Toolbar?);
+    method @Deprecated public void setSupportProgress(int);
+    method @Deprecated public void setSupportProgressBarIndeterminate(boolean);
+    method @Deprecated public void setSupportProgressBarIndeterminateVisibility(boolean);
+    method @Deprecated public void setSupportProgressBarVisibility(boolean);
+    method public androidx.appcompat.view.ActionMode? startSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+    method public void supportInvalidateOptionsMenu();
+    method public void supportNavigateUpTo(android.content.Intent);
+    method public boolean supportRequestWindowFeature(int);
+    method public boolean supportShouldUpRecreateTask(android.content.Intent);
+  }
+
+  public interface AppCompatCallback {
+    method public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode!);
+    method public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode!);
+    method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback!);
+  }
+
+  public abstract class AppCompatDelegate {
+    method public abstract void addContentView(android.view.View!, android.view.ViewGroup.LayoutParams!);
+    method public abstract boolean applyDayNight();
+    method @Deprecated public void attachBaseContext(android.content.Context!);
+    method @CallSuper public android.content.Context attachBaseContext2(android.content.Context);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.app.Activity, androidx.appcompat.app.AppCompatCallback?);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.app.Dialog, androidx.appcompat.app.AppCompatCallback?);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.content.Context, android.view.Window, androidx.appcompat.app.AppCompatCallback?);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.content.Context, android.app.Activity, androidx.appcompat.app.AppCompatCallback?);
+    method public abstract android.view.View! createView(android.view.View?, String!, android.content.Context, android.util.AttributeSet);
+    method public abstract <T extends android.view.View> T! findViewById(@IdRes int);
+    method public static int getDefaultNightMode();
+    method public abstract androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+    method public int getLocalNightMode();
+    method public abstract android.view.MenuInflater! getMenuInflater();
+    method public abstract androidx.appcompat.app.ActionBar? getSupportActionBar();
+    method public abstract boolean hasWindowFeature(int);
+    method public abstract void installViewFactory();
+    method public abstract void invalidateOptionsMenu();
+    method public static boolean isCompatVectorFromResourcesEnabled();
+    method public abstract boolean isHandleNativeActionModesEnabled();
+    method public abstract void onConfigurationChanged(android.content.res.Configuration!);
+    method public abstract void onCreate(android.os.Bundle!);
+    method public abstract void onDestroy();
+    method public abstract void onPostCreate(android.os.Bundle!);
+    method public abstract void onPostResume();
+    method public abstract void onSaveInstanceState(android.os.Bundle!);
+    method public abstract void onStart();
+    method public abstract void onStop();
+    method public abstract boolean requestWindowFeature(int);
+    method public static void setCompatVectorFromResourcesEnabled(boolean);
+    method public abstract void setContentView(android.view.View!);
+    method public abstract void setContentView(@LayoutRes int);
+    method public abstract void setContentView(android.view.View!, android.view.ViewGroup.LayoutParams!);
+    method public static void setDefaultNightMode(int);
+    method public abstract void setHandleNativeActionModesEnabled(boolean);
+    method @RequiresApi(17) public abstract void setLocalNightMode(int);
+    method public abstract void setSupportActionBar(androidx.appcompat.widget.Toolbar?);
+    method public void setTheme(@StyleRes int);
+    method public abstract void setTitle(CharSequence?);
+    method public abstract androidx.appcompat.view.ActionMode? startSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+    field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
+    field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+    field @Deprecated public static final int MODE_NIGHT_AUTO = 0; // 0x0
+    field public static final int MODE_NIGHT_AUTO_BATTERY = 3; // 0x3
+    field @Deprecated public static final int MODE_NIGHT_AUTO_TIME = 0; // 0x0
+    field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
+    field public static final int MODE_NIGHT_NO = 1; // 0x1
+    field public static final int MODE_NIGHT_UNSPECIFIED = -100; // 0xffffff9c
+    field public static final int MODE_NIGHT_YES = 2; // 0x2
+  }
+
+  public class AppCompatDialog extends android.app.Dialog implements androidx.appcompat.app.AppCompatCallback {
+    ctor public AppCompatDialog(android.content.Context!);
+    ctor public AppCompatDialog(android.content.Context!, int);
+    ctor protected AppCompatDialog(android.content.Context!, boolean, android.content.DialogInterface.OnCancelListener!);
+    method public androidx.appcompat.app.AppCompatDelegate! getDelegate();
+    method public androidx.appcompat.app.ActionBar! getSupportActionBar();
+    method public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode!);
+    method public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode!);
+    method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback!);
+    method public boolean supportRequestWindowFeature(int);
+  }
+
+  public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
+    ctor public AppCompatDialogFragment();
+  }
+
+  public class AppCompatViewInflater {
+    ctor public AppCompatViewInflater();
+    method protected androidx.appcompat.widget.AppCompatAutoCompleteTextView createAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatButton createButton(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatCheckBox createCheckBox(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatCheckedTextView createCheckedTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatEditText createEditText(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatImageButton createImageButton(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatImageView createImageView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView createMultiAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatRadioButton createRadioButton(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatRatingBar createRatingBar(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatSeekBar createSeekBar(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatSpinner createSpinner(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatTextView createTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatToggleButton createToggleButton(android.content.Context!, android.util.AttributeSet!);
+    method protected android.view.View? createView(android.content.Context!, String!, android.util.AttributeSet!);
+  }
+
+}
+
+package androidx.appcompat.graphics.drawable {
+
+  public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
+    ctor public DrawerArrowDrawable(android.content.Context!);
+    method public void draw(android.graphics.Canvas!);
+    method public float getArrowHeadLength();
+    method public float getArrowShaftLength();
+    method public float getBarLength();
+    method public float getBarThickness();
+    method @ColorInt public int getColor();
+    method public int getDirection();
+    method public float getGapSize();
+    method public int getOpacity();
+    method public final android.graphics.Paint! getPaint();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
+    method public boolean isSpinEnabled();
+    method public void setAlpha(int);
+    method public void setArrowHeadLength(float);
+    method public void setArrowShaftLength(float);
+    method public void setBarLength(float);
+    method public void setBarThickness(float);
+    method public void setColor(@ColorInt int);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setDirection(int);
+    method public void setGapSize(float);
+    method public void setProgress(@FloatRange(from=0.0, to=1.0) float);
+    method public void setSpinEnabled(boolean);
+    method public void setVerticalMirror(boolean);
+    field public static final int ARROW_DIRECTION_END = 3; // 0x3
+    field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
+    field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
+    field public static final int ARROW_DIRECTION_START = 2; // 0x2
+  }
+
+}
+
+package androidx.appcompat.view {
+
+  public abstract class ActionMode {
+    ctor public ActionMode();
+    method public abstract void finish();
+    method public abstract android.view.View! getCustomView();
+    method public abstract android.view.Menu! getMenu();
+    method public abstract android.view.MenuInflater! getMenuInflater();
+    method public abstract CharSequence! getSubtitle();
+    method public Object! getTag();
+    method public abstract CharSequence! getTitle();
+    method public boolean getTitleOptionalHint();
+    method public abstract void invalidate();
+    method public boolean isTitleOptional();
+    method public abstract void setCustomView(android.view.View!);
+    method public abstract void setSubtitle(CharSequence!);
+    method public abstract void setSubtitle(int);
+    method public void setTag(Object!);
+    method public abstract void setTitle(CharSequence!);
+    method public abstract void setTitle(int);
+    method public void setTitleOptionalHint(boolean);
+  }
+
+  public static interface ActionMode.Callback {
+    method public boolean onActionItemClicked(androidx.appcompat.view.ActionMode!, android.view.MenuItem!);
+    method public boolean onCreateActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+    method public void onDestroyActionMode(androidx.appcompat.view.ActionMode!);
+    method public boolean onPrepareActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+  }
+
+  @Deprecated public interface CollapsibleActionView {
+    method @Deprecated public void onActionViewCollapsed();
+    method @Deprecated public void onActionViewExpanded();
+  }
+
+  public class ContextThemeWrapper extends android.content.ContextWrapper {
+    ctor public ContextThemeWrapper();
+    ctor public ContextThemeWrapper(android.content.Context!, @StyleRes int);
+    ctor public ContextThemeWrapper(android.content.Context!, android.content.res.Resources.Theme!);
+    method public void applyOverrideConfiguration(android.content.res.Configuration!);
+    method public int getThemeResId();
+    method protected void onApplyThemeResource(android.content.res.Resources.Theme!, int, boolean);
+  }
+
+}
+
+package androidx.appcompat.widget {
+
+  public class ActionMenuView extends androidx.appcompat.widget.LinearLayoutCompat {
+    ctor public ActionMenuView(android.content.Context);
+    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet?);
+    method public void dismissPopupMenus();
+    method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public android.view.Menu! getMenu();
+    method public android.graphics.drawable.Drawable? getOverflowIcon();
+    method public int getPopupTheme();
+    method public boolean hideOverflowMenu();
+    method public boolean isOverflowMenuShowing();
+    method public void onConfigurationChanged(android.content.res.Configuration!);
+    method public void onDetachedFromWindow();
+    method public void setOnMenuItemClickListener(androidx.appcompat.widget.ActionMenuView.OnMenuItemClickListener!);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable?);
+    method public void setPopupTheme(@StyleRes int);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class ActionMenuView.LayoutParams extends androidx.appcompat.widget.LinearLayoutCompat.LayoutParams {
+    ctor public ActionMenuView.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+    ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    ctor public ActionMenuView.LayoutParams(androidx.appcompat.widget.ActionMenuView.LayoutParams!);
+    ctor public ActionMenuView.LayoutParams(int, int);
+    field public int cellsUsed;
+    field public boolean expandable;
+    field public int extraPixels;
+    field public boolean isOverflowButton;
+    field public boolean preventEdgeOffset;
+  }
+
+  public static interface ActionMenuView.OnMenuItemClickListener {
+    method public boolean onMenuItemClick(android.view.MenuItem!);
+  }
+
+  public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatButton extends android.widget.Button implements androidx.core.widget.AutoSizeableTextView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+    ctor public AppCompatButton(android.content.Context);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public void setSupportAllCaps(boolean);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatCheckBox extends android.widget.CheckBox implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton {
+    ctor public AppCompatCheckBox(android.content.Context);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
+    ctor public AppCompatCheckedTextView(android.content.Context);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?, int);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.OnReceiveContentViewBehavior androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatEditText(android.content.Context);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatImageButton extends android.widget.ImageButton implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableImageSourceView {
+    ctor public AppCompatImageButton(android.content.Context);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportImageTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportImageTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatImageView extends android.widget.ImageView implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableImageSourceView {
+    ctor public AppCompatImageView(android.content.Context);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportImageTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportImageTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatRadioButton extends android.widget.RadioButton implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton {
+    ctor public AppCompatRadioButton(android.content.Context!);
+    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?);
+    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatRatingBar extends android.widget.RatingBar {
+    ctor public AppCompatRatingBar(android.content.Context);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class AppCompatSeekBar extends android.widget.SeekBar {
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class AppCompatSpinner extends android.widget.Spinner implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatSpinner(android.content.Context);
+    ctor public AppCompatSpinner(android.content.Context, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int, android.content.res.Resources.Theme!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatTextView extends android.widget.TextView implements androidx.core.widget.AutoSizeableTextView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+    ctor public AppCompatTextView(android.content.Context);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method public androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParamsCompat();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public void setPrecomputedText(androidx.core.text.PrecomputedTextCompat);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+    method public void setTextFuture(java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>?);
+    method public void setTextMetricsParamsCompat(androidx.core.text.PrecomputedTextCompat.Params);
+  }
+
+  public class AppCompatToggleButton extends android.widget.ToggleButton implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatToggleButton(android.content.Context);
+    ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class LinearLayoutCompat extends android.view.ViewGroup {
+    ctor public LinearLayoutCompat(android.content.Context);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?, int);
+    method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public int getBaselineAlignedChildIndex();
+    method public android.graphics.drawable.Drawable! getDividerDrawable();
+    method public int getDividerPadding();
+    method public int getGravity();
+    method public int getOrientation();
+    method public int getShowDividers();
+    method public float getWeightSum();
+    method public boolean isBaselineAligned();
+    method public boolean isMeasureWithLargestChildEnabled();
+    method public void setBaselineAligned(boolean);
+    method public void setBaselineAlignedChildIndex(int);
+    method public void setDividerDrawable(android.graphics.drawable.Drawable!);
+    method public void setDividerPadding(int);
+    method public void setGravity(int);
+    method public void setHorizontalGravity(int);
+    method public void setMeasureWithLargestChildEnabled(boolean);
+    method public void setOrientation(int);
+    method public void setShowDividers(int);
+    method public void setVerticalGravity(int);
+    method public void setWeightSum(float);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
+    field public static final int SHOW_DIVIDER_END = 4; // 0x4
+    field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
+    field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  public static class LinearLayoutCompat.LayoutParams extends android.widget.LinearLayout.LayoutParams {
+    ctor public LinearLayoutCompat.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+    ctor public LinearLayoutCompat.LayoutParams(int, int);
+    ctor public LinearLayoutCompat.LayoutParams(int, int, float);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+  }
+
+  public class ListPopupWindow {
+    ctor public ListPopupWindow(android.content.Context);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?, @AttrRes int);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?, @AttrRes int, @StyleRes int);
+    method public void clearListSelection();
+    method public android.view.View.OnTouchListener! createDragToOpenListener(android.view.View!);
+    method public void dismiss();
+    method public android.view.View? getAnchorView();
+    method @StyleRes public int getAnimationStyle();
+    method public android.graphics.drawable.Drawable? getBackground();
+    method public android.graphics.Rect? getEpicenterBounds();
+    method public int getHeight();
+    method public int getHorizontalOffset();
+    method public int getInputMethodMode();
+    method public android.widget.ListView? getListView();
+    method public int getPromptPosition();
+    method public Object? getSelectedItem();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public android.view.View? getSelectedView();
+    method public int getSoftInputMode();
+    method public int getVerticalOffset();
+    method public int getWidth();
+    method public boolean isInputMethodNotNeeded();
+    method public boolean isModal();
+    method public boolean isShowing();
+    method public boolean onKeyDown(int, android.view.KeyEvent);
+    method public boolean onKeyPreIme(int, android.view.KeyEvent);
+    method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public boolean performItemClick(int);
+    method public void postShow();
+    method public void setAdapter(android.widget.ListAdapter?);
+    method public void setAnchorView(android.view.View?);
+    method public void setAnimationStyle(@StyleRes int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public void setContentWidth(int);
+    method public void setDropDownGravity(int);
+    method public void setEpicenterBounds(android.graphics.Rect?);
+    method public void setHeight(int);
+    method public void setHorizontalOffset(int);
+    method public void setInputMethodMode(int);
+    method public void setListSelector(android.graphics.drawable.Drawable!);
+    method public void setModal(boolean);
+    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener?);
+    method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener?);
+    method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener?);
+    method public void setPromptPosition(int);
+    method public void setPromptView(android.view.View?);
+    method public void setSelection(int);
+    method public void setSoftInputMode(int);
+    method public void setVerticalOffset(int);
+    method public void setWidth(int);
+    method public void setWindowLayoutType(int);
+    method public void show();
+    field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
+    field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
+    field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
+    field public static final int MATCH_PARENT = -1; // 0xffffffff
+    field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
+    field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
+    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
+  }
+
+  public class PopupMenu {
+    ctor public PopupMenu(android.content.Context, android.view.View);
+    ctor public PopupMenu(android.content.Context, android.view.View, int);
+    ctor public PopupMenu(android.content.Context, android.view.View, int, @AttrRes int, @StyleRes int);
+    method public void dismiss();
+    method public android.view.View.OnTouchListener getDragToOpenListener();
+    method public int getGravity();
+    method public android.view.Menu getMenu();
+    method public android.view.MenuInflater getMenuInflater();
+    method public void inflate(@MenuRes int);
+    method public void setGravity(int);
+    method public void setOnDismissListener(androidx.appcompat.widget.PopupMenu.OnDismissListener?);
+    method public void setOnMenuItemClickListener(androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener?);
+    method public void show();
+  }
+
+  public static interface PopupMenu.OnDismissListener {
+    method public void onDismiss(androidx.appcompat.widget.PopupMenu!);
+  }
+
+  public static interface PopupMenu.OnMenuItemClickListener {
+    method public boolean onMenuItemClick(android.view.MenuItem!);
+  }
+
+  public class SearchView extends androidx.appcompat.widget.LinearLayoutCompat implements androidx.appcompat.view.CollapsibleActionView {
+    ctor public SearchView(android.content.Context);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet?);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet?, int);
+    method public int getImeOptions();
+    method public int getInputType();
+    method public int getMaxWidth();
+    method public CharSequence! getQuery();
+    method public CharSequence? getQueryHint();
+    method public androidx.cursoradapter.widget.CursorAdapter! getSuggestionsAdapter();
+    method public boolean isIconfiedByDefault();
+    method public boolean isIconified();
+    method public boolean isQueryRefinementEnabled();
+    method public boolean isSubmitButtonEnabled();
+    method public void onActionViewCollapsed();
+    method public void onActionViewExpanded();
+    method public void setIconified(boolean);
+    method public void setIconifiedByDefault(boolean);
+    method public void setImeOptions(int);
+    method public void setInputType(int);
+    method public void setMaxWidth(int);
+    method public void setOnCloseListener(androidx.appcompat.widget.SearchView.OnCloseListener!);
+    method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener!);
+    method public void setOnQueryTextListener(androidx.appcompat.widget.SearchView.OnQueryTextListener!);
+    method public void setOnSearchClickListener(android.view.View.OnClickListener!);
+    method public void setOnSuggestionListener(androidx.appcompat.widget.SearchView.OnSuggestionListener!);
+    method public void setQuery(CharSequence!, boolean);
+    method public void setQueryHint(CharSequence?);
+    method public void setQueryRefinementEnabled(boolean);
+    method public void setSearchableInfo(android.app.SearchableInfo!);
+    method public void setSubmitButtonEnabled(boolean);
+    method public void setSuggestionsAdapter(androidx.cursoradapter.widget.CursorAdapter!);
+  }
+
+  public static interface SearchView.OnCloseListener {
+    method public boolean onClose();
+  }
+
+  public static interface SearchView.OnQueryTextListener {
+    method public boolean onQueryTextChange(String!);
+    method public boolean onQueryTextSubmit(String!);
+  }
+
+  public static interface SearchView.OnSuggestionListener {
+    method public boolean onSuggestionClick(int);
+    method public boolean onSuggestionSelect(int);
+  }
+
+  public class ShareActionProvider extends androidx.core.view.ActionProvider {
+    ctor public ShareActionProvider(android.content.Context!);
+    method public android.view.View! onCreateActionView();
+    method public void setOnShareTargetSelectedListener(androidx.appcompat.widget.ShareActionProvider.OnShareTargetSelectedListener!);
+    method public void setShareHistoryFileName(String!);
+    method public void setShareIntent(android.content.Intent!);
+    field public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+  }
+
+  public static interface ShareActionProvider.OnShareTargetSelectedListener {
+    method public boolean onShareTargetSelected(androidx.appcompat.widget.ShareActionProvider!, android.content.Intent!);
+  }
+
+  public class SwitchCompat extends android.widget.CompoundButton {
+    ctor public SwitchCompat(android.content.Context);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?, int);
+    method public boolean getShowText();
+    method public boolean getSplitTrack();
+    method public int getSwitchMinWidth();
+    method public int getSwitchPadding();
+    method public CharSequence! getTextOff();
+    method public CharSequence! getTextOn();
+    method public android.graphics.drawable.Drawable! getThumbDrawable();
+    method public int getThumbTextPadding();
+    method public android.content.res.ColorStateList? getThumbTintList();
+    method public android.graphics.PorterDuff.Mode? getThumbTintMode();
+    method public android.graphics.drawable.Drawable! getTrackDrawable();
+    method public android.content.res.ColorStateList? getTrackTintList();
+    method public android.graphics.PorterDuff.Mode? getTrackTintMode();
+    method public void onMeasure(int, int);
+    method public void setShowText(boolean);
+    method public void setSplitTrack(boolean);
+    method public void setSwitchMinWidth(int);
+    method public void setSwitchPadding(int);
+    method public void setSwitchTextAppearance(android.content.Context!, int);
+    method public void setSwitchTypeface(android.graphics.Typeface!, int);
+    method public void setSwitchTypeface(android.graphics.Typeface!);
+    method public void setTextOff(CharSequence!);
+    method public void setTextOn(CharSequence!);
+    method public void setThumbDrawable(android.graphics.drawable.Drawable!);
+    method public void setThumbResource(int);
+    method public void setThumbTextPadding(int);
+    method public void setThumbTintList(android.content.res.ColorStateList?);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTrackDrawable(android.graphics.drawable.Drawable!);
+    method public void setTrackResource(int);
+    method public void setTrackTintList(android.content.res.ColorStateList?);
+    method public void setTrackTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
+    method public android.content.res.Resources.Theme? getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme?);
+  }
+
+  public static final class ThemedSpinnerAdapter.Helper {
+    ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
+    method public android.view.LayoutInflater getDropDownViewInflater();
+    method public android.content.res.Resources.Theme? getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme?);
+  }
+
+  public class Toolbar extends android.view.ViewGroup {
+    ctor public Toolbar(android.content.Context);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet?);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet?, int);
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
+    method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.appcompat.widget.Toolbar.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public CharSequence? getCollapseContentDescription();
+    method public android.graphics.drawable.Drawable? getCollapseIcon();
+    method public int getContentInsetEnd();
+    method public int getContentInsetEndWithActions();
+    method public int getContentInsetLeft();
+    method public int getContentInsetRight();
+    method public int getContentInsetStart();
+    method public int getContentInsetStartWithNavigation();
+    method public int getCurrentContentInsetEnd();
+    method public int getCurrentContentInsetLeft();
+    method public int getCurrentContentInsetRight();
+    method public int getCurrentContentInsetStart();
+    method public android.graphics.drawable.Drawable! getLogo();
+    method public CharSequence! getLogoDescription();
+    method public android.view.Menu! getMenu();
+    method public CharSequence? getNavigationContentDescription();
+    method public android.graphics.drawable.Drawable? getNavigationIcon();
+    method public android.graphics.drawable.Drawable? getOverflowIcon();
+    method public int getPopupTheme();
+    method public CharSequence! getSubtitle();
+    method public CharSequence! getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
+    method public boolean hasExpandedActionView();
+    method public boolean hideOverflowMenu();
+    method public void inflateMenu(@MenuRes int);
+    method public boolean isOverflowMenuShowing();
+    method public void setCollapseContentDescription(@StringRes int);
+    method public void setCollapseContentDescription(CharSequence?);
+    method public void setCollapseIcon(@DrawableRes int);
+    method public void setCollapseIcon(android.graphics.drawable.Drawable?);
+    method public void setContentInsetEndWithActions(int);
+    method public void setContentInsetStartWithNavigation(int);
+    method public void setContentInsetsAbsolute(int, int);
+    method public void setContentInsetsRelative(int, int);
+    method public void setLogo(@DrawableRes int);
+    method public void setLogo(android.graphics.drawable.Drawable!);
+    method public void setLogoDescription(@StringRes int);
+    method public void setLogoDescription(CharSequence!);
+    method public void setNavigationContentDescription(@StringRes int);
+    method public void setNavigationContentDescription(CharSequence?);
+    method public void setNavigationIcon(@DrawableRes int);
+    method public void setNavigationIcon(android.graphics.drawable.Drawable?);
+    method public void setNavigationOnClickListener(android.view.View.OnClickListener!);
+    method public void setOnMenuItemClickListener(androidx.appcompat.widget.Toolbar.OnMenuItemClickListener!);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable?);
+    method public void setPopupTheme(@StyleRes int);
+    method public void setSubtitle(@StringRes int);
+    method public void setSubtitle(CharSequence!);
+    method public void setSubtitleTextAppearance(android.content.Context!, @StyleRes int);
+    method public void setSubtitleTextColor(@ColorInt int);
+    method public void setSubtitleTextColor(android.content.res.ColorStateList);
+    method public void setTitle(@StringRes int);
+    method public void setTitle(CharSequence!);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
+    method public void setTitleTextAppearance(android.content.Context!, @StyleRes int);
+    method public void setTitleTextColor(@ColorInt int);
+    method public void setTitleTextColor(android.content.res.ColorStateList);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class Toolbar.LayoutParams extends androidx.appcompat.app.ActionBar.LayoutParams {
+    ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet!);
+    ctor public Toolbar.LayoutParams(int, int);
+    ctor public Toolbar.LayoutParams(int, int, int);
+    ctor public Toolbar.LayoutParams(int);
+    ctor public Toolbar.LayoutParams(androidx.appcompat.widget.Toolbar.LayoutParams!);
+    ctor public Toolbar.LayoutParams(androidx.appcompat.app.ActionBar.LayoutParams!);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams!);
+  }
+
+  public static interface Toolbar.OnMenuItemClickListener {
+    method public boolean onMenuItemClick(android.view.MenuItem!);
+  }
+
+  public static class Toolbar.SavedState extends androidx.customview.view.AbsSavedState {
+    ctor public Toolbar.SavedState(android.os.Parcel!);
+    ctor public Toolbar.SavedState(android.os.Parcel!, ClassLoader!);
+    ctor public Toolbar.SavedState(android.os.Parcelable!);
+    field public static final android.os.Parcelable.Creator<androidx.appcompat.widget.Toolbar.SavedState!>! CREATOR;
+  }
+
+  public class TooltipCompat {
+    method public static void setTooltipText(android.view.View, CharSequence?);
+  }
+
+}
+
diff --git a/appcompat/appcompat/api/public_plus_experimental_current.txt b/appcompat/appcompat/api/public_plus_experimental_current.txt
index cb1efa6..8886644 100644
--- a/appcompat/appcompat/api/public_plus_experimental_current.txt
+++ b/appcompat/appcompat/api/public_plus_experimental_current.txt
@@ -513,15 +513,14 @@
     method public void setTextAppearance(android.content.Context!, int);
   }
 
-  public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.TintableBackgroundView {
+  public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.OnReceiveContentViewBehavior androidx.core.view.TintableBackgroundView {
     ctor public AppCompatEditText(android.content.Context);
     ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?);
     ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?, int);
-    method public androidx.core.widget.RichContentReceiverCompat<android.widget.TextView!>? getRichContentReceiverCompat();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
-    method public void setRichContentReceiverCompat(androidx.core.widget.RichContentReceiverCompat<android.widget.TextView!>?);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
     method public void setTextAppearance(android.content.Context!, int);
diff --git a/appcompat/appcompat/api/res-1.3.0-beta01.txt b/appcompat/appcompat/api/res-1.3.0-beta01.txt
new file mode 100644
index 0000000..78975ff
--- /dev/null
+++ b/appcompat/appcompat/api/res-1.3.0-beta01.txt
@@ -0,0 +1,368 @@
+attr actionBarDivider
+attr actionBarItemBackground
+attr actionBarPopupTheme
+attr actionBarSize
+attr actionBarSplitStyle
+attr actionBarStyle
+attr actionBarTabBarStyle
+attr actionBarTabStyle
+attr actionBarTabTextStyle
+attr actionBarTheme
+attr actionBarWidgetTheme
+attr actionButtonStyle
+attr actionDropDownStyle
+attr actionLayout
+attr actionMenuTextAppearance
+attr actionMenuTextColor
+attr actionModeBackground
+attr actionModeCloseButtonStyle
+attr actionModeCloseContentDescription
+attr actionModeCloseDrawable
+attr actionModeCopyDrawable
+attr actionModeCutDrawable
+attr actionModeFindDrawable
+attr actionModePasteDrawable
+attr actionModeSelectAllDrawable
+attr actionModeShareDrawable
+attr actionModeSplitBackground
+attr actionModeStyle
+attr actionModeTheme
+attr actionModeWebSearchDrawable
+attr actionOverflowButtonStyle
+attr actionOverflowMenuStyle
+attr actionProviderClass
+attr actionViewClass
+attr alertDialogStyle
+attr alertDialogTheme
+attr arrowHeadLength
+attr arrowShaftLength
+attr autoCompleteTextViewStyle
+attr autoSizeMaxTextSize
+attr autoSizeMinTextSize
+attr autoSizePresetSizes
+attr autoSizeStepGranularity
+attr autoSizeTextType
+attr background
+attr backgroundSplit
+attr backgroundStacked
+attr backgroundTint
+attr backgroundTintMode
+attr barLength
+attr borderlessButtonStyle
+attr buttonBarButtonStyle
+attr buttonBarNegativeButtonStyle
+attr buttonBarNeutralButtonStyle
+attr buttonBarPositiveButtonStyle
+attr buttonBarStyle
+attr buttonGravity
+attr buttonStyle
+attr buttonStyleSmall
+attr buttonTint
+attr buttonTintMode
+attr checkboxStyle
+attr checkedTextViewStyle
+attr closeIcon
+attr closeItemLayout
+attr collapseContentDescription
+attr collapseIcon
+attr color
+attr colorAccent
+attr colorBackgroundFloating
+attr colorButtonNormal
+attr colorControlActivated
+attr colorControlHighlight
+attr colorControlNormal
+attr colorError
+attr colorPrimary
+attr colorPrimaryDark
+attr commitIcon
+attr contentInsetEnd
+attr contentInsetEndWithActions
+attr contentInsetLeft
+attr contentInsetRight
+attr contentInsetStart
+attr contentInsetStartWithNavigation
+attr customNavigationLayout
+attr dialogCornerRadius
+attr dialogPreferredPadding
+attr dialogTheme
+attr displayOptions
+attr divider
+attr dividerHorizontal
+attr dividerPadding
+attr dividerVertical
+attr drawableSize
+attr drawerArrowStyle
+attr dropDownListViewStyle
+attr editTextBackground
+attr editTextColor
+attr editTextStyle
+attr elevation
+attr firstBaselineToTopHeight
+attr fontFamily
+attr fontVariationSettings
+attr gapBetweenBars
+attr goIcon
+attr height
+attr hideOnContentScroll
+attr homeAsUpIndicator
+attr homeLayout
+attr icon
+attr iconTint
+attr iconTintMode
+attr iconifiedByDefault
+attr imageButtonStyle
+attr indeterminateProgressStyle
+attr isLightTheme
+attr itemPadding
+attr lastBaselineToBottomHeight
+attr layout
+attr lineHeight
+attr listChoiceBackgroundIndicator
+attr listChoiceIndicatorMultipleAnimated
+attr listChoiceIndicatorSingleAnimated
+attr listDividerAlertDialog
+attr listPopupWindowStyle
+attr listPreferredItemHeight
+attr listPreferredItemHeightLarge
+attr listPreferredItemHeightSmall
+attr listPreferredItemPaddingEnd
+attr listPreferredItemPaddingLeft
+attr listPreferredItemPaddingRight
+attr listPreferredItemPaddingStart
+attr logo
+attr logoDescription
+attr maxButtonHeight
+attr measureWithLargestChild
+attr navigationContentDescription
+attr navigationIcon
+attr navigationMode
+attr overlapAnchor
+attr paddingEnd
+attr paddingStart
+attr panelBackground
+attr popupMenuStyle
+attr popupTheme
+attr popupWindowStyle
+attr preserveIconSpacing
+attr progressBarPadding
+attr progressBarStyle
+attr queryBackground
+attr queryHint
+attr radioButtonStyle
+attr ratingBarStyle
+attr ratingBarStyleIndicator
+attr ratingBarStyleSmall
+attr searchHintIcon
+attr searchIcon
+attr searchViewStyle
+attr seekBarStyle
+attr selectableItemBackground
+attr selectableItemBackgroundBorderless
+attr showAsAction
+attr showDividers
+attr showText
+attr spinBars
+attr spinnerDropDownItemStyle
+attr spinnerStyle
+attr splitTrack
+attr srcCompat
+attr state_above_anchor
+attr submitBackground
+attr subtitle
+attr subtitleTextAppearance
+attr subtitleTextColor
+attr subtitleTextStyle
+attr suggestionRowLayout
+attr switchMinWidth
+attr switchPadding
+attr switchStyle
+attr switchTextAppearance
+attr textAllCaps
+attr textAppearanceLargePopupMenu
+attr textAppearanceListItem
+attr textAppearanceListItemSecondary
+attr textAppearanceListItemSmall
+attr textAppearancePopupMenuHeader
+attr textAppearanceSearchResultSubtitle
+attr textAppearanceSearchResultTitle
+attr textAppearanceSmallPopupMenu
+attr textColorAlertDialogListItem
+attr textLocale
+attr theme
+attr thickness
+attr thumbTextPadding
+attr thumbTint
+attr thumbTintMode
+attr tickMark
+attr tickMarkTint
+attr tickMarkTintMode
+attr tint
+attr tintMode
+attr title
+attr titleMargin
+attr titleMarginBottom
+attr titleMarginEnd
+attr titleMarginStart
+attr titleMarginTop
+attr titleMargins
+attr titleTextAppearance
+attr titleTextColor
+attr titleTextStyle
+attr toolbarNavigationButtonStyle
+attr toolbarStyle
+attr track
+attr trackTint
+attr trackTintMode
+attr voiceIcon
+attr windowActionBar
+attr windowActionBarOverlay
+attr windowActionModeOverlay
+attr windowNoTitle
+layout support_simple_spinner_dropdown_item
+style TextAppearance_AppCompat
+style TextAppearance_AppCompat_Body1
+style TextAppearance_AppCompat_Body2
+style TextAppearance_AppCompat_Button
+style TextAppearance_AppCompat_Caption
+style TextAppearance_AppCompat_Display1
+style TextAppearance_AppCompat_Display2
+style TextAppearance_AppCompat_Display3
+style TextAppearance_AppCompat_Display4
+style TextAppearance_AppCompat_Headline
+style TextAppearance_AppCompat_Inverse
+style TextAppearance_AppCompat_Large
+style TextAppearance_AppCompat_Large_Inverse
+style TextAppearance_AppCompat_Light_SearchResult_Subtitle
+style TextAppearance_AppCompat_Light_SearchResult_Title
+style TextAppearance_AppCompat_Light_Widget_PopupMenu_Large
+style TextAppearance_AppCompat_Light_Widget_PopupMenu_Small
+style TextAppearance_AppCompat_Medium
+style TextAppearance_AppCompat_Medium_Inverse
+style TextAppearance_AppCompat_Menu
+style TextAppearance_AppCompat_SearchResult_Subtitle
+style TextAppearance_AppCompat_SearchResult_Title
+style TextAppearance_AppCompat_Small
+style TextAppearance_AppCompat_Small_Inverse
+style TextAppearance_AppCompat_Subhead
+style TextAppearance_AppCompat_Subhead_Inverse
+style TextAppearance_AppCompat_Title
+style TextAppearance_AppCompat_Title_Inverse
+style TextAppearance_AppCompat_Widget_ActionBar_Menu
+style TextAppearance_AppCompat_Widget_ActionBar_Subtitle
+style TextAppearance_AppCompat_Widget_ActionBar_Subtitle_Inverse
+style TextAppearance_AppCompat_Widget_ActionBar_Title
+style TextAppearance_AppCompat_Widget_ActionBar_Title_Inverse
+style TextAppearance_AppCompat_Widget_ActionMode_Subtitle
+style TextAppearance_AppCompat_Widget_ActionMode_Subtitle_Inverse
+style TextAppearance_AppCompat_Widget_ActionMode_Title
+style TextAppearance_AppCompat_Widget_ActionMode_Title_Inverse
+style TextAppearance_AppCompat_Widget_Button
+style TextAppearance_AppCompat_Widget_Button_Borderless_Colored
+style TextAppearance_AppCompat_Widget_Button_Colored
+style TextAppearance_AppCompat_Widget_Button_Inverse
+style TextAppearance_AppCompat_Widget_DropDownItem
+style TextAppearance_AppCompat_Widget_PopupMenu_Header
+style TextAppearance_AppCompat_Widget_PopupMenu_Large
+style TextAppearance_AppCompat_Widget_PopupMenu_Small
+style TextAppearance_AppCompat_Widget_Switch
+style TextAppearance_AppCompat_Widget_TextView_SpinnerItem
+style ThemeOverlay_AppCompat
+style ThemeOverlay_AppCompat_ActionBar
+style ThemeOverlay_AppCompat_Dark
+style ThemeOverlay_AppCompat_Dark_ActionBar
+style ThemeOverlay_AppCompat_DayNight
+style ThemeOverlay_AppCompat_DayNight_ActionBar
+style ThemeOverlay_AppCompat_Dialog
+style ThemeOverlay_AppCompat_Dialog_Alert
+style ThemeOverlay_AppCompat_Light
+style Theme_AppCompat
+style Theme_AppCompat_DayNight
+style Theme_AppCompat_DayNight_DarkActionBar
+style Theme_AppCompat_DayNight_Dialog
+style Theme_AppCompat_DayNight_DialogWhenLarge
+style Theme_AppCompat_DayNight_Dialog_Alert
+style Theme_AppCompat_DayNight_Dialog_MinWidth
+style Theme_AppCompat_DayNight_NoActionBar
+style Theme_AppCompat_Dialog
+style Theme_AppCompat_DialogWhenLarge
+style Theme_AppCompat_Dialog_Alert
+style Theme_AppCompat_Dialog_MinWidth
+style Theme_AppCompat_Light
+style Theme_AppCompat_Light_DarkActionBar
+style Theme_AppCompat_Light_Dialog
+style Theme_AppCompat_Light_DialogWhenLarge
+style Theme_AppCompat_Light_Dialog_Alert
+style Theme_AppCompat_Light_Dialog_MinWidth
+style Theme_AppCompat_Light_NoActionBar
+style Theme_AppCompat_NoActionBar
+style Widget_AppCompat_ActionBar
+style Widget_AppCompat_ActionBar_Solid
+style Widget_AppCompat_ActionBar_TabBar
+style Widget_AppCompat_ActionBar_TabText
+style Widget_AppCompat_ActionBar_TabView
+style Widget_AppCompat_ActionButton
+style Widget_AppCompat_ActionButton_CloseMode
+style Widget_AppCompat_ActionButton_Overflow
+style Widget_AppCompat_ActionMode
+style Widget_AppCompat_AutoCompleteTextView
+style Widget_AppCompat_Button
+style Widget_AppCompat_ButtonBar
+style Widget_AppCompat_ButtonBar_AlertDialog
+style Widget_AppCompat_Button_Borderless
+style Widget_AppCompat_Button_Borderless_Colored
+style Widget_AppCompat_Button_ButtonBar_AlertDialog
+style Widget_AppCompat_Button_Colored
+style Widget_AppCompat_Button_Small
+style Widget_AppCompat_CompoundButton_CheckBox
+style Widget_AppCompat_CompoundButton_RadioButton
+style Widget_AppCompat_CompoundButton_Switch
+style Widget_AppCompat_DrawerArrowToggle
+style Widget_AppCompat_DropDownItem_Spinner
+style Widget_AppCompat_EditText
+style Widget_AppCompat_ImageButton
+style Widget_AppCompat_Light_ActionBar
+style Widget_AppCompat_Light_ActionBar_Solid
+style Widget_AppCompat_Light_ActionBar_Solid_Inverse
+style Widget_AppCompat_Light_ActionBar_TabBar
+style Widget_AppCompat_Light_ActionBar_TabBar_Inverse
+style Widget_AppCompat_Light_ActionBar_TabText
+style Widget_AppCompat_Light_ActionBar_TabText_Inverse
+style Widget_AppCompat_Light_ActionBar_TabView
+style Widget_AppCompat_Light_ActionBar_TabView_Inverse
+style Widget_AppCompat_Light_ActionButton
+style Widget_AppCompat_Light_ActionButton_CloseMode
+style Widget_AppCompat_Light_ActionButton_Overflow
+style Widget_AppCompat_Light_ActionMode_Inverse
+style Widget_AppCompat_Light_AutoCompleteTextView
+style Widget_AppCompat_Light_DropDownItem_Spinner
+style Widget_AppCompat_Light_ListPopupWindow
+style Widget_AppCompat_Light_ListView_DropDown
+style Widget_AppCompat_Light_PopupMenu
+style Widget_AppCompat_Light_PopupMenu_Overflow
+style Widget_AppCompat_Light_SearchView
+style Widget_AppCompat_Light_Spinner_DropDown_ActionBar
+style Widget_AppCompat_ListPopupWindow
+style Widget_AppCompat_ListView
+style Widget_AppCompat_ListView_DropDown
+style Widget_AppCompat_ListView_Menu
+style Widget_AppCompat_PopupMenu
+style Widget_AppCompat_PopupMenu_Overflow
+style Widget_AppCompat_PopupWindow
+style Widget_AppCompat_ProgressBar
+style Widget_AppCompat_ProgressBar_Horizontal
+style Widget_AppCompat_RatingBar
+style Widget_AppCompat_RatingBar_Indicator
+style Widget_AppCompat_RatingBar_Small
+style Widget_AppCompat_SearchView
+style Widget_AppCompat_SearchView_ActionBar
+style Widget_AppCompat_SeekBar
+style Widget_AppCompat_SeekBar_Discrete
+style Widget_AppCompat_Spinner
+style Widget_AppCompat_Spinner_DropDown
+style Widget_AppCompat_Spinner_DropDown_ActionBar
+style Widget_AppCompat_Spinner_Underlined
+style Widget_AppCompat_TextView
+style Widget_AppCompat_TextView_SpinnerItem
+style Widget_AppCompat_Toolbar
+style Widget_AppCompat_Toolbar_Button_Navigation
diff --git a/appcompat/appcompat/api/restricted_1.3.0-beta01.txt b/appcompat/appcompat/api/restricted_1.3.0-beta01.txt
new file mode 100644
index 0000000..7439cfd
--- /dev/null
+++ b/appcompat/appcompat/api/restricted_1.3.0-beta01.txt
@@ -0,0 +1,2206 @@
+// Signature format: 4.0
+package androidx.appcompat.app {
+
+  public abstract class ActionBar {
+    ctor public ActionBar();
+    method public abstract void addOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, boolean);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, int);
+    method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, int, boolean);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean closeOptionsMenu();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean collapseActionView();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void dispatchMenuVisibilityChanged(boolean);
+    method public abstract android.view.View! getCustomView();
+    method @androidx.appcompat.app.ActionBar.DisplayOptions public abstract int getDisplayOptions();
+    method public float getElevation();
+    method public abstract int getHeight();
+    method public int getHideOffset();
+    method @Deprecated public abstract int getNavigationItemCount();
+    method @Deprecated @androidx.appcompat.app.ActionBar.NavigationMode public abstract int getNavigationMode();
+    method @Deprecated public abstract int getSelectedNavigationIndex();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab? getSelectedTab();
+    method public abstract CharSequence? getSubtitle();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! getTabAt(int);
+    method @Deprecated public abstract int getTabCount();
+    method public android.content.Context! getThemedContext();
+    method public abstract CharSequence? getTitle();
+    method public abstract void hide();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean invalidateOptionsMenu();
+    method public boolean isHideOnContentScrollEnabled();
+    method public abstract boolean isShowing();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isTitleTruncated();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! newTab();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void onConfigurationChanged(android.content.res.Configuration!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean onKeyShortcut(int, android.view.KeyEvent!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean onMenuKeyEvent(android.view.KeyEvent!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean openOptionsMenu();
+    method @Deprecated public abstract void removeAllTabs();
+    method public abstract void removeOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+    method @Deprecated public abstract void removeTab(androidx.appcompat.app.ActionBar.Tab!);
+    method @Deprecated public abstract void removeTabAt(int);
+    method @Deprecated public abstract void selectTab(androidx.appcompat.app.ActionBar.Tab!);
+    method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public abstract void setCustomView(android.view.View!);
+    method public abstract void setCustomView(android.view.View!, androidx.appcompat.app.ActionBar.LayoutParams!);
+    method public abstract void setCustomView(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setDefaultDisplayHomeAsUpEnabled(boolean);
+    method public abstract void setDisplayHomeAsUpEnabled(boolean);
+    method public abstract void setDisplayOptions(@androidx.appcompat.app.ActionBar.DisplayOptions int);
+    method public abstract void setDisplayOptions(@androidx.appcompat.app.ActionBar.DisplayOptions int, @androidx.appcompat.app.ActionBar.DisplayOptions int);
+    method public abstract void setDisplayShowCustomEnabled(boolean);
+    method public abstract void setDisplayShowHomeEnabled(boolean);
+    method public abstract void setDisplayShowTitleEnabled(boolean);
+    method public abstract void setDisplayUseLogoEnabled(boolean);
+    method public void setElevation(float);
+    method public void setHideOffset(int);
+    method public void setHideOnContentScrollEnabled(boolean);
+    method public void setHomeActionContentDescription(CharSequence?);
+    method public void setHomeActionContentDescription(@StringRes int);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable?);
+    method public void setHomeAsUpIndicator(@DrawableRes int);
+    method public void setHomeButtonEnabled(boolean);
+    method public abstract void setIcon(@DrawableRes int);
+    method public abstract void setIcon(android.graphics.drawable.Drawable!);
+    method @Deprecated public abstract void setListNavigationCallbacks(android.widget.SpinnerAdapter!, androidx.appcompat.app.ActionBar.OnNavigationListener!);
+    method public abstract void setLogo(@DrawableRes int);
+    method public abstract void setLogo(android.graphics.drawable.Drawable!);
+    method @Deprecated public abstract void setNavigationMode(@androidx.appcompat.app.ActionBar.NavigationMode int);
+    method @Deprecated public abstract void setSelectedNavigationItem(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setShowHideAnimationEnabled(boolean);
+    method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable!);
+    method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable!);
+    method public abstract void setSubtitle(CharSequence!);
+    method public abstract void setSubtitle(int);
+    method public abstract void setTitle(CharSequence!);
+    method public abstract void setTitle(@StringRes int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setWindowTitle(CharSequence!);
+    method public abstract void show();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.view.ActionMode! startActionMode(androidx.appcompat.view.ActionMode.Callback!);
+    field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
+    field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
+    field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
+    field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
+    field public static final int DISPLAY_USE_LOGO = 1; // 0x1
+    field @Deprecated public static final int NAVIGATION_MODE_LIST = 1; // 0x1
+    field @Deprecated public static final int NAVIGATION_MODE_STANDARD = 0; // 0x0
+    field @Deprecated public static final int NAVIGATION_MODE_TABS = 2; // 0x2
+  }
+
+  @IntDef(flag=true, value={androidx.appcompat.app.ActionBar.DISPLAY_USE_LOGO, androidx.appcompat.app.ActionBar.DISPLAY_SHOW_HOME, androidx.appcompat.app.ActionBar.DISPLAY_HOME_AS_UP, androidx.appcompat.app.ActionBar.DISPLAY_SHOW_TITLE, androidx.appcompat.app.ActionBar.DISPLAY_SHOW_CUSTOM}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ActionBar.DisplayOptions {
+  }
+
+  public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet!);
+    ctor public ActionBar.LayoutParams(int, int);
+    ctor public ActionBar.LayoutParams(int, int, int);
+    ctor public ActionBar.LayoutParams(int);
+    ctor public ActionBar.LayoutParams(androidx.appcompat.app.ActionBar.LayoutParams!);
+    ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    field public int gravity;
+  }
+
+  @IntDef({androidx.appcompat.app.ActionBar.NAVIGATION_MODE_STANDARD, androidx.appcompat.app.ActionBar.NAVIGATION_MODE_LIST, androidx.appcompat.app.ActionBar.NAVIGATION_MODE_TABS}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ActionBar.NavigationMode {
+  }
+
+  public static interface ActionBar.OnMenuVisibilityListener {
+    method public void onMenuVisibilityChanged(boolean);
+  }
+
+  @Deprecated public static interface ActionBar.OnNavigationListener {
+    method @Deprecated public boolean onNavigationItemSelected(int, long);
+  }
+
+  @Deprecated public abstract static class ActionBar.Tab {
+    ctor @Deprecated public ActionBar.Tab();
+    method @Deprecated public abstract CharSequence! getContentDescription();
+    method @Deprecated public abstract android.view.View! getCustomView();
+    method @Deprecated public abstract android.graphics.drawable.Drawable! getIcon();
+    method @Deprecated public abstract int getPosition();
+    method @Deprecated public abstract Object! getTag();
+    method @Deprecated public abstract CharSequence! getText();
+    method @Deprecated public abstract void select();
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setContentDescription(@StringRes int);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setContentDescription(CharSequence!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setCustomView(android.view.View!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setCustomView(int);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setIcon(android.graphics.drawable.Drawable!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setIcon(@DrawableRes int);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setTabListener(androidx.appcompat.app.ActionBar.TabListener!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setTag(Object!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setText(CharSequence!);
+    method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setText(int);
+    field @Deprecated public static final int INVALID_POSITION = -1; // 0xffffffff
+  }
+
+  @Deprecated public static interface ActionBar.TabListener {
+    method @Deprecated public void onTabReselected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+    method @Deprecated public void onTabSelected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+    method @Deprecated public void onTabUnselected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+  }
+
+  public class ActionBarDrawerToggle implements androidx.drawerlayout.widget.DrawerLayout.DrawerListener {
+    ctor public ActionBarDrawerToggle(android.app.Activity!, androidx.drawerlayout.widget.DrawerLayout!, @StringRes int, @StringRes int);
+    ctor public ActionBarDrawerToggle(android.app.Activity!, androidx.drawerlayout.widget.DrawerLayout!, androidx.appcompat.widget.Toolbar!, @StringRes int, @StringRes int);
+    method public androidx.appcompat.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
+    method public android.view.View.OnClickListener! getToolbarNavigationClickListener();
+    method public boolean isDrawerIndicatorEnabled();
+    method public boolean isDrawerSlideAnimationEnabled();
+    method public void onConfigurationChanged(android.content.res.Configuration!);
+    method public void onDrawerClosed(android.view.View!);
+    method public void onDrawerOpened(android.view.View!);
+    method public void onDrawerSlide(android.view.View!, float);
+    method public void onDrawerStateChanged(int);
+    method public boolean onOptionsItemSelected(android.view.MenuItem!);
+    method public void setDrawerArrowDrawable(androidx.appcompat.graphics.drawable.DrawerArrowDrawable);
+    method public void setDrawerIndicatorEnabled(boolean);
+    method public void setDrawerSlideAnimationEnabled(boolean);
+    method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable!);
+    method public void setHomeAsUpIndicator(int);
+    method public void setToolbarNavigationClickListener(android.view.View.OnClickListener!);
+    method public void syncState();
+  }
+
+  public static interface ActionBarDrawerToggle.Delegate {
+    method public android.content.Context! getActionBarThemedContext();
+    method public android.graphics.drawable.Drawable! getThemeUpIndicator();
+    method public boolean isNavigationVisible();
+    method public void setActionBarDescription(@StringRes int);
+    method public void setActionBarUpIndicator(android.graphics.drawable.Drawable!, @StringRes int);
+  }
+
+  public static interface ActionBarDrawerToggle.DelegateProvider {
+    method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+  }
+
+  public class AlertDialog extends androidx.appcompat.app.AppCompatDialog implements android.content.DialogInterface {
+    ctor protected AlertDialog(android.content.Context);
+    ctor protected AlertDialog(android.content.Context, @StyleRes int);
+    ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener?);
+    method public android.widget.Button! getButton(int);
+    method public android.widget.ListView! getListView();
+    method public void setButton(int, CharSequence!, android.os.Message!);
+    method public void setButton(int, CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public void setButton(int, CharSequence!, android.graphics.drawable.Drawable!, android.content.DialogInterface.OnClickListener!);
+    method public void setCustomTitle(android.view.View!);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setIconAttribute(int);
+    method public void setMessage(CharSequence!);
+    method public void setView(android.view.View!);
+    method public void setView(android.view.View!, int, int, int, int);
+  }
+
+  public static class AlertDialog.Builder {
+    ctor public AlertDialog.Builder(android.content.Context);
+    ctor public AlertDialog.Builder(android.content.Context, @StyleRes int);
+    method public androidx.appcompat.app.AlertDialog create();
+    method public android.content.Context getContext();
+    method public androidx.appcompat.app.AlertDialog.Builder! setAdapter(android.widget.ListAdapter!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setCancelable(boolean);
+    method public androidx.appcompat.app.AlertDialog.Builder! setCursor(android.database.Cursor!, android.content.DialogInterface.OnClickListener!, String!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setCustomTitle(android.view.View?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setIcon(@DrawableRes int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setIcon(android.graphics.drawable.Drawable?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setIconAttribute(@AttrRes int);
+    method @Deprecated public androidx.appcompat.app.AlertDialog.Builder! setInverseBackgroundForced(boolean);
+    method public androidx.appcompat.app.AlertDialog.Builder! setItems(@ArrayRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setItems(CharSequence![]!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMessage(@StringRes int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMessage(CharSequence?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(@ArrayRes int, boolean[]!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(CharSequence![]!, boolean[]!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(android.database.Cursor!, String!, String!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButtonIcon(android.graphics.drawable.Drawable!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButtonIcon(android.graphics.drawable.Drawable!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnCancelListener(android.content.DialogInterface.OnCancelListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnDismissListener(android.content.DialogInterface.OnDismissListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setOnKeyListener(android.content.DialogInterface.OnKeyListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButtonIcon(android.graphics.drawable.Drawable!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.app.AlertDialog.Builder! setRecycleOnMeasureEnabled(boolean);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(@ArrayRes int, int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(android.database.Cursor!, int, String!, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(CharSequence![]!, int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(android.widget.ListAdapter!, int, android.content.DialogInterface.OnClickListener!);
+    method public androidx.appcompat.app.AlertDialog.Builder! setTitle(@StringRes int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setTitle(CharSequence?);
+    method public androidx.appcompat.app.AlertDialog.Builder! setView(int);
+    method public androidx.appcompat.app.AlertDialog.Builder! setView(android.view.View!);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.app.AlertDialog.Builder! setView(android.view.View!, int, int, int, int);
+    method public androidx.appcompat.app.AlertDialog! show();
+  }
+
+  public class AppCompatActivity extends androidx.fragment.app.FragmentActivity implements androidx.appcompat.app.ActionBarDrawerToggle.DelegateProvider androidx.appcompat.app.AppCompatCallback androidx.core.app.TaskStackBuilder.SupportParentable {
+    ctor public AppCompatActivity();
+    ctor @ContentView public AppCompatActivity(@LayoutRes int);
+    method public androidx.appcompat.app.AppCompatDelegate getDelegate();
+    method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+    method public androidx.appcompat.app.ActionBar? getSupportActionBar();
+    method public android.content.Intent? getSupportParentActivityIntent();
+    method public void onCreateSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder);
+    method public final boolean onMenuItemSelected(int, android.view.MenuItem);
+    method protected void onNightModeChanged(@androidx.appcompat.app.AppCompatDelegate.NightMode int);
+    method public void onPrepareSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder);
+    method @CallSuper public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode);
+    method @CallSuper public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode);
+    method @Deprecated public void onSupportContentChanged();
+    method public boolean onSupportNavigateUp();
+    method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+    method public void setSupportActionBar(androidx.appcompat.widget.Toolbar?);
+    method @Deprecated public void setSupportProgress(int);
+    method @Deprecated public void setSupportProgressBarIndeterminate(boolean);
+    method @Deprecated public void setSupportProgressBarIndeterminateVisibility(boolean);
+    method @Deprecated public void setSupportProgressBarVisibility(boolean);
+    method public androidx.appcompat.view.ActionMode? startSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+    method public void supportInvalidateOptionsMenu();
+    method public void supportNavigateUpTo(android.content.Intent);
+    method public boolean supportRequestWindowFeature(int);
+    method public boolean supportShouldUpRecreateTask(android.content.Intent);
+  }
+
+  public interface AppCompatCallback {
+    method public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode!);
+    method public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode!);
+    method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback!);
+  }
+
+  public abstract class AppCompatDelegate {
+    method public abstract void addContentView(android.view.View!, android.view.ViewGroup.LayoutParams!);
+    method public abstract boolean applyDayNight();
+    method @Deprecated public void attachBaseContext(android.content.Context!);
+    method @CallSuper public android.content.Context attachBaseContext2(android.content.Context);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.app.Activity, androidx.appcompat.app.AppCompatCallback?);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.app.Dialog, androidx.appcompat.app.AppCompatCallback?);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.content.Context, android.view.Window, androidx.appcompat.app.AppCompatCallback?);
+    method public static androidx.appcompat.app.AppCompatDelegate create(android.content.Context, android.app.Activity, androidx.appcompat.app.AppCompatCallback?);
+    method public abstract android.view.View! createView(android.view.View?, String!, android.content.Context, android.util.AttributeSet);
+    method public abstract <T extends android.view.View> T! findViewById(@IdRes int);
+    method @androidx.appcompat.app.AppCompatDelegate.NightMode public static int getDefaultNightMode();
+    method public abstract androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+    method @androidx.appcompat.app.AppCompatDelegate.NightMode public int getLocalNightMode();
+    method public abstract android.view.MenuInflater! getMenuInflater();
+    method public abstract androidx.appcompat.app.ActionBar? getSupportActionBar();
+    method public abstract boolean hasWindowFeature(int);
+    method public abstract void installViewFactory();
+    method public abstract void invalidateOptionsMenu();
+    method public static boolean isCompatVectorFromResourcesEnabled();
+    method public abstract boolean isHandleNativeActionModesEnabled();
+    method public abstract void onConfigurationChanged(android.content.res.Configuration!);
+    method public abstract void onCreate(android.os.Bundle!);
+    method public abstract void onDestroy();
+    method public abstract void onPostCreate(android.os.Bundle!);
+    method public abstract void onPostResume();
+    method public abstract void onSaveInstanceState(android.os.Bundle!);
+    method public abstract void onStart();
+    method public abstract void onStop();
+    method public abstract boolean requestWindowFeature(int);
+    method public static void setCompatVectorFromResourcesEnabled(boolean);
+    method public abstract void setContentView(android.view.View!);
+    method public abstract void setContentView(@LayoutRes int);
+    method public abstract void setContentView(android.view.View!, android.view.ViewGroup.LayoutParams!);
+    method public static void setDefaultNightMode(@androidx.appcompat.app.AppCompatDelegate.NightMode int);
+    method public abstract void setHandleNativeActionModesEnabled(boolean);
+    method @RequiresApi(17) public abstract void setLocalNightMode(@androidx.appcompat.app.AppCompatDelegate.NightMode int);
+    method public abstract void setSupportActionBar(androidx.appcompat.widget.Toolbar?);
+    method public void setTheme(@StyleRes int);
+    method public abstract void setTitle(CharSequence?);
+    method public abstract androidx.appcompat.view.ActionMode? startSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+    field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
+    field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+    field @Deprecated public static final int MODE_NIGHT_AUTO = 0; // 0x0
+    field public static final int MODE_NIGHT_AUTO_BATTERY = 3; // 0x3
+    field @Deprecated public static final int MODE_NIGHT_AUTO_TIME = 0; // 0x0
+    field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
+    field public static final int MODE_NIGHT_NO = 1; // 0x1
+    field public static final int MODE_NIGHT_UNSPECIFIED = -100; // 0xffffff9c
+    field public static final int MODE_NIGHT_YES = 2; // 0x2
+  }
+
+  @IntDef({androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO, androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES, androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_AUTO_TIME, androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_UNSPECIFIED, androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AppCompatDelegate.NightMode {
+  }
+
+  public class AppCompatDialog extends android.app.Dialog implements androidx.appcompat.app.AppCompatCallback {
+    ctor public AppCompatDialog(android.content.Context!);
+    ctor public AppCompatDialog(android.content.Context!, int);
+    ctor protected AppCompatDialog(android.content.Context!, boolean, android.content.DialogInterface.OnCancelListener!);
+    method public androidx.appcompat.app.AppCompatDelegate! getDelegate();
+    method public androidx.appcompat.app.ActionBar! getSupportActionBar();
+    method public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode!);
+    method public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode!);
+    method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback!);
+    method public boolean supportRequestWindowFeature(int);
+  }
+
+  public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
+    ctor public AppCompatDialogFragment();
+  }
+
+  public class AppCompatViewInflater {
+    ctor public AppCompatViewInflater();
+    method protected androidx.appcompat.widget.AppCompatAutoCompleteTextView createAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatButton createButton(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatCheckBox createCheckBox(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatCheckedTextView createCheckedTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatEditText createEditText(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatImageButton createImageButton(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatImageView createImageView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView createMultiAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatRadioButton createRadioButton(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatRatingBar createRatingBar(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatSeekBar createSeekBar(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatSpinner createSpinner(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatTextView createTextView(android.content.Context!, android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.AppCompatToggleButton createToggleButton(android.content.Context!, android.util.AttributeSet!);
+    method protected android.view.View? createView(android.content.Context!, String!, android.util.AttributeSet!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class WindowDecorActionBar extends androidx.appcompat.app.ActionBar implements androidx.appcompat.widget.ActionBarOverlayLayout.ActionBarVisibilityCallback {
+    ctor public WindowDecorActionBar(android.app.Activity!, boolean);
+    ctor public WindowDecorActionBar(android.app.Dialog!);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public WindowDecorActionBar(android.view.View!);
+    method public void addOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+    method public void addTab(androidx.appcompat.app.ActionBar.Tab!);
+    method public void addTab(androidx.appcompat.app.ActionBar.Tab!, int);
+    method public void addTab(androidx.appcompat.app.ActionBar.Tab!, boolean);
+    method public void addTab(androidx.appcompat.app.ActionBar.Tab!, int, boolean);
+    method public void animateToMode(boolean);
+    method public void doHide(boolean);
+    method public void doShow(boolean);
+    method public void enableContentAnimations(boolean);
+    method public android.view.View! getCustomView();
+    method public int getDisplayOptions();
+    method public int getHeight();
+    method public int getNavigationItemCount();
+    method public int getNavigationMode();
+    method public int getSelectedNavigationIndex();
+    method public androidx.appcompat.app.ActionBar.Tab! getSelectedTab();
+    method public CharSequence! getSubtitle();
+    method public androidx.appcompat.app.ActionBar.Tab! getTabAt(int);
+    method public int getTabCount();
+    method public CharSequence! getTitle();
+    method public boolean hasIcon();
+    method public boolean hasLogo();
+    method public void hide();
+    method public void hideForSystem();
+    method public boolean isShowing();
+    method public androidx.appcompat.app.ActionBar.Tab! newTab();
+    method public void onContentScrollStarted();
+    method public void onContentScrollStopped();
+    method public void onWindowVisibilityChanged(int);
+    method public void removeAllTabs();
+    method public void removeOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+    method public void removeTab(androidx.appcompat.app.ActionBar.Tab!);
+    method public void removeTabAt(int);
+    method public boolean requestFocus();
+    method public void selectTab(androidx.appcompat.app.ActionBar.Tab!);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
+    method public void setCustomView(int);
+    method public void setCustomView(android.view.View!);
+    method public void setCustomView(android.view.View!, androidx.appcompat.app.ActionBar.LayoutParams!);
+    method public void setDisplayHomeAsUpEnabled(boolean);
+    method public void setDisplayOptions(int);
+    method public void setDisplayOptions(int, int);
+    method public void setDisplayShowCustomEnabled(boolean);
+    method public void setDisplayShowHomeEnabled(boolean);
+    method public void setDisplayShowTitleEnabled(boolean);
+    method public void setDisplayUseLogoEnabled(boolean);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setListNavigationCallbacks(android.widget.SpinnerAdapter!, androidx.appcompat.app.ActionBar.OnNavigationListener!);
+    method public void setLogo(int);
+    method public void setLogo(android.graphics.drawable.Drawable!);
+    method public void setNavigationMode(int);
+    method public void setSelectedNavigationItem(int);
+    method public void setSubtitle(int);
+    method public void setSubtitle(CharSequence!);
+    method public void setTitle(int);
+    method public void setTitle(CharSequence!);
+    method public void show();
+    method public void showForSystem();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class WindowDecorActionBar.ActionModeImpl extends androidx.appcompat.view.ActionMode implements androidx.appcompat.view.menu.MenuBuilder.Callback {
+    ctor public WindowDecorActionBar.ActionModeImpl(android.content.Context!, androidx.appcompat.view.ActionMode.Callback!);
+    method public boolean dispatchOnCreate();
+    method public void finish();
+    method public android.view.View! getCustomView();
+    method public android.view.Menu! getMenu();
+    method public android.view.MenuInflater! getMenuInflater();
+    method public CharSequence! getSubtitle();
+    method public CharSequence! getTitle();
+    method public void invalidate();
+    method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder!, boolean);
+    method public void onCloseSubMenu(androidx.appcompat.view.menu.SubMenuBuilder!);
+    method public boolean onMenuItemSelected(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+    method public void onMenuModeChange(androidx.appcompat.view.menu.MenuBuilder);
+    method public boolean onSubMenuSelected(androidx.appcompat.view.menu.SubMenuBuilder!);
+    method public void setCustomView(android.view.View!);
+    method public void setSubtitle(CharSequence!);
+    method public void setSubtitle(int);
+    method public void setTitle(CharSequence!);
+    method public void setTitle(int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class WindowDecorActionBar.TabImpl extends androidx.appcompat.app.ActionBar.Tab {
+    ctor public WindowDecorActionBar.TabImpl();
+    method public androidx.appcompat.app.ActionBar.TabListener! getCallback();
+    method public CharSequence! getContentDescription();
+    method public android.view.View! getCustomView();
+    method public android.graphics.drawable.Drawable! getIcon();
+    method public int getPosition();
+    method public Object! getTag();
+    method public CharSequence! getText();
+    method public void select();
+    method public androidx.appcompat.app.ActionBar.Tab! setContentDescription(int);
+    method public androidx.appcompat.app.ActionBar.Tab! setContentDescription(CharSequence!);
+    method public androidx.appcompat.app.ActionBar.Tab! setCustomView(android.view.View!);
+    method public androidx.appcompat.app.ActionBar.Tab! setCustomView(int);
+    method public androidx.appcompat.app.ActionBar.Tab! setIcon(android.graphics.drawable.Drawable!);
+    method public androidx.appcompat.app.ActionBar.Tab! setIcon(int);
+    method public void setPosition(int);
+    method public androidx.appcompat.app.ActionBar.Tab! setTabListener(androidx.appcompat.app.ActionBar.TabListener!);
+    method public androidx.appcompat.app.ActionBar.Tab! setTag(Object!);
+    method public androidx.appcompat.app.ActionBar.Tab! setText(CharSequence!);
+    method public androidx.appcompat.app.ActionBar.Tab! setText(int);
+  }
+
+}
+
+package androidx.appcompat.graphics.drawable {
+
+  public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
+    ctor public DrawerArrowDrawable(android.content.Context!);
+    method public void draw(android.graphics.Canvas!);
+    method public float getArrowHeadLength();
+    method public float getArrowShaftLength();
+    method public float getBarLength();
+    method public float getBarThickness();
+    method @ColorInt public int getColor();
+    method @androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ArrowDirection public int getDirection();
+    method public float getGapSize();
+    method public int getOpacity();
+    method public final android.graphics.Paint! getPaint();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
+    method public boolean isSpinEnabled();
+    method public void setAlpha(int);
+    method public void setArrowHeadLength(float);
+    method public void setArrowShaftLength(float);
+    method public void setBarLength(float);
+    method public void setBarThickness(float);
+    method public void setColor(@ColorInt int);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setDirection(@androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ArrowDirection int);
+    method public void setGapSize(float);
+    method public void setProgress(@FloatRange(from=0.0, to=1.0) float);
+    method public void setSpinEnabled(boolean);
+    method public void setVerticalMirror(boolean);
+    field public static final int ARROW_DIRECTION_END = 3; // 0x3
+    field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
+    field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
+    field public static final int ARROW_DIRECTION_START = 2; // 0x2
+  }
+
+  @IntDef({androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ARROW_DIRECTION_LEFT, androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ARROW_DIRECTION_RIGHT, androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ARROW_DIRECTION_START, androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ARROW_DIRECTION_END}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface DrawerArrowDrawable.ArrowDirection {
+  }
+
+}
+
+package androidx.appcompat.text {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AllCapsTransformationMethod implements android.text.method.TransformationMethod {
+    ctor public AllCapsTransformationMethod(android.content.Context!);
+    method public CharSequence! getTransformation(CharSequence!, android.view.View!);
+    method public void onFocusChanged(android.view.View!, CharSequence!, boolean, int, android.graphics.Rect!);
+  }
+
+}
+
+package androidx.appcompat.view {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionBarPolicy {
+    method public boolean enableHomeButtonByDefault();
+    method public static androidx.appcompat.view.ActionBarPolicy! get(android.content.Context!);
+    method public int getEmbeddedMenuWidthLimit();
+    method public int getMaxActionButtons();
+    method public int getStackedTabMaxWidth();
+    method public int getTabContainerHeight();
+    method public boolean hasEmbeddedTabs();
+    method public boolean showsOverflowMenuButton();
+  }
+
+  public abstract class ActionMode {
+    ctor public ActionMode();
+    method public abstract void finish();
+    method public abstract android.view.View! getCustomView();
+    method public abstract android.view.Menu! getMenu();
+    method public abstract android.view.MenuInflater! getMenuInflater();
+    method public abstract CharSequence! getSubtitle();
+    method public Object! getTag();
+    method public abstract CharSequence! getTitle();
+    method public boolean getTitleOptionalHint();
+    method public abstract void invalidate();
+    method public boolean isTitleOptional();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isUiFocusable();
+    method public abstract void setCustomView(android.view.View!);
+    method public abstract void setSubtitle(CharSequence!);
+    method public abstract void setSubtitle(int);
+    method public void setTag(Object!);
+    method public abstract void setTitle(CharSequence!);
+    method public abstract void setTitle(int);
+    method public void setTitleOptionalHint(boolean);
+  }
+
+  public static interface ActionMode.Callback {
+    method public boolean onActionItemClicked(androidx.appcompat.view.ActionMode!, android.view.MenuItem!);
+    method public boolean onCreateActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+    method public void onDestroyActionMode(androidx.appcompat.view.ActionMode!);
+    method public boolean onPrepareActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+  }
+
+  @Deprecated public interface CollapsibleActionView {
+    method @Deprecated public void onActionViewCollapsed();
+    method @Deprecated public void onActionViewExpanded();
+  }
+
+  public class ContextThemeWrapper extends android.content.ContextWrapper {
+    ctor public ContextThemeWrapper();
+    ctor public ContextThemeWrapper(android.content.Context!, @StyleRes int);
+    ctor public ContextThemeWrapper(android.content.Context!, android.content.res.Resources.Theme!);
+    method public void applyOverrideConfiguration(android.content.res.Configuration!);
+    method public int getThemeResId();
+    method protected void onApplyThemeResource(android.content.res.Resources.Theme!, int, boolean);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class StandaloneActionMode extends androidx.appcompat.view.ActionMode implements androidx.appcompat.view.menu.MenuBuilder.Callback {
+    ctor public StandaloneActionMode(android.content.Context!, androidx.appcompat.widget.ActionBarContextView!, androidx.appcompat.view.ActionMode.Callback!, boolean);
+    method public void finish();
+    method public android.view.View! getCustomView();
+    method public android.view.Menu! getMenu();
+    method public android.view.MenuInflater! getMenuInflater();
+    method public CharSequence! getSubtitle();
+    method public CharSequence! getTitle();
+    method public void invalidate();
+    method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder!, boolean);
+    method public void onCloseSubMenu(androidx.appcompat.view.menu.SubMenuBuilder!);
+    method public boolean onMenuItemSelected(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+    method public void onMenuModeChange(androidx.appcompat.view.menu.MenuBuilder);
+    method public boolean onSubMenuSelected(androidx.appcompat.view.menu.SubMenuBuilder!);
+    method public void setCustomView(android.view.View!);
+    method public void setSubtitle(CharSequence!);
+    method public void setSubtitle(int);
+    method public void setTitle(CharSequence!);
+    method public void setTitle(int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class SupportActionModeWrapper extends android.view.ActionMode {
+    ctor public SupportActionModeWrapper(android.content.Context!, androidx.appcompat.view.ActionMode!);
+    method public void finish();
+    method public android.view.View! getCustomView();
+    method public android.view.Menu! getMenu();
+    method public android.view.MenuInflater! getMenuInflater();
+    method public CharSequence! getSubtitle();
+    method public CharSequence! getTitle();
+    method public void invalidate();
+    method public void setCustomView(android.view.View!);
+    method public void setSubtitle(CharSequence!);
+    method public void setSubtitle(int);
+    method public void setTitle(CharSequence!);
+    method public void setTitle(int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class SupportActionModeWrapper.CallbackWrapper implements androidx.appcompat.view.ActionMode.Callback {
+    ctor public SupportActionModeWrapper.CallbackWrapper(android.content.Context!, android.view.ActionMode.Callback!);
+    method public android.view.ActionMode! getActionModeWrapper(androidx.appcompat.view.ActionMode!);
+    method public boolean onActionItemClicked(androidx.appcompat.view.ActionMode!, android.view.MenuItem!);
+    method public boolean onCreateActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+    method public void onDestroyActionMode(androidx.appcompat.view.ActionMode!);
+    method public boolean onPrepareActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class SupportMenuInflater extends android.view.MenuInflater {
+    ctor public SupportMenuInflater(android.content.Context!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ViewPropertyAnimatorCompatSet {
+    ctor public ViewPropertyAnimatorCompatSet();
+    method public void cancel();
+    method public androidx.appcompat.view.ViewPropertyAnimatorCompatSet! play(androidx.core.view.ViewPropertyAnimatorCompat!);
+    method public androidx.appcompat.view.ViewPropertyAnimatorCompatSet! playSequentially(androidx.core.view.ViewPropertyAnimatorCompat!, androidx.core.view.ViewPropertyAnimatorCompat!);
+    method public androidx.appcompat.view.ViewPropertyAnimatorCompatSet! setDuration(long);
+    method public androidx.appcompat.view.ViewPropertyAnimatorCompatSet! setInterpolator(android.view.animation.Interpolator!);
+    method public androidx.appcompat.view.ViewPropertyAnimatorCompatSet! setListener(androidx.core.view.ViewPropertyAnimatorListener!);
+    method public void start();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class WindowCallbackWrapper implements android.view.Window.Callback {
+    ctor public WindowCallbackWrapper(android.view.Window.Callback!);
+    method public boolean dispatchGenericMotionEvent(android.view.MotionEvent!);
+    method public boolean dispatchKeyEvent(android.view.KeyEvent!);
+    method public boolean dispatchKeyShortcutEvent(android.view.KeyEvent!);
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent!);
+    method public boolean dispatchTouchEvent(android.view.MotionEvent!);
+    method public boolean dispatchTrackballEvent(android.view.MotionEvent!);
+    method public final android.view.Window.Callback! getWrapped();
+    method public void onActionModeFinished(android.view.ActionMode!);
+    method public void onActionModeStarted(android.view.ActionMode!);
+    method public void onAttachedToWindow();
+    method public void onContentChanged();
+    method public boolean onCreatePanelMenu(int, android.view.Menu!);
+    method public android.view.View! onCreatePanelView(int);
+    method public void onDetachedFromWindow();
+    method public boolean onMenuItemSelected(int, android.view.MenuItem!);
+    method public boolean onMenuOpened(int, android.view.Menu!);
+    method public void onPanelClosed(int, android.view.Menu!);
+    method public boolean onPreparePanel(int, android.view.View!, android.view.Menu!);
+    method @RequiresApi(23) public boolean onSearchRequested(android.view.SearchEvent!);
+    method public boolean onSearchRequested();
+    method public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams!);
+    method public void onWindowFocusChanged(boolean);
+    method public android.view.ActionMode! onWindowStartingActionMode(android.view.ActionMode.Callback!);
+    method @RequiresApi(23) public android.view.ActionMode! onWindowStartingActionMode(android.view.ActionMode.Callback!, int);
+  }
+
+}
+
+package androidx.appcompat.view.menu {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionMenuItem implements androidx.core.internal.view.SupportMenuItem {
+    ctor public ActionMenuItem(android.content.Context!, int, int, int, int, CharSequence!);
+    method public boolean collapseActionView();
+    method public boolean expandActionView();
+    method public android.view.ActionProvider! getActionProvider();
+    method public android.view.View! getActionView();
+    method public char getAlphabeticShortcut();
+    method public int getGroupId();
+    method public android.graphics.drawable.Drawable! getIcon();
+    method public android.content.Intent! getIntent();
+    method public int getItemId();
+    method public android.view.ContextMenu.ContextMenuInfo! getMenuInfo();
+    method public char getNumericShortcut();
+    method public int getOrder();
+    method public android.view.SubMenu! getSubMenu();
+    method public androidx.core.view.ActionProvider! getSupportActionProvider();
+    method public CharSequence! getTitle();
+    method public CharSequence! getTitleCondensed();
+    method public boolean hasSubMenu();
+    method public boolean invoke();
+    method public boolean isActionViewExpanded();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isEnabled();
+    method public boolean isVisible();
+    method public boolean requiresActionButton();
+    method public boolean requiresOverflow();
+    method public android.view.MenuItem! setActionProvider(android.view.ActionProvider!);
+    method public androidx.core.internal.view.SupportMenuItem! setActionView(android.view.View!);
+    method public androidx.core.internal.view.SupportMenuItem! setActionView(int);
+    method public android.view.MenuItem! setAlphabeticShortcut(char);
+    method public android.view.MenuItem! setCheckable(boolean);
+    method public android.view.MenuItem! setChecked(boolean);
+    method public androidx.core.internal.view.SupportMenuItem! setContentDescription(CharSequence!);
+    method public android.view.MenuItem! setEnabled(boolean);
+    method public androidx.appcompat.view.menu.ActionMenuItem! setExclusiveCheckable(boolean);
+    method public android.view.MenuItem! setIcon(android.graphics.drawable.Drawable!);
+    method public android.view.MenuItem! setIcon(int);
+    method public android.view.MenuItem! setIntent(android.content.Intent!);
+    method public android.view.MenuItem! setNumericShortcut(char);
+    method public android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem.OnActionExpandListener!);
+    method public android.view.MenuItem! setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener!);
+    method public android.view.MenuItem! setShortcut(char, char);
+    method public void setShowAsAction(int);
+    method public androidx.core.internal.view.SupportMenuItem! setShowAsActionFlags(int);
+    method public androidx.core.internal.view.SupportMenuItem! setSupportActionProvider(androidx.core.view.ActionProvider!);
+    method public android.view.MenuItem! setTitle(CharSequence!);
+    method public android.view.MenuItem! setTitle(int);
+    method public android.view.MenuItem! setTitleCondensed(CharSequence!);
+    method public androidx.core.internal.view.SupportMenuItem! setTooltipText(CharSequence!);
+    method public android.view.MenuItem! setVisible(boolean);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionMenuItemView extends androidx.appcompat.widget.AppCompatTextView implements androidx.appcompat.widget.ActionMenuView.ActionMenuChildView androidx.appcompat.view.menu.MenuView.ItemView android.view.View.OnClickListener {
+    ctor public ActionMenuItemView(android.content.Context!);
+    ctor public ActionMenuItemView(android.content.Context!, android.util.AttributeSet!);
+    ctor public ActionMenuItemView(android.content.Context!, android.util.AttributeSet!, int);
+    method public androidx.appcompat.view.menu.MenuItemImpl! getItemData();
+    method public boolean hasText();
+    method public void initialize(androidx.appcompat.view.menu.MenuItemImpl!, int);
+    method public boolean needsDividerAfter();
+    method public boolean needsDividerBefore();
+    method public void onClick(android.view.View!);
+    method public void onConfigurationChanged(android.content.res.Configuration!);
+    method public boolean prefersCondensedTitle();
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setExpandedFormat(boolean);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setItemInvoker(androidx.appcompat.view.menu.MenuBuilder.ItemInvoker!);
+    method public void setPopupCallback(androidx.appcompat.view.menu.ActionMenuItemView.PopupCallback!);
+    method public void setShortcut(boolean, char);
+    method public void setTitle(CharSequence!);
+    method public boolean showsIcon();
+  }
+
+  public abstract static class ActionMenuItemView.PopupCallback {
+    ctor public ActionMenuItemView.PopupCallback();
+    method public abstract androidx.appcompat.view.menu.ShowableListMenu! getPopup();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class BaseMenuPresenter implements androidx.appcompat.view.menu.MenuPresenter {
+    ctor public BaseMenuPresenter(android.content.Context!, int, int);
+    method protected void addItemView(android.view.View!, int);
+    method public abstract void bindItemView(androidx.appcompat.view.menu.MenuItemImpl!, androidx.appcompat.view.menu.MenuView.ItemView!);
+    method public boolean collapseItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+    method public androidx.appcompat.view.menu.MenuView.ItemView! createItemView(android.view.ViewGroup!);
+    method public boolean expandItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+    method protected boolean filterLeftoverView(android.view.ViewGroup!, int);
+    method public boolean flagActionItems();
+    method public androidx.appcompat.view.menu.MenuPresenter.Callback! getCallback();
+    method public int getId();
+    method public android.view.View! getItemView(androidx.appcompat.view.menu.MenuItemImpl!, android.view.View!, android.view.ViewGroup!);
+    method public androidx.appcompat.view.menu.MenuView! getMenuView(android.view.ViewGroup!);
+    method public void initForMenu(android.content.Context!, androidx.appcompat.view.menu.MenuBuilder!);
+    method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder!, boolean);
+    method public boolean onSubMenuSelected(androidx.appcompat.view.menu.SubMenuBuilder!);
+    method public void setCallback(androidx.appcompat.view.menu.MenuPresenter.Callback!);
+    method public void setId(int);
+    method public boolean shouldIncludeItem(int, androidx.appcompat.view.menu.MenuItemImpl!);
+    method public void updateMenuView(boolean);
+    field protected android.content.Context! mContext;
+    field protected android.view.LayoutInflater! mInflater;
+    field protected androidx.appcompat.view.menu.MenuBuilder! mMenu;
+    field protected androidx.appcompat.view.menu.MenuView! mMenuView;
+    field protected android.content.Context! mSystemContext;
+    field protected android.view.LayoutInflater! mSystemInflater;
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ExpandedMenuView extends android.widget.ListView implements android.widget.AdapterView.OnItemClickListener androidx.appcompat.view.menu.MenuBuilder.ItemInvoker androidx.appcompat.view.menu.MenuView {
+    ctor public ExpandedMenuView(android.content.Context!, android.util.AttributeSet!);
+    ctor public ExpandedMenuView(android.content.Context!, android.util.AttributeSet!, int);
+    method public int getWindowAnimations();
+    method public void initialize(androidx.appcompat.view.menu.MenuBuilder!);
+    method public boolean invokeItem(androidx.appcompat.view.menu.MenuItemImpl!);
+    method public void onItemClick(android.widget.AdapterView!, android.view.View!, int, long);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ListMenuItemView extends android.widget.LinearLayout implements android.widget.AbsListView.SelectionBoundsAdjuster androidx.appcompat.view.menu.MenuView.ItemView {
+    ctor public ListMenuItemView(android.content.Context!, android.util.AttributeSet!);
+    ctor public ListMenuItemView(android.content.Context!, android.util.AttributeSet!, int);
+    method public void adjustListItemSelectionBounds(android.graphics.Rect!);
+    method public androidx.appcompat.view.menu.MenuItemImpl! getItemData();
+    method public void initialize(androidx.appcompat.view.menu.MenuItemImpl!, int);
+    method public boolean prefersCondensedTitle();
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setForceShowIcon(boolean);
+    method public void setGroupDividerEnabled(boolean);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setShortcut(boolean, char);
+    method public void setTitle(CharSequence!);
+    method public boolean showsIcon();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ListMenuPresenter implements android.widget.AdapterView.OnItemClickListener androidx.appcompat.view.menu.MenuPresenter {
+    ctor public ListMenuPresenter(android.content.Context!, int);
+    ctor public ListMenuPresenter(int, int);
+    method public boolean collapseItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+    method public boolean expandItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+    method public boolean flagActionItems();
+    method public android.widget.ListAdapter! getAdapter();
+    method public int getId();
+    method public androidx.appcompat.view.menu.MenuView! getMenuView(android.view.ViewGroup!);
+    method public void initForMenu(android.content.Context!, androidx.appcompat.view.menu.MenuBuilder!);
+    method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder!, boolean);
+    method public void onItemClick(android.widget.AdapterView<?>!, android.view.View!, int, long);
+    method public void onRestoreInstanceState(android.os.Parcelable!);
+    method public android.os.Parcelable! onSaveInstanceState();
+    method public boolean onSubMenuSelected(androidx.appcompat.view.menu.SubMenuBuilder!);
+    method public void restoreHierarchyState(android.os.Bundle!);
+    method public void saveHierarchyState(android.os.Bundle!);
+    method public void setCallback(androidx.appcompat.view.menu.MenuPresenter.Callback!);
+    method public void setId(int);
+    method public void setItemIndexOffset(int);
+    method public void updateMenuView(boolean);
+    field public static final String VIEWS_TAG = "android:menu:list";
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuAdapter extends android.widget.BaseAdapter {
+    ctor public MenuAdapter(androidx.appcompat.view.menu.MenuBuilder!, android.view.LayoutInflater!, boolean, int);
+    method public androidx.appcompat.view.menu.MenuBuilder! getAdapterMenu();
+    method public int getCount();
+    method public boolean getForceShowIcon();
+    method public androidx.appcompat.view.menu.MenuItemImpl! getItem(int);
+    method public long getItemId(int);
+    method public android.view.View! getView(int, android.view.View!, android.view.ViewGroup!);
+    method public void setForceShowIcon(boolean);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuBuilder implements androidx.core.internal.view.SupportMenu {
+    ctor public MenuBuilder(android.content.Context!);
+    method public android.view.MenuItem! add(CharSequence!);
+    method public android.view.MenuItem! add(int);
+    method public android.view.MenuItem! add(int, int, int, CharSequence!);
+    method public android.view.MenuItem! add(int, int, int, int);
+    method public int addIntentOptions(int, int, int, android.content.ComponentName!, android.content.Intent![]!, android.content.Intent!, int, android.view.MenuItem![]!);
+    method protected android.view.MenuItem! addInternal(int, int, int, CharSequence!);
+    method public void addMenuPresenter(androidx.appcompat.view.menu.MenuPresenter!);
+    method public void addMenuPresenter(androidx.appcompat.view.menu.MenuPresenter!, android.content.Context!);
+    method public android.view.SubMenu! addSubMenu(CharSequence!);
+    method public android.view.SubMenu! addSubMenu(int);
+    method public android.view.SubMenu! addSubMenu(int, int, int, CharSequence!);
+    method public android.view.SubMenu! addSubMenu(int, int, int, int);
+    method public void changeMenuMode();
+    method public void clear();
+    method public void clearAll();
+    method public void clearHeader();
+    method public final void close(boolean);
+    method public void close();
+    method public boolean collapseItemActionView(androidx.appcompat.view.menu.MenuItemImpl!);
+    method public boolean expandItemActionView(androidx.appcompat.view.menu.MenuItemImpl!);
+    method public int findGroupIndex(int);
+    method public int findGroupIndex(int, int);
+    method public android.view.MenuItem! findItem(int);
+    method public int findItemIndex(int);
+    method public void flagActionItems();
+    method public java.util.ArrayList<androidx.appcompat.view.menu.MenuItemImpl!>! getActionItems();
+    method protected String! getActionViewStatesKey();
+    method public android.content.Context! getContext();
+    method public androidx.appcompat.view.menu.MenuItemImpl! getExpandedItem();
+    method public android.graphics.drawable.Drawable! getHeaderIcon();
+    method public CharSequence! getHeaderTitle();
+    method public android.view.View! getHeaderView();
+    method public android.view.MenuItem! getItem(int);
+    method public java.util.ArrayList<androidx.appcompat.view.menu.MenuItemImpl!>! getNonActionItems();
+    method public androidx.appcompat.view.menu.MenuBuilder! getRootMenu();
+    method public java.util.ArrayList<androidx.appcompat.view.menu.MenuItemImpl!> getVisibleItems();
+    method public boolean hasVisibleItems();
+    method public boolean isGroupDividerEnabled();
+    method public boolean isShortcutKey(int, android.view.KeyEvent!);
+    method public boolean isShortcutsVisible();
+    method public void onItemsChanged(boolean);
+    method public boolean performIdentifierAction(int, int);
+    method public boolean performItemAction(android.view.MenuItem!, int);
+    method public boolean performItemAction(android.view.MenuItem!, androidx.appcompat.view.menu.MenuPresenter!, int);
+    method public boolean performShortcut(int, android.view.KeyEvent!, int);
+    method public void removeGroup(int);
+    method public void removeItem(int);
+    method public void removeItemAt(int);
+    method public void removeMenuPresenter(androidx.appcompat.view.menu.MenuPresenter!);
+    method public void restoreActionViewStates(android.os.Bundle!);
+    method public void restorePresenterStates(android.os.Bundle!);
+    method public void saveActionViewStates(android.os.Bundle!);
+    method public void savePresenterStates(android.os.Bundle!);
+    method public void setCallback(androidx.appcompat.view.menu.MenuBuilder.Callback!);
+    method public void setCurrentMenuInfo(android.view.ContextMenu.ContextMenuInfo!);
+    method public androidx.appcompat.view.menu.MenuBuilder! setDefaultShowAsAction(int);
+    method public void setGroupCheckable(int, boolean, boolean);
+    method public void setGroupEnabled(int, boolean);
+    method public void setGroupVisible(int, boolean);
+    method protected androidx.appcompat.view.menu.MenuBuilder! setHeaderIconInt(android.graphics.drawable.Drawable!);
+    method protected androidx.appcompat.view.menu.MenuBuilder! setHeaderIconInt(int);
+    method protected androidx.appcompat.view.menu.MenuBuilder! setHeaderTitleInt(CharSequence!);
+    method protected androidx.appcompat.view.menu.MenuBuilder! setHeaderTitleInt(int);
+    method protected androidx.appcompat.view.menu.MenuBuilder! setHeaderViewInt(android.view.View!);
+    method public void setOptionalIconsVisible(boolean);
+    method public void setOverrideVisibleItems(boolean);
+    method public void setQwertyMode(boolean);
+    method public void setShortcutsVisible(boolean);
+    method public int size();
+    method public void startDispatchingItemsChanged();
+    method public void stopDispatchingItemsChanged();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface MenuBuilder.Callback {
+    method public boolean onMenuItemSelected(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+    method public void onMenuModeChange(androidx.appcompat.view.menu.MenuBuilder);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface MenuBuilder.ItemInvoker {
+    method public boolean invokeItem(androidx.appcompat.view.menu.MenuItemImpl!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class MenuItemImpl implements androidx.core.internal.view.SupportMenuItem {
+    method public void actionFormatChanged();
+    method public boolean collapseActionView();
+    method public boolean expandActionView();
+    method public android.view.ActionProvider! getActionProvider();
+    method public android.view.View! getActionView();
+    method public char getAlphabeticShortcut();
+    method public int getGroupId();
+    method public android.graphics.drawable.Drawable! getIcon();
+    method public android.content.Intent! getIntent();
+    method public int getItemId();
+    method public android.view.ContextMenu.ContextMenuInfo! getMenuInfo();
+    method public char getNumericShortcut();
+    method public int getOrder();
+    method public int getOrdering();
+    method public android.view.SubMenu! getSubMenu();
+    method public androidx.core.view.ActionProvider! getSupportActionProvider();
+    method public CharSequence! getTitle();
+    method public CharSequence! getTitleCondensed();
+    method public boolean hasCollapsibleActionView();
+    method public boolean hasSubMenu();
+    method public boolean invoke();
+    method public boolean isActionButton();
+    method public boolean isActionViewExpanded();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isEnabled();
+    method public boolean isExclusiveCheckable();
+    method public boolean isVisible();
+    method public boolean requestsActionButton();
+    method public boolean requiresActionButton();
+    method public boolean requiresOverflow();
+    method public android.view.MenuItem! setActionProvider(android.view.ActionProvider!);
+    method public androidx.core.internal.view.SupportMenuItem! setActionView(android.view.View!);
+    method public androidx.core.internal.view.SupportMenuItem! setActionView(int);
+    method public void setActionViewExpanded(boolean);
+    method public android.view.MenuItem! setAlphabeticShortcut(char);
+    method public android.view.MenuItem! setCallback(Runnable!);
+    method public android.view.MenuItem! setCheckable(boolean);
+    method public android.view.MenuItem! setChecked(boolean);
+    method public androidx.core.internal.view.SupportMenuItem! setContentDescription(CharSequence!);
+    method public android.view.MenuItem! setEnabled(boolean);
+    method public void setExclusiveCheckable(boolean);
+    method public android.view.MenuItem! setIcon(android.graphics.drawable.Drawable!);
+    method public android.view.MenuItem! setIcon(int);
+    method public android.view.MenuItem! setIntent(android.content.Intent!);
+    method public void setIsActionButton(boolean);
+    method public android.view.MenuItem! setNumericShortcut(char);
+    method public android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem.OnActionExpandListener!);
+    method public android.view.MenuItem! setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener!);
+    method public android.view.MenuItem! setShortcut(char, char);
+    method public void setShowAsAction(int);
+    method public androidx.core.internal.view.SupportMenuItem! setShowAsActionFlags(int);
+    method public void setSubMenu(androidx.appcompat.view.menu.SubMenuBuilder!);
+    method public androidx.core.internal.view.SupportMenuItem! setSupportActionProvider(androidx.core.view.ActionProvider!);
+    method public android.view.MenuItem! setTitle(CharSequence!);
+    method public android.view.MenuItem! setTitle(int);
+    method public android.view.MenuItem! setTitleCondensed(CharSequence!);
+    method public androidx.core.internal.view.SupportMenuItem! setTooltipText(CharSequence!);
+    method public android.view.MenuItem! setVisible(boolean);
+    method public boolean shouldShowIcon();
+    method public boolean showsTextAsAction();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuItemWrapperICS implements android.view.MenuItem {
+    ctor public MenuItemWrapperICS(android.content.Context!, androidx.core.internal.view.SupportMenuItem!);
+    method public boolean collapseActionView();
+    method public boolean expandActionView();
+    method public android.view.ActionProvider! getActionProvider();
+    method public android.view.View! getActionView();
+    method public char getAlphabeticShortcut();
+    method public int getGroupId();
+    method public android.graphics.drawable.Drawable! getIcon();
+    method public android.content.Intent! getIntent();
+    method public int getItemId();
+    method public android.view.ContextMenu.ContextMenuInfo! getMenuInfo();
+    method public char getNumericShortcut();
+    method public int getOrder();
+    method public android.view.SubMenu! getSubMenu();
+    method public CharSequence! getTitle();
+    method public CharSequence! getTitleCondensed();
+    method public boolean hasSubMenu();
+    method public boolean isActionViewExpanded();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isEnabled();
+    method public boolean isVisible();
+    method public android.view.MenuItem! setActionProvider(android.view.ActionProvider!);
+    method public android.view.MenuItem! setActionView(android.view.View!);
+    method public android.view.MenuItem! setActionView(int);
+    method public android.view.MenuItem! setAlphabeticShortcut(char);
+    method public android.view.MenuItem! setCheckable(boolean);
+    method public android.view.MenuItem! setChecked(boolean);
+    method public android.view.MenuItem! setEnabled(boolean);
+    method public void setExclusiveCheckable(boolean);
+    method public android.view.MenuItem! setIcon(android.graphics.drawable.Drawable!);
+    method public android.view.MenuItem! setIcon(int);
+    method public android.view.MenuItem! setIntent(android.content.Intent!);
+    method public android.view.MenuItem! setNumericShortcut(char);
+    method public android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem.OnActionExpandListener!);
+    method public android.view.MenuItem! setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener!);
+    method public android.view.MenuItem! setShortcut(char, char);
+    method public void setShowAsAction(int);
+    method public android.view.MenuItem! setShowAsActionFlags(int);
+    method public android.view.MenuItem! setTitle(CharSequence!);
+    method public android.view.MenuItem! setTitle(int);
+    method public android.view.MenuItem! setTitleCondensed(CharSequence!);
+    method public android.view.MenuItem! setVisible(boolean);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuPopupHelper {
+    ctor public MenuPopupHelper(android.content.Context, androidx.appcompat.view.menu.MenuBuilder);
+    ctor public MenuPopupHelper(android.content.Context, androidx.appcompat.view.menu.MenuBuilder, android.view.View);
+    ctor public MenuPopupHelper(android.content.Context, androidx.appcompat.view.menu.MenuBuilder, android.view.View, boolean, @AttrRes int);
+    ctor public MenuPopupHelper(android.content.Context, androidx.appcompat.view.menu.MenuBuilder, android.view.View, boolean, @AttrRes int, @StyleRes int);
+    method public void dismiss();
+    method public int getGravity();
+    method public android.widget.ListView! getListView();
+    method public androidx.appcompat.view.menu.MenuPopup getPopup();
+    method public boolean isShowing();
+    method protected void onDismiss();
+    method public void setAnchorView(android.view.View);
+    method public void setForceShowIcon(boolean);
+    method public void setGravity(int);
+    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener?);
+    method public void setPresenterCallback(androidx.appcompat.view.menu.MenuPresenter.Callback?);
+    method public void show();
+    method public void show(int, int);
+    method public boolean tryShow();
+    method public boolean tryShow(int, int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface MenuPresenter {
+    method public boolean collapseItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+    method public boolean expandItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+    method public boolean flagActionItems();
+    method public int getId();
+    method public androidx.appcompat.view.menu.MenuView! getMenuView(android.view.ViewGroup!);
+    method public void initForMenu(android.content.Context!, androidx.appcompat.view.menu.MenuBuilder!);
+    method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder!, boolean);
+    method public void onRestoreInstanceState(android.os.Parcelable!);
+    method public android.os.Parcelable! onSaveInstanceState();
+    method public boolean onSubMenuSelected(androidx.appcompat.view.menu.SubMenuBuilder!);
+    method public void setCallback(androidx.appcompat.view.menu.MenuPresenter.Callback!);
+    method public void updateMenuView(boolean);
+  }
+
+  public static interface MenuPresenter.Callback {
+    method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder, boolean);
+    method public boolean onOpenSubMenu(androidx.appcompat.view.menu.MenuBuilder);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface MenuView {
+    method public int getWindowAnimations();
+    method public void initialize(androidx.appcompat.view.menu.MenuBuilder!);
+  }
+
+  public static interface MenuView.ItemView {
+    method public androidx.appcompat.view.menu.MenuItemImpl! getItemData();
+    method public void initialize(androidx.appcompat.view.menu.MenuItemImpl!, int);
+    method public boolean prefersCondensedTitle();
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setEnabled(boolean);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setShortcut(boolean, char);
+    method public void setTitle(CharSequence!);
+    method public boolean showsIcon();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuWrapperICS implements android.view.Menu {
+    ctor public MenuWrapperICS(android.content.Context!, androidx.core.internal.view.SupportMenu!);
+    method public android.view.MenuItem! add(CharSequence!);
+    method public android.view.MenuItem! add(int);
+    method public android.view.MenuItem! add(int, int, int, CharSequence!);
+    method public android.view.MenuItem! add(int, int, int, int);
+    method public int addIntentOptions(int, int, int, android.content.ComponentName!, android.content.Intent![]!, android.content.Intent!, int, android.view.MenuItem![]!);
+    method public android.view.SubMenu! addSubMenu(CharSequence!);
+    method public android.view.SubMenu! addSubMenu(int);
+    method public android.view.SubMenu! addSubMenu(int, int, int, CharSequence!);
+    method public android.view.SubMenu! addSubMenu(int, int, int, int);
+    method public void clear();
+    method public void close();
+    method public android.view.MenuItem! findItem(int);
+    method public android.view.MenuItem! getItem(int);
+    method public boolean hasVisibleItems();
+    method public boolean isShortcutKey(int, android.view.KeyEvent!);
+    method public boolean performIdentifierAction(int, int);
+    method public boolean performShortcut(int, android.view.KeyEvent!, int);
+    method public void removeGroup(int);
+    method public void removeItem(int);
+    method public void setGroupCheckable(int, boolean, boolean);
+    method public void setGroupEnabled(int, boolean);
+    method public void setGroupVisible(int, boolean);
+    method public void setQwertyMode(boolean);
+    method public int size();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface ShowableListMenu {
+    method public void dismiss();
+    method public android.widget.ListView! getListView();
+    method public boolean isShowing();
+    method public void show();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class SubMenuBuilder extends androidx.appcompat.view.menu.MenuBuilder implements android.view.SubMenu {
+    ctor public SubMenuBuilder(android.content.Context!, androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+    method public String! getActionViewStatesKey();
+    method public android.view.MenuItem! getItem();
+    method public android.view.Menu! getParentMenu();
+    method public boolean isQwertyMode();
+    method public android.view.SubMenu! setHeaderIcon(android.graphics.drawable.Drawable!);
+    method public android.view.SubMenu! setHeaderIcon(int);
+    method public android.view.SubMenu! setHeaderTitle(CharSequence!);
+    method public android.view.SubMenu! setHeaderTitle(int);
+    method public android.view.SubMenu! setHeaderView(android.view.View!);
+    method public android.view.SubMenu! setIcon(android.graphics.drawable.Drawable!);
+    method public android.view.SubMenu! setIcon(int);
+  }
+
+}
+
+package androidx.appcompat.widget {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionBarContainer extends android.widget.FrameLayout {
+    ctor public ActionBarContainer(android.content.Context!);
+    ctor public ActionBarContainer(android.content.Context!, android.util.AttributeSet!);
+    method public android.view.View! getTabContainer();
+    method public void onFinishInflate();
+    method public void onLayout(boolean, int, int, int, int);
+    method public void onMeasure(int, int);
+    method public void setPrimaryBackground(android.graphics.drawable.Drawable!);
+    method public void setSplitBackground(android.graphics.drawable.Drawable!);
+    method public void setStackedBackground(android.graphics.drawable.Drawable!);
+    method public void setTabContainer(androidx.appcompat.widget.ScrollingTabContainerView!);
+    method public void setTransitioning(boolean);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionBarContextView extends android.view.ViewGroup {
+    ctor public ActionBarContextView(android.content.Context);
+    ctor public ActionBarContextView(android.content.Context, android.util.AttributeSet?);
+    ctor public ActionBarContextView(android.content.Context, android.util.AttributeSet?, int);
+    method public void animateToVisibility(int);
+    method public boolean canShowOverflowMenu();
+    method public void closeMode();
+    method public void dismissPopupMenus();
+    method public int getAnimatedVisibility();
+    method public int getContentHeight();
+    method public CharSequence! getSubtitle();
+    method public CharSequence! getTitle();
+    method public boolean hideOverflowMenu();
+    method public void initForMode(androidx.appcompat.view.ActionMode!);
+    method public boolean isOverflowMenuShowPending();
+    method public boolean isOverflowMenuShowing();
+    method public boolean isOverflowReserved();
+    method public boolean isTitleOptional();
+    method public void killMode();
+    method public void onDetachedFromWindow();
+    method public void postShowOverflowMenu();
+    method public void setContentHeight(int);
+    method public void setCustomView(android.view.View!);
+    method public void setSubtitle(CharSequence!);
+    method public void setTitle(CharSequence!);
+    method public void setTitleOptional(boolean);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setupAnimatorToVisibility(int, long);
+    method public boolean showOverflowMenu();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionBarOverlayLayout extends android.view.ViewGroup implements androidx.appcompat.widget.DecorContentParent androidx.core.view.NestedScrollingParent androidx.core.view.NestedScrollingParent2 androidx.core.view.NestedScrollingParent3 {
+    ctor public ActionBarOverlayLayout(android.content.Context);
+    ctor public ActionBarOverlayLayout(android.content.Context, android.util.AttributeSet?);
+    method public boolean canShowOverflowMenu();
+    method public void dismissPopups();
+    method protected boolean fitSystemWindows(android.graphics.Rect!);
+    method protected androidx.appcompat.widget.ActionBarOverlayLayout.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.appcompat.widget.ActionBarOverlayLayout.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method public int getActionBarHideOffset();
+    method public CharSequence! getTitle();
+    method public boolean hasIcon();
+    method public boolean hasLogo();
+    method public boolean hideOverflowMenu();
+    method public void initFeature(int);
+    method public boolean isHideOnContentScrollEnabled();
+    method public boolean isInOverlayMode();
+    method public boolean isOverflowMenuShowPending();
+    method public boolean isOverflowMenuShowing();
+    method public void onNestedPreScroll(android.view.View!, int, int, int[]!, int);
+    method public void onNestedScroll(android.view.View!, int, int, int, int, int, int[]!);
+    method public void onNestedScroll(android.view.View!, int, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View!, android.view.View!, int, int);
+    method public boolean onStartNestedScroll(android.view.View!, android.view.View!, int, int);
+    method public void onStopNestedScroll(android.view.View!, int);
+    method public void restoreToolbarHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+    method public void saveToolbarHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+    method public void setActionBarHideOffset(int);
+    method public void setActionBarVisibilityCallback(androidx.appcompat.widget.ActionBarOverlayLayout.ActionBarVisibilityCallback!);
+    method public void setHasNonEmbeddedTabs(boolean);
+    method public void setHideOnContentScrollEnabled(boolean);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setLogo(int);
+    method public void setMenu(android.view.Menu!, androidx.appcompat.view.menu.MenuPresenter.Callback!);
+    method public void setMenuPrepared();
+    method public void setOverlayMode(boolean);
+    method public void setShowingForActionMode(boolean);
+    method public void setUiOptions(int);
+    method public void setWindowCallback(android.view.Window.Callback!);
+    method public void setWindowTitle(CharSequence!);
+    method public boolean showOverflowMenu();
+  }
+
+  public static interface ActionBarOverlayLayout.ActionBarVisibilityCallback {
+    method public void enableContentAnimations(boolean);
+    method public void hideForSystem();
+    method public void onContentScrollStarted();
+    method public void onContentScrollStopped();
+    method public void onWindowVisibilityChanged(int);
+    method public void showForSystem();
+  }
+
+  public static class ActionBarOverlayLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+    ctor public ActionBarOverlayLayout.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+    ctor public ActionBarOverlayLayout.LayoutParams(int, int);
+    ctor public ActionBarOverlayLayout.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    ctor public ActionBarOverlayLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+  }
+
+  public class ActionMenuView extends androidx.appcompat.widget.LinearLayoutCompat implements androidx.appcompat.view.menu.MenuBuilder.ItemInvoker androidx.appcompat.view.menu.MenuView {
+    ctor public ActionMenuView(android.content.Context);
+    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet?);
+    method public void dismissPopupMenus();
+    method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.widget.ActionMenuView.LayoutParams! generateOverflowButtonLayoutParams();
+    method public android.view.Menu! getMenu();
+    method public android.graphics.drawable.Drawable? getOverflowIcon();
+    method public int getPopupTheme();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int getWindowAnimations();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected boolean hasSupportDividerBeforeChildAt(int);
+    method public boolean hideOverflowMenu();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void initialize(androidx.appcompat.view.menu.MenuBuilder!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean invokeItem(androidx.appcompat.view.menu.MenuItemImpl!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isOverflowMenuShowPending();
+    method public boolean isOverflowMenuShowing();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isOverflowReserved();
+    method public void onConfigurationChanged(android.content.res.Configuration!);
+    method public void onDetachedFromWindow();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.view.menu.MenuBuilder! peekMenu();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setExpandedActionViewsExclusive(boolean);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setMenuCallbacks(androidx.appcompat.view.menu.MenuPresenter.Callback!, androidx.appcompat.view.menu.MenuBuilder.Callback!);
+    method public void setOnMenuItemClickListener(androidx.appcompat.widget.ActionMenuView.OnMenuItemClickListener!);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setOverflowReserved(boolean);
+    method public void setPopupTheme(@StyleRes int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setPresenter(androidx.appcompat.widget.ActionMenuPresenter!);
+    method public boolean showOverflowMenu();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface ActionMenuView.ActionMenuChildView {
+    method public boolean needsDividerAfter();
+    method public boolean needsDividerBefore();
+  }
+
+  public static class ActionMenuView.LayoutParams extends androidx.appcompat.widget.LinearLayoutCompat.LayoutParams {
+    ctor public ActionMenuView.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+    ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    ctor public ActionMenuView.LayoutParams(androidx.appcompat.widget.ActionMenuView.LayoutParams!);
+    ctor public ActionMenuView.LayoutParams(int, int);
+    field public int cellsUsed;
+    field public boolean expandable;
+    field public int extraPixels;
+    field public boolean isOverflowButton;
+    field public boolean preventEdgeOffset;
+  }
+
+  public static interface ActionMenuView.OnMenuItemClickListener {
+    method public boolean onMenuItemClick(android.view.MenuItem!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActivityChooserView extends android.view.ViewGroup {
+    ctor public ActivityChooserView(android.content.Context);
+    ctor public ActivityChooserView(android.content.Context, android.util.AttributeSet?);
+    ctor public ActivityChooserView(android.content.Context, android.util.AttributeSet?, int);
+    method public boolean dismissPopup();
+    method public androidx.appcompat.widget.ActivityChooserModel! getDataModel();
+    method public boolean isShowingPopup();
+    method public void setActivityChooserModel(androidx.appcompat.widget.ActivityChooserModel!);
+    method public void setDefaultActionButtonContentDescription(int);
+    method public void setExpandActivityOverflowButtonContentDescription(int);
+    method public void setExpandActivityOverflowButtonDrawable(android.graphics.drawable.Drawable!);
+    method public void setInitialActivityCount(int);
+    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setProvider(androidx.core.view.ActionProvider!);
+    method public boolean showPopup();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ActivityChooserView.InnerLayout extends android.widget.LinearLayout {
+    ctor public ActivityChooserView.InnerLayout(android.content.Context!, android.util.AttributeSet!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AlertDialogLayout extends androidx.appcompat.widget.LinearLayoutCompat {
+    ctor public AlertDialogLayout(android.content.Context?);
+    ctor public AlertDialogLayout(android.content.Context?, android.util.AttributeSet?);
+  }
+
+  public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatButton extends android.widget.Button implements androidx.core.widget.AutoSizeableTextView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+    ctor public AppCompatButton(android.content.Context);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public void setSupportAllCaps(boolean);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatCheckBox extends android.widget.CheckBox implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton {
+    ctor public AppCompatCheckBox(android.content.Context);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
+    ctor public AppCompatCheckedTextView(android.content.Context);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?, int);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class AppCompatDrawableManager {
+    ctor public AppCompatDrawableManager();
+    method public static androidx.appcompat.widget.AppCompatDrawableManager! get();
+    method public android.graphics.drawable.Drawable! getDrawable(android.content.Context, @DrawableRes int);
+    method public static android.graphics.PorterDuffColorFilter! getPorterDuffColorFilter(int, android.graphics.PorterDuff.Mode!);
+    method public void onConfigurationChanged(android.content.Context);
+    method public static void preload();
+  }
+
+  public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.OnReceiveContentViewBehavior androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatEditText(android.content.Context);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatImageButton extends android.widget.ImageButton implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableImageSourceView {
+    ctor public AppCompatImageButton(android.content.Context);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportImageTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportImageTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AppCompatImageHelper {
+    ctor public AppCompatImageHelper(android.widget.ImageView);
+    method public void loadFromAttributes(android.util.AttributeSet!, int);
+    method public void setImageResource(int);
+  }
+
+  public class AppCompatImageView extends android.widget.ImageView implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableImageSourceView {
+    ctor public AppCompatImageView(android.content.Context);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportImageTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportImageTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+  }
+
+  public class AppCompatRadioButton extends android.widget.RadioButton implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton {
+    ctor public AppCompatRadioButton(android.content.Context!);
+    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?);
+    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatRatingBar extends android.widget.RatingBar {
+    ctor public AppCompatRatingBar(android.content.Context);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class AppCompatSeekBar extends android.widget.SeekBar {
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class AppCompatSpinner extends android.widget.Spinner implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatSpinner(android.content.Context);
+    ctor public AppCompatSpinner(android.content.Context, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int, android.content.res.Resources.Theme!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public class AppCompatTextView extends android.widget.TextView implements androidx.core.widget.AutoSizeableTextView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+    ctor public AppCompatTextView(android.content.Context);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method public androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParamsCompat();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public void setPrecomputedText(androidx.core.text.PrecomputedTextCompat);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTextAppearance(android.content.Context!, int);
+    method public void setTextFuture(java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>?);
+    method public void setTextMetricsParamsCompat(androidx.core.text.PrecomputedTextCompat.Params);
+  }
+
+  public class AppCompatToggleButton extends android.widget.ToggleButton implements androidx.core.view.TintableBackgroundView {
+    ctor public AppCompatToggleButton(android.content.Context);
+    ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ButtonBarLayout extends android.widget.LinearLayout {
+    ctor public ButtonBarLayout(android.content.Context, android.util.AttributeSet?);
+    method public void setAllowStacking(boolean);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface DecorContentParent {
+    method public boolean canShowOverflowMenu();
+    method public void dismissPopups();
+    method public CharSequence! getTitle();
+    method public boolean hasIcon();
+    method public boolean hasLogo();
+    method public boolean hideOverflowMenu();
+    method public void initFeature(int);
+    method public boolean isOverflowMenuShowPending();
+    method public boolean isOverflowMenuShowing();
+    method public void restoreToolbarHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+    method public void saveToolbarHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setLogo(int);
+    method public void setMenu(android.view.Menu!, androidx.appcompat.view.menu.MenuPresenter.Callback!);
+    method public void setMenuPrepared();
+    method public void setUiOptions(int);
+    method public void setWindowCallback(android.view.Window.Callback!);
+    method public void setWindowTitle(CharSequence!);
+    method public boolean showOverflowMenu();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface DecorToolbar {
+    method public void animateToVisibility(int);
+    method public boolean canShowOverflowMenu();
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
+    method public android.content.Context! getContext();
+    method public android.view.View! getCustomView();
+    method public int getDisplayOptions();
+    method public int getDropdownItemCount();
+    method public int getDropdownSelectedPosition();
+    method public int getHeight();
+    method public android.view.Menu! getMenu();
+    method public int getNavigationMode();
+    method public CharSequence! getSubtitle();
+    method public CharSequence! getTitle();
+    method public android.view.ViewGroup! getViewGroup();
+    method public int getVisibility();
+    method public boolean hasEmbeddedTabs();
+    method public boolean hasExpandedActionView();
+    method public boolean hasIcon();
+    method public boolean hasLogo();
+    method public boolean hideOverflowMenu();
+    method public void initIndeterminateProgress();
+    method public void initProgress();
+    method public boolean isOverflowMenuShowPending();
+    method public boolean isOverflowMenuShowing();
+    method public boolean isTitleTruncated();
+    method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+    method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
+    method public void setCollapsible(boolean);
+    method public void setCustomView(android.view.View!);
+    method public void setDefaultNavigationContentDescription(int);
+    method public void setDefaultNavigationIcon(android.graphics.drawable.Drawable!);
+    method public void setDisplayOptions(int);
+    method public void setDropdownParams(android.widget.SpinnerAdapter!, android.widget.AdapterView.OnItemSelectedListener!);
+    method public void setDropdownSelectedPosition(int);
+    method public void setEmbeddedTabView(androidx.appcompat.widget.ScrollingTabContainerView!);
+    method public void setHomeButtonEnabled(boolean);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setLogo(int);
+    method public void setLogo(android.graphics.drawable.Drawable!);
+    method public void setMenu(android.view.Menu!, androidx.appcompat.view.menu.MenuPresenter.Callback!);
+    method public void setMenuCallbacks(androidx.appcompat.view.menu.MenuPresenter.Callback!, androidx.appcompat.view.menu.MenuBuilder.Callback!);
+    method public void setMenuPrepared();
+    method public void setNavigationContentDescription(CharSequence!);
+    method public void setNavigationContentDescription(int);
+    method public void setNavigationIcon(android.graphics.drawable.Drawable!);
+    method public void setNavigationIcon(int);
+    method public void setNavigationMode(int);
+    method public void setSubtitle(CharSequence!);
+    method public void setTitle(CharSequence!);
+    method public void setVisibility(int);
+    method public void setWindowCallback(android.view.Window.Callback!);
+    method public void setWindowTitle(CharSequence!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setupAnimatorToVisibility(int, long);
+    method public boolean showOverflowMenu();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DialogTitle extends androidx.appcompat.widget.AppCompatTextView {
+    ctor public DialogTitle(android.content.Context, android.util.AttributeSet?, int);
+    ctor public DialogTitle(android.content.Context, android.util.AttributeSet?);
+    ctor public DialogTitle(android.content.Context);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FitWindowsFrameLayout extends android.widget.FrameLayout implements androidx.appcompat.widget.FitWindowsViewGroup {
+    ctor public FitWindowsFrameLayout(android.content.Context);
+    ctor public FitWindowsFrameLayout(android.content.Context, android.util.AttributeSet?);
+    method protected boolean fitSystemWindows(android.graphics.Rect!);
+    method public void setOnFitSystemWindowsListener(androidx.appcompat.widget.FitWindowsViewGroup.OnFitSystemWindowsListener!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FitWindowsLinearLayout extends android.widget.LinearLayout implements androidx.appcompat.widget.FitWindowsViewGroup {
+    ctor public FitWindowsLinearLayout(android.content.Context);
+    ctor public FitWindowsLinearLayout(android.content.Context, android.util.AttributeSet?);
+    method protected boolean fitSystemWindows(android.graphics.Rect!);
+    method public void setOnFitSystemWindowsListener(androidx.appcompat.widget.FitWindowsViewGroup.OnFitSystemWindowsListener!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface FitWindowsViewGroup {
+    method public void setOnFitSystemWindowsListener(androidx.appcompat.widget.FitWindowsViewGroup.OnFitSystemWindowsListener!);
+  }
+
+  public static interface FitWindowsViewGroup.OnFitSystemWindowsListener {
+    method public void onFitSystemWindows(android.graphics.Rect!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class ForwardingListener implements android.view.View.OnAttachStateChangeListener android.view.View.OnTouchListener {
+    ctor public ForwardingListener(android.view.View!);
+    method public abstract androidx.appcompat.view.menu.ShowableListMenu! getPopup();
+    method protected boolean onForwardingStarted();
+    method protected boolean onForwardingStopped();
+    method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+    method public void onViewAttachedToWindow(android.view.View!);
+    method public void onViewDetachedFromWindow(android.view.View!);
+  }
+
+  public class LinearLayoutCompat extends android.view.ViewGroup {
+    ctor public LinearLayoutCompat(android.content.Context);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?, int);
+    method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public int getBaselineAlignedChildIndex();
+    method public android.graphics.drawable.Drawable! getDividerDrawable();
+    method public int getDividerPadding();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int getDividerWidth();
+    method public int getGravity();
+    method @androidx.appcompat.widget.LinearLayoutCompat.OrientationMode public int getOrientation();
+    method @androidx.appcompat.widget.LinearLayoutCompat.DividerMode public int getShowDividers();
+    method public float getWeightSum();
+    method public boolean isBaselineAligned();
+    method public boolean isMeasureWithLargestChildEnabled();
+    method public void setBaselineAligned(boolean);
+    method public void setBaselineAlignedChildIndex(int);
+    method public void setDividerDrawable(android.graphics.drawable.Drawable!);
+    method public void setDividerPadding(int);
+    method public void setGravity(int);
+    method public void setHorizontalGravity(int);
+    method public void setMeasureWithLargestChildEnabled(boolean);
+    method public void setOrientation(@androidx.appcompat.widget.LinearLayoutCompat.OrientationMode int);
+    method public void setShowDividers(@androidx.appcompat.widget.LinearLayoutCompat.DividerMode int);
+    method public void setVerticalGravity(int);
+    method public void setWeightSum(float);
+    field public static final int HORIZONTAL = 0; // 0x0
+    field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
+    field public static final int SHOW_DIVIDER_END = 4; // 0x4
+    field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
+    field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
+    field public static final int VERTICAL = 1; // 0x1
+  }
+
+  @IntDef(flag=true, value={androidx.appcompat.widget.LinearLayoutCompat.SHOW_DIVIDER_NONE, androidx.appcompat.widget.LinearLayoutCompat.SHOW_DIVIDER_BEGINNING, androidx.appcompat.widget.LinearLayoutCompat.SHOW_DIVIDER_MIDDLE, androidx.appcompat.widget.LinearLayoutCompat.SHOW_DIVIDER_END}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface LinearLayoutCompat.DividerMode {
+  }
+
+  public static class LinearLayoutCompat.LayoutParams extends android.widget.LinearLayout.LayoutParams {
+    ctor public LinearLayoutCompat.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+    ctor public LinearLayoutCompat.LayoutParams(int, int);
+    ctor public LinearLayoutCompat.LayoutParams(int, int, float);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams!);
+    ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+  }
+
+  @IntDef({androidx.appcompat.widget.LinearLayoutCompat.HORIZONTAL, androidx.appcompat.widget.LinearLayoutCompat.VERTICAL}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface LinearLayoutCompat.OrientationMode {
+  }
+
+  public class ListPopupWindow implements androidx.appcompat.view.menu.ShowableListMenu {
+    ctor public ListPopupWindow(android.content.Context);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?, @AttrRes int);
+    ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?, @AttrRes int, @StyleRes int);
+    method public void clearListSelection();
+    method public android.view.View.OnTouchListener! createDragToOpenListener(android.view.View!);
+    method public void dismiss();
+    method public android.view.View? getAnchorView();
+    method @StyleRes public int getAnimationStyle();
+    method public android.graphics.drawable.Drawable? getBackground();
+    method public android.graphics.Rect? getEpicenterBounds();
+    method public int getHeight();
+    method public int getHorizontalOffset();
+    method public int getInputMethodMode();
+    method public android.widget.ListView? getListView();
+    method public int getPromptPosition();
+    method public Object? getSelectedItem();
+    method public long getSelectedItemId();
+    method public int getSelectedItemPosition();
+    method public android.view.View? getSelectedView();
+    method public int getSoftInputMode();
+    method public int getVerticalOffset();
+    method public int getWidth();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isDropDownAlwaysVisible();
+    method public boolean isInputMethodNotNeeded();
+    method public boolean isModal();
+    method public boolean isShowing();
+    method public boolean onKeyDown(int, android.view.KeyEvent);
+    method public boolean onKeyPreIme(int, android.view.KeyEvent);
+    method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public boolean performItemClick(int);
+    method public void postShow();
+    method public void setAdapter(android.widget.ListAdapter?);
+    method public void setAnchorView(android.view.View?);
+    method public void setAnimationStyle(@StyleRes int);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+    method public void setContentWidth(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setDropDownAlwaysVisible(boolean);
+    method public void setDropDownGravity(int);
+    method public void setEpicenterBounds(android.graphics.Rect?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setForceIgnoreOutsideTouch(boolean);
+    method public void setHeight(int);
+    method public void setHorizontalOffset(int);
+    method public void setInputMethodMode(int);
+    method public void setListSelector(android.graphics.drawable.Drawable!);
+    method public void setModal(boolean);
+    method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener?);
+    method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener?);
+    method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setOverlapAnchor(boolean);
+    method public void setPromptPosition(int);
+    method public void setPromptView(android.view.View?);
+    method public void setSelection(int);
+    method public void setSoftInputMode(int);
+    method public void setVerticalOffset(int);
+    method public void setWidth(int);
+    method public void setWindowLayoutType(int);
+    method public void show();
+    field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
+    field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
+    field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
+    field public static final int MATCH_PARENT = -1; // 0xffffffff
+    field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
+    field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
+    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface MenuItemHoverListener {
+    method public void onItemHoverEnter(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+    method public void onItemHoverExit(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuPopupWindow extends androidx.appcompat.widget.ListPopupWindow implements androidx.appcompat.widget.MenuItemHoverListener {
+    ctor public MenuPopupWindow(android.content.Context, android.util.AttributeSet?, int, int);
+    method public void onItemHoverEnter(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+    method public void onItemHoverExit(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+    method public void setEnterTransition(Object!);
+    method public void setExitTransition(Object!);
+    method public void setHoverListener(androidx.appcompat.widget.MenuItemHoverListener!);
+    method public void setTouchModal(boolean);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class MenuPopupWindow.MenuDropDownListView extends android.widget.ListView {
+    ctor public MenuPopupWindow.MenuDropDownListView(android.content.Context!, boolean);
+    method public void clearSelection();
+    method public int lookForSelectablePosition(int, boolean);
+    method public int measureHeightOfChildrenCompat(int, int, int, int, int);
+    method public boolean onForwardedEvent(android.view.MotionEvent!, int);
+    method public void setHoverListener(androidx.appcompat.widget.MenuItemHoverListener!);
+    field public static final int INVALID_POSITION = -1; // 0xffffffff
+    field public static final int NO_POSITION = -1; // 0xffffffff
+  }
+
+  public class PopupMenu {
+    ctor public PopupMenu(android.content.Context, android.view.View);
+    ctor public PopupMenu(android.content.Context, android.view.View, int);
+    ctor public PopupMenu(android.content.Context, android.view.View, int, @AttrRes int, @StyleRes int);
+    method public void dismiss();
+    method public android.view.View.OnTouchListener getDragToOpenListener();
+    method public int getGravity();
+    method public android.view.Menu getMenu();
+    method public android.view.MenuInflater getMenuInflater();
+    method public void inflate(@MenuRes int);
+    method public void setGravity(int);
+    method public void setOnDismissListener(androidx.appcompat.widget.PopupMenu.OnDismissListener?);
+    method public void setOnMenuItemClickListener(androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener?);
+    method public void show();
+  }
+
+  public static interface PopupMenu.OnDismissListener {
+    method public void onDismiss(androidx.appcompat.widget.PopupMenu!);
+  }
+
+  public static interface PopupMenu.OnMenuItemClickListener {
+    method public boolean onMenuItemClick(android.view.MenuItem!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ScrollingTabContainerView extends android.widget.HorizontalScrollView implements android.widget.AdapterView.OnItemSelectedListener {
+    ctor public ScrollingTabContainerView(android.content.Context);
+    method public void addTab(androidx.appcompat.app.ActionBar.Tab!, boolean);
+    method public void addTab(androidx.appcompat.app.ActionBar.Tab!, int, boolean);
+    method public void animateToTab(int);
+    method public void animateToVisibility(int);
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void onItemSelected(android.widget.AdapterView<?>!, android.view.View!, int, long);
+    method public void onMeasure(int, int);
+    method public void onNothingSelected(android.widget.AdapterView<?>!);
+    method public void removeAllTabs();
+    method public void removeTabAt(int);
+    method public void setAllowCollapse(boolean);
+    method public void setContentHeight(int);
+    method public void setTabSelected(int);
+    method public void updateTab(int);
+    field protected final androidx.appcompat.widget.ScrollingTabContainerView.VisibilityAnimListener! mVisAnimListener;
+    field protected android.view.ViewPropertyAnimator! mVisibilityAnim;
+  }
+
+  protected class ScrollingTabContainerView.VisibilityAnimListener extends android.animation.AnimatorListenerAdapter {
+    ctor protected ScrollingTabContainerView.VisibilityAnimListener();
+    method public androidx.appcompat.widget.ScrollingTabContainerView.VisibilityAnimListener! withFinalVisibility(android.view.ViewPropertyAnimator!, int);
+  }
+
+  public class SearchView extends androidx.appcompat.widget.LinearLayoutCompat implements androidx.appcompat.view.CollapsibleActionView {
+    ctor public SearchView(android.content.Context);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet?);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet?, int);
+    method public int getImeOptions();
+    method public int getInputType();
+    method public int getMaxWidth();
+    method public CharSequence! getQuery();
+    method public CharSequence? getQueryHint();
+    method public androidx.cursoradapter.widget.CursorAdapter! getSuggestionsAdapter();
+    method public boolean isIconfiedByDefault();
+    method public boolean isIconified();
+    method public boolean isQueryRefinementEnabled();
+    method public boolean isSubmitButtonEnabled();
+    method public void onActionViewCollapsed();
+    method public void onActionViewExpanded();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAppSearchData(android.os.Bundle!);
+    method public void setIconified(boolean);
+    method public void setIconifiedByDefault(boolean);
+    method public void setImeOptions(int);
+    method public void setInputType(int);
+    method public void setMaxWidth(int);
+    method public void setOnCloseListener(androidx.appcompat.widget.SearchView.OnCloseListener!);
+    method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener!);
+    method public void setOnQueryTextListener(androidx.appcompat.widget.SearchView.OnQueryTextListener!);
+    method public void setOnSearchClickListener(android.view.View.OnClickListener!);
+    method public void setOnSuggestionListener(androidx.appcompat.widget.SearchView.OnSuggestionListener!);
+    method public void setQuery(CharSequence!, boolean);
+    method public void setQueryHint(CharSequence?);
+    method public void setQueryRefinementEnabled(boolean);
+    method public void setSearchableInfo(android.app.SearchableInfo!);
+    method public void setSubmitButtonEnabled(boolean);
+    method public void setSuggestionsAdapter(androidx.cursoradapter.widget.CursorAdapter!);
+  }
+
+  public static interface SearchView.OnCloseListener {
+    method public boolean onClose();
+  }
+
+  public static interface SearchView.OnQueryTextListener {
+    method public boolean onQueryTextChange(String!);
+    method public boolean onQueryTextSubmit(String!);
+  }
+
+  public static interface SearchView.OnSuggestionListener {
+    method public boolean onSuggestionClick(int);
+    method public boolean onSuggestionSelect(int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class SearchView.SearchAutoComplete extends androidx.appcompat.widget.AppCompatAutoCompleteTextView {
+    ctor public SearchView.SearchAutoComplete(android.content.Context!);
+    ctor public SearchView.SearchAutoComplete(android.content.Context!, android.util.AttributeSet!);
+    ctor public SearchView.SearchAutoComplete(android.content.Context!, android.util.AttributeSet!, int);
+  }
+
+  public class ShareActionProvider extends androidx.core.view.ActionProvider {
+    ctor public ShareActionProvider(android.content.Context!);
+    method public android.view.View! onCreateActionView();
+    method public void setOnShareTargetSelectedListener(androidx.appcompat.widget.ShareActionProvider.OnShareTargetSelectedListener!);
+    method public void setShareHistoryFileName(String!);
+    method public void setShareIntent(android.content.Intent!);
+    field public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+  }
+
+  public static interface ShareActionProvider.OnShareTargetSelectedListener {
+    method public boolean onShareTargetSelected(androidx.appcompat.widget.ShareActionProvider!, android.content.Intent!);
+  }
+
+  public class SwitchCompat extends android.widget.CompoundButton {
+    ctor public SwitchCompat(android.content.Context);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?, int);
+    method public boolean getShowText();
+    method public boolean getSplitTrack();
+    method public int getSwitchMinWidth();
+    method public int getSwitchPadding();
+    method public CharSequence! getTextOff();
+    method public CharSequence! getTextOn();
+    method public android.graphics.drawable.Drawable! getThumbDrawable();
+    method public int getThumbTextPadding();
+    method public android.content.res.ColorStateList? getThumbTintList();
+    method public android.graphics.PorterDuff.Mode? getThumbTintMode();
+    method public android.graphics.drawable.Drawable! getTrackDrawable();
+    method public android.content.res.ColorStateList? getTrackTintList();
+    method public android.graphics.PorterDuff.Mode? getTrackTintMode();
+    method public void onMeasure(int, int);
+    method public void setShowText(boolean);
+    method public void setSplitTrack(boolean);
+    method public void setSwitchMinWidth(int);
+    method public void setSwitchPadding(int);
+    method public void setSwitchTextAppearance(android.content.Context!, int);
+    method public void setSwitchTypeface(android.graphics.Typeface!, int);
+    method public void setSwitchTypeface(android.graphics.Typeface!);
+    method public void setTextOff(CharSequence!);
+    method public void setTextOn(CharSequence!);
+    method public void setThumbDrawable(android.graphics.drawable.Drawable!);
+    method public void setThumbResource(int);
+    method public void setThumbTextPadding(int);
+    method public void setThumbTintList(android.content.res.ColorStateList?);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode?);
+    method public void setTrackDrawable(android.graphics.drawable.Drawable!);
+    method public void setTrackResource(int);
+    method public void setTrackTintList(android.content.res.ColorStateList?);
+    method public void setTrackTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
+    method public android.content.res.Resources.Theme? getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme?);
+  }
+
+  public static final class ThemedSpinnerAdapter.Helper {
+    ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
+    method public android.view.LayoutInflater getDropDownViewInflater();
+    method public android.content.res.Resources.Theme? getDropDownViewTheme();
+    method public void setDropDownViewTheme(android.content.res.Resources.Theme?);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TintTypedArray {
+    method public boolean getBoolean(int, boolean);
+    method @RequiresApi(21) public int getChangingConfigurations();
+    method public int getColor(int, int);
+    method public android.content.res.ColorStateList! getColorStateList(int);
+    method public float getDimension(int, float);
+    method public int getDimensionPixelOffset(int, int);
+    method public int getDimensionPixelSize(int, int);
+    method public android.graphics.drawable.Drawable! getDrawable(int);
+    method public android.graphics.drawable.Drawable! getDrawableIfKnown(int);
+    method public float getFloat(int, float);
+    method public android.graphics.Typeface? getFont(@StyleableRes int, int, androidx.core.content.res.ResourcesCompat.FontCallback?);
+    method public float getFraction(int, int, int, float);
+    method public int getIndex(int);
+    method public int getIndexCount();
+    method public int getInt(int, int);
+    method public int getInteger(int, int);
+    method public int getLayoutDimension(int, String!);
+    method public int getLayoutDimension(int, int);
+    method public String! getNonResourceString(int);
+    method public String! getPositionDescription();
+    method public int getResourceId(int, int);
+    method public android.content.res.Resources! getResources();
+    method public String! getString(int);
+    method public CharSequence! getText(int);
+    method public CharSequence![]! getTextArray(int);
+    method public int getType(int);
+    method public boolean getValue(int, android.util.TypedValue!);
+    method public android.content.res.TypedArray! getWrappedTypeArray();
+    method public boolean hasValue(int);
+    method public int length();
+    method public static androidx.appcompat.widget.TintTypedArray! obtainStyledAttributes(android.content.Context!, android.util.AttributeSet!, int[]!);
+    method public static androidx.appcompat.widget.TintTypedArray! obtainStyledAttributes(android.content.Context!, android.util.AttributeSet!, int[]!, int, int);
+    method public static androidx.appcompat.widget.TintTypedArray! obtainStyledAttributes(android.content.Context!, int, int[]!);
+    method public android.util.TypedValue! peekValue(int);
+    method public void recycle();
+  }
+
+  public class Toolbar extends android.view.ViewGroup {
+    ctor public Toolbar(android.content.Context);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet?);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean canShowOverflowMenu();
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
+    method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateDefaultLayoutParams();
+    method public androidx.appcompat.widget.Toolbar.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+    method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+    method public CharSequence? getCollapseContentDescription();
+    method public android.graphics.drawable.Drawable? getCollapseIcon();
+    method public int getContentInsetEnd();
+    method public int getContentInsetEndWithActions();
+    method public int getContentInsetLeft();
+    method public int getContentInsetRight();
+    method public int getContentInsetStart();
+    method public int getContentInsetStartWithNavigation();
+    method public int getCurrentContentInsetEnd();
+    method public int getCurrentContentInsetLeft();
+    method public int getCurrentContentInsetRight();
+    method public int getCurrentContentInsetStart();
+    method public android.graphics.drawable.Drawable! getLogo();
+    method public CharSequence! getLogoDescription();
+    method public android.view.Menu! getMenu();
+    method public CharSequence? getNavigationContentDescription();
+    method public android.graphics.drawable.Drawable? getNavigationIcon();
+    method public android.graphics.drawable.Drawable? getOverflowIcon();
+    method public int getPopupTheme();
+    method public CharSequence! getSubtitle();
+    method public CharSequence! getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.widget.DecorToolbar! getWrapper();
+    method public boolean hasExpandedActionView();
+    method public boolean hideOverflowMenu();
+    method public void inflateMenu(@MenuRes int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isOverflowMenuShowPending();
+    method public boolean isOverflowMenuShowing();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isTitleTruncated();
+    method public void setCollapseContentDescription(@StringRes int);
+    method public void setCollapseContentDescription(CharSequence?);
+    method public void setCollapseIcon(@DrawableRes int);
+    method public void setCollapseIcon(android.graphics.drawable.Drawable?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setCollapsible(boolean);
+    method public void setContentInsetEndWithActions(int);
+    method public void setContentInsetStartWithNavigation(int);
+    method public void setContentInsetsAbsolute(int, int);
+    method public void setContentInsetsRelative(int, int);
+    method public void setLogo(@DrawableRes int);
+    method public void setLogo(android.graphics.drawable.Drawable!);
+    method public void setLogoDescription(@StringRes int);
+    method public void setLogoDescription(CharSequence!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setMenu(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.widget.ActionMenuPresenter!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setMenuCallbacks(androidx.appcompat.view.menu.MenuPresenter.Callback!, androidx.appcompat.view.menu.MenuBuilder.Callback!);
+    method public void setNavigationContentDescription(@StringRes int);
+    method public void setNavigationContentDescription(CharSequence?);
+    method public void setNavigationIcon(@DrawableRes int);
+    method public void setNavigationIcon(android.graphics.drawable.Drawable?);
+    method public void setNavigationOnClickListener(android.view.View.OnClickListener!);
+    method public void setOnMenuItemClickListener(androidx.appcompat.widget.Toolbar.OnMenuItemClickListener!);
+    method public void setOverflowIcon(android.graphics.drawable.Drawable?);
+    method public void setPopupTheme(@StyleRes int);
+    method public void setSubtitle(@StringRes int);
+    method public void setSubtitle(CharSequence!);
+    method public void setSubtitleTextAppearance(android.content.Context!, @StyleRes int);
+    method public void setSubtitleTextColor(@ColorInt int);
+    method public void setSubtitleTextColor(android.content.res.ColorStateList);
+    method public void setTitle(@StringRes int);
+    method public void setTitle(CharSequence!);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
+    method public void setTitleTextAppearance(android.content.Context!, @StyleRes int);
+    method public void setTitleTextColor(@ColorInt int);
+    method public void setTitleTextColor(android.content.res.ColorStateList);
+    method public boolean showOverflowMenu();
+  }
+
+  public static class Toolbar.LayoutParams extends androidx.appcompat.app.ActionBar.LayoutParams {
+    ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet!);
+    ctor public Toolbar.LayoutParams(int, int);
+    ctor public Toolbar.LayoutParams(int, int, int);
+    ctor public Toolbar.LayoutParams(int);
+    ctor public Toolbar.LayoutParams(androidx.appcompat.widget.Toolbar.LayoutParams!);
+    ctor public Toolbar.LayoutParams(androidx.appcompat.app.ActionBar.LayoutParams!);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+    ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams!);
+  }
+
+  public static interface Toolbar.OnMenuItemClickListener {
+    method public boolean onMenuItemClick(android.view.MenuItem!);
+  }
+
+  public static class Toolbar.SavedState extends androidx.customview.view.AbsSavedState {
+    ctor public Toolbar.SavedState(android.os.Parcel!);
+    ctor public Toolbar.SavedState(android.os.Parcel!, ClassLoader!);
+    ctor public Toolbar.SavedState(android.os.Parcelable!);
+    field public static final android.os.Parcelable.Creator<androidx.appcompat.widget.Toolbar.SavedState!>! CREATOR;
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ToolbarWidgetWrapper implements androidx.appcompat.widget.DecorToolbar {
+    ctor public ToolbarWidgetWrapper(androidx.appcompat.widget.Toolbar!, boolean);
+    ctor public ToolbarWidgetWrapper(androidx.appcompat.widget.Toolbar!, boolean, int, int);
+    method public void animateToVisibility(int);
+    method public boolean canShowOverflowMenu();
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
+    method public android.content.Context! getContext();
+    method public android.view.View! getCustomView();
+    method public int getDisplayOptions();
+    method public int getDropdownItemCount();
+    method public int getDropdownSelectedPosition();
+    method public int getHeight();
+    method public android.view.Menu! getMenu();
+    method public int getNavigationMode();
+    method public CharSequence! getSubtitle();
+    method public CharSequence! getTitle();
+    method public android.view.ViewGroup! getViewGroup();
+    method public int getVisibility();
+    method public boolean hasEmbeddedTabs();
+    method public boolean hasExpandedActionView();
+    method public boolean hasIcon();
+    method public boolean hasLogo();
+    method public boolean hideOverflowMenu();
+    method public void initIndeterminateProgress();
+    method public void initProgress();
+    method public boolean isOverflowMenuShowPending();
+    method public boolean isOverflowMenuShowing();
+    method public boolean isTitleTruncated();
+    method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+    method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
+    method public void setCollapsible(boolean);
+    method public void setCustomView(android.view.View!);
+    method public void setDefaultNavigationContentDescription(int);
+    method public void setDefaultNavigationIcon(android.graphics.drawable.Drawable!);
+    method public void setDisplayOptions(int);
+    method public void setDropdownParams(android.widget.SpinnerAdapter!, android.widget.AdapterView.OnItemSelectedListener!);
+    method public void setDropdownSelectedPosition(int);
+    method public void setEmbeddedTabView(androidx.appcompat.widget.ScrollingTabContainerView!);
+    method public void setHomeButtonEnabled(boolean);
+    method public void setIcon(int);
+    method public void setIcon(android.graphics.drawable.Drawable!);
+    method public void setLogo(int);
+    method public void setLogo(android.graphics.drawable.Drawable!);
+    method public void setMenu(android.view.Menu!, androidx.appcompat.view.menu.MenuPresenter.Callback!);
+    method public void setMenuCallbacks(androidx.appcompat.view.menu.MenuPresenter.Callback!, androidx.appcompat.view.menu.MenuBuilder.Callback!);
+    method public void setMenuPrepared();
+    method public void setNavigationContentDescription(CharSequence!);
+    method public void setNavigationContentDescription(int);
+    method public void setNavigationIcon(android.graphics.drawable.Drawable!);
+    method public void setNavigationIcon(int);
+    method public void setNavigationMode(int);
+    method public void setSubtitle(CharSequence!);
+    method public void setTitle(CharSequence!);
+    method public void setVisibility(int);
+    method public void setWindowCallback(android.view.Window.Callback!);
+    method public void setWindowTitle(CharSequence!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setupAnimatorToVisibility(int, long);
+    method public boolean showOverflowMenu();
+  }
+
+  public class TooltipCompat {
+    method public static void setTooltipText(android.view.View, CharSequence?);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ViewStubCompat extends android.view.View {
+    ctor public ViewStubCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public ViewStubCompat(android.content.Context, android.util.AttributeSet?, int);
+    method public int getInflatedId();
+    method public android.view.LayoutInflater! getLayoutInflater();
+    method public int getLayoutResource();
+    method public android.view.View! inflate();
+    method public void setInflatedId(int);
+    method public void setLayoutInflater(android.view.LayoutInflater!);
+    method public void setLayoutResource(int);
+    method public void setOnInflateListener(androidx.appcompat.widget.ViewStubCompat.OnInflateListener!);
+  }
+
+  public static interface ViewStubCompat.OnInflateListener {
+    method public void onInflate(androidx.appcompat.widget.ViewStubCompat!, android.view.View!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ViewUtils {
+    method public static void computeFitSystemWindows(android.view.View!, android.graphics.Rect!, android.graphics.Rect!);
+    method public static boolean isLayoutRtl(android.view.View!);
+    method public static void makeOptionalFitsSystemWindows(android.view.View!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface WithHint {
+    method public CharSequence? getHint();
+  }
+
+}
+
diff --git a/appcompat/appcompat/api/restricted_current.txt b/appcompat/appcompat/api/restricted_current.txt
index 14d5f94..7439cfd 100644
--- a/appcompat/appcompat/api/restricted_current.txt
+++ b/appcompat/appcompat/api/restricted_current.txt
@@ -1394,15 +1394,14 @@
     method public static void preload();
   }
 
-  public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.TintableBackgroundView {
+  public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.OnReceiveContentViewBehavior androidx.core.view.TintableBackgroundView {
     ctor public AppCompatEditText(android.content.Context);
     ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?);
     ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?, int);
-    method public androidx.core.widget.RichContentReceiverCompat<android.widget.TextView!>? getRichContentReceiverCompat();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
-    method public void setRichContentReceiverCompat(androidx.core.widget.RichContentReceiverCompat<android.widget.TextView!>?);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
     method public void setTextAppearance(android.content.Context!, int);
diff --git a/appcompat/appcompat/src/androidTest/AndroidManifest.xml b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
index 9af9433..c216f56 100644
--- a/appcompat/appcompat/src/androidTest/AndroidManifest.xml
+++ b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
@@ -100,8 +100,8 @@
             android:theme="@style/Theme.TextColors"/>
 
         <activity
-            android:name="androidx.appcompat.widget.AppCompatEditTextRichContentReceiverActivity"
-            android:label="@string/app_compat_edit_text_rich_content_receiver_activity"
+            android:name="androidx.appcompat.widget.AppCompatEditTextReceiveContentActivity"
+            android:label="@string/app_compat_edit_text_receive_content_activity"
             android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AlertDialogCursorTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AlertDialogCursorTest.java
index d32054d..6ce6ee6 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AlertDialogCursorTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AlertDialogCursorTest.java
@@ -65,6 +65,7 @@
 
 import java.io.File;
 
+@SuppressWarnings("unchecked")
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class AlertDialogCursorTest {
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AlertDialogTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AlertDialogTest.java
index 923ec16..e197fff 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AlertDialogTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AlertDialogTest.java
@@ -104,6 +104,7 @@
  *     is rendered by a single <code>CheckedTextView</code>.</li>
  * </ul>
  */
+@SuppressWarnings("unchecked")
 @LargeTest
 public class AlertDialogTest {
     @Rule
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/testutils/AppCompatTintableViewActions.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/testutils/AppCompatTintableViewActions.java
index 0f29c4c..36c0f87 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/testutils/AppCompatTintableViewActions.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/testutils/AppCompatTintableViewActions.java
@@ -36,6 +36,7 @@
 
 import org.hamcrest.Matcher;
 
+@SuppressWarnings("unchecked")
 public class AppCompatTintableViewActions {
     /**
      * Sets the passed color state list as the background tint on a {@link View}.
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseImageViewTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseImageViewTest.java
index fe4058d..18f7ef7 100755
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseImageViewTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseImageViewTest.java
@@ -69,7 +69,7 @@
     public void testImageTintingAcrossStateChange() {
         final @IdRes int viewId = R.id.view_tinted_source;
         final Resources res = mActivity.getResources();
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
         @ColorInt int lilacDisabled = ResourcesCompat.getColor(res, R.color.lilac_disabled, null);
@@ -147,7 +147,7 @@
     public void testImageTintingAcrossModeChange() {
         final @IdRes int viewId = R.id.view_untinted_source;
         final Resources res = mActivity.getResources();
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         @ColorInt int emeraldDefault = ResourcesCompat.getColor(
                 res, R.color.emerald_translucent_default, null);
@@ -218,7 +218,7 @@
     public void testImageTintingWithDefaultMode() {
         final @IdRes int viewId = R.id.view_untinted_source;
         final Resources res = mActivity.getResources();
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         @ColorInt final int sandDefault = ResourcesCompat.getColor(
                 res, R.color.sand_default, null);
@@ -258,7 +258,7 @@
     public void testImageOpaqueTintingAcrossImageChange() {
         final @IdRes int viewId = R.id.view_tinted_no_source;
         final Resources res = mActivity.getResources();
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
         @ColorInt int lilacDisabled = ResourcesCompat.getColor(res, R.color.lilac_disabled, null);
@@ -313,7 +313,7 @@
     public void testImageTranslucentTintingAcrossImageChange() {
         final @IdRes int viewId = R.id.view_untinted_no_source;
         final Resources res = mActivity.getResources();
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         @ColorInt int emeraldDefault = ResourcesCompat.getColor(
                 res, R.color.emerald_translucent_default, null);
@@ -403,7 +403,7 @@
     public void testImageTintingAcrossBackgroundTintingChange() {
         final @IdRes int viewId = R.id.view_untinted_source;
         final Resources res = mActivity.getResources();
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
         @ColorInt int lilacDisabled = ResourcesCompat.getColor(res, R.color.lilac_disabled, null);
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseViewTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseViewTest.java
index 2137144..cc18495 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseViewTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseViewTest.java
@@ -110,7 +110,7 @@
         }
 
         final @IdRes int viewId = R.id.view_tinted_no_background;
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         // Note that all the asserts in this test check that the view background
         // is null. This is because the matching child in the activity doesn't define any
@@ -153,7 +153,7 @@
         }
 
         final @IdRes int viewId = R.id.view_tinted_no_background;
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         // Note that all the asserts in this test check that the view background
         // is null. This is because the matching child in the activity doesn't define any
@@ -192,7 +192,7 @@
     @Test
     public void testBackgroundTintingAcrossStateChange() {
         final @IdRes int viewId = R.id.view_tinted_background;
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         final @ColorInt int lilacDefault = ResourcesCompat.getColor(
                 mResources, R.color.lilac_default, null);
@@ -272,7 +272,7 @@
     @Test
     public void testBackgroundTintingViewCompatAcrossStateChange() {
         final @IdRes int viewId = R.id.view_tinted_background;
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         final @ColorInt int lilacDefault = ResourcesCompat.getColor(
                 mResources, R.color.lilac_default, null);
@@ -353,7 +353,7 @@
     @Test
     public void testBackgroundTintingAcrossModeChange() {
         final @IdRes int viewId = R.id.view_untinted_background;
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         final @ColorInt int emeraldDefault = ResourcesCompat.getColor(
                 mResources, R.color.emerald_translucent_default, null);
@@ -421,7 +421,7 @@
     @Test
     public void testBackgroundTintingViewCompatAcrossModeChange() {
         final @IdRes int viewId = R.id.view_untinted_background;
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         final @ColorInt int emeraldDefault = ResourcesCompat.getColor(
                 mResources, R.color.emerald_translucent_default, null);
@@ -488,7 +488,7 @@
     @Test
     public void testBackgroundOpaqueTintingAcrossBackgroundChange() {
         final @IdRes int viewId = R.id.view_tinted_no_background;
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         final @ColorInt int lilacDefault = ResourcesCompat.getColor(
                 mResources, R.color.lilac_default, null);
@@ -546,7 +546,7 @@
     @Test
     public void testBackgroundTranslucentTintingAcrossBackgroundChange() {
         final @IdRes int viewId = R.id.view_untinted_no_background;
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         final @ColorInt int emeraldDefault = ResourcesCompat.getColor(
                 mResources, R.color.emerald_translucent_default, null);
@@ -628,7 +628,7 @@
     }
 
     protected void testUntintedBackgroundTintingViewCompatAcrossStateChange(@IdRes int viewId) {
-        final T view = (T) mContainer.findViewById(viewId);
+        final T view = mContainer.findViewById(viewId);
 
         final @ColorInt int oceanDefault = ResourcesCompat.getColor(
                 mResources, R.color.ocean_default, null);
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextRichContentReceiverActivity.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextReceiveContentActivity.java
similarity index 83%
rename from appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextRichContentReceiverActivity.java
rename to appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextReceiveContentActivity.java
index 9601ac1..4dc2c31 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextRichContentReceiverActivity.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextReceiveContentActivity.java
@@ -18,9 +18,9 @@
 import androidx.appcompat.test.R;
 import androidx.appcompat.testutils.BaseTestActivity;
 
-public class AppCompatEditTextRichContentReceiverActivity extends BaseTestActivity {
+public class AppCompatEditTextReceiveContentActivity extends BaseTestActivity {
     @Override
     protected int getContentViewLayoutResId() {
-        return R.layout.appcompat_edittext_richcontentreceiver_activity;
+        return R.layout.appcompat_edittext_receive_content_activity;
     }
 }
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextReceiveContentTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextReceiveContentTest.java
new file mode 100644
index 0000000..3fc3605
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextReceiveContentTest.java
@@ -0,0 +1,628 @@
+/*
+ * 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.appcompat.widget;
+
+import static androidx.core.view.ContentInfoCompat.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static androidx.core.view.ContentInfoCompat.SOURCE_CLIPBOARD;
+import static androidx.core.view.ContentInfoCompat.SOURCE_DRAG_AND_DROP;
+import static androidx.core.view.ContentInfoCompat.SOURCE_INPUT_METHOD;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.SpannableStringBuilder;
+import android.view.DragEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.test.R;
+import androidx.core.util.ObjectsCompat;
+import androidx.core.view.ContentInfoCompat;
+import androidx.core.view.OnReceiveContentListener;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.inputmethod.EditorInfoCompat;
+import androidx.core.view.inputmethod.InputConnectionCompat;
+import androidx.core.view.inputmethod.InputContentInfoCompat;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mockito;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AppCompatEditTextReceiveContentTest {
+    private static final String[] MIME_TYPES_IMAGES = new String[] {"image/*"};
+    private static final Uri SAMPLE_CONTENT_URI = Uri.parse("content://com.example/path");
+
+    @Rule
+    public final ActivityTestRule<AppCompatEditTextReceiveContentActivity> mActivityTestRule =
+            new ActivityTestRule<>(AppCompatEditTextReceiveContentActivity.class);
+
+    private Context mContext;
+    private AppCompatEditText mEditText;
+    private OnReceiveContentListener mMockReceiver;
+    private ClipboardManager mClipboardManager;
+
+    @SuppressWarnings("unchecked")
+    @UiThreadTest
+    @Before
+    public void before() {
+        AppCompatActivity activity = mActivityTestRule.getActivity();
+        mContext = activity;
+        mEditText = activity.findViewById(R.id.edit_text);
+
+        mMockReceiver = Mockito.mock(OnReceiveContentListener.class);
+
+        mClipboardManager = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
+
+        // Clear the clipboard
+        if (Build.VERSION.SDK_INT >= 28) {
+            mClipboardManager.clearPrimaryClip();
+        } else {
+            mClipboardManager.setPrimaryClip(ClipData.newPlainText("", ""));
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    public void testOnCreateInputConnection_nullEditorInfo() throws Exception {
+        setTextAndCursor("xz", 1);
+        try {
+            mEditText.onCreateInputConnection(null);
+            Assert.fail("Expected NullPointerException");
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    public void testOnCreateInputConnection_noReceiver() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Call onCreateInputConnection() and assert that contentMimeTypes is not set.
+        EditorInfo editorInfo = new EditorInfo();
+        InputConnection ic = mEditText.onCreateInputConnection(editorInfo);
+        assertThat(ic).isNotNull();
+        assertThat(EditorInfoCompat.getContentMimeTypes(editorInfo)).isEqualTo(new String[0]);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testOnCreateInputConnection_withReceiver() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Configure the receiver to a custom impl.
+        String[] mimeTypes = new String[] {"image/*", "video/mp4"};
+        ViewCompat.setOnReceiveContentListener(mEditText, mimeTypes, mMockReceiver);
+
+        // Call onCreateInputConnection() and assert that contentMimeTypes uses the receiver's MIME
+        // types.
+        EditorInfo editorInfo = new EditorInfo();
+        InputConnection ic = mEditText.onCreateInputConnection(editorInfo);
+        assertThat(ic).isNotNull();
+        verifyZeroInteractions(mMockReceiver);
+        assertThat(EditorInfoCompat.getContentMimeTypes(editorInfo)).isEqualTo(mimeTypes);
+    }
+
+    // ============================================================================================
+    // Tests to verify that the listener is invoked for all the appropriate user interactions:
+    // * Paste from clipboard ("Paste" and "Paste as plain text" actions)
+    // * Content insertion from IME
+    // ============================================================================================
+
+    @UiThreadTest
+    @Test
+    public void testPaste_noReceiver() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Copy text to the clipboard.
+        ClipData clip = ClipData.newPlainText("test", "y");
+        clip = copyToClipboard(clip);
+
+        // Trigger the "Paste" action. This should execute the platform paste handling, so the
+        // content should be inserted according to whatever behavior is implemented in the OS
+        // version that's running.
+        boolean result = triggerContextMenuAction(android.R.id.paste);
+        assertThat(result).isTrue();
+        if (Build.VERSION.SDK_INT <= 20) {
+            // The platform code on Android K and earlier had logic to insert a space before and
+            // after the pasted content (if no space was already present). See
+            // https://cs.android.com/android/platform/superproject/+/android-4.4.4_r2:frameworks/base/core/java/android/widget/TextView.java;l=8526,8527,8528,8545,8546
+            assertTextAndCursorPosition("x y z", 3);
+        } else {
+            assertTextAndCursorPosition("xyz", 2);
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    public void testPaste_withReceiver() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Copy text to the clipboard.
+        ClipData clip = ClipData.newPlainText("test", "y");
+        clip = copyToClipboard(clip);
+
+        // Setup: Configure to use the mock receiver.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the "Paste" action and assert that the custom receiver was executed.
+        triggerContextMenuAction(android.R.id.paste);
+        verify(mMockReceiver, times(1)).onReceiveContent(
+                eq(mEditText), payloadEq(clip, SOURCE_CLIPBOARD, 0));
+        verifyNoMoreInteractions(mMockReceiver);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testPaste_withReceiver_resultBoolean() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Copy text to the clipboard.
+        ClipData clip = ClipData.newPlainText("test", "y");
+        clip = copyToClipboard(clip);
+
+        // Setup: Configure to use the mock receiver.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the "Paste" action and assert the boolean it returns.
+        ContentInfoCompat toReturn = new ContentInfoCompat.Builder(clip, SOURCE_CLIPBOARD).build();
+        when(mMockReceiver.onReceiveContent(eq(mEditText), any(ContentInfoCompat.class)))
+                .thenReturn(toReturn);
+        boolean result = triggerContextMenuAction(android.R.id.paste);
+        assertThat(result).isTrue();
+
+        when(mMockReceiver.onReceiveContent(eq(mEditText), any(ContentInfoCompat.class)))
+                .thenReturn(null);
+        result = triggerContextMenuAction(android.R.id.paste);
+        assertThat(result).isTrue();
+    }
+
+    @UiThreadTest
+    @Test
+    public void testPaste_withReceiver_unsupportedMimeType() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Copy a URI to the clipboard with a MIME type that's not supported by the receiver.
+        ClipData clip = new ClipData("test", new String[]{"video/mp4"},
+                new ClipData.Item("text", null, Uri.parse("content://com.example/path")));
+        clip = copyToClipboard(clip);
+
+        // Setup: Configure to use the mock receiver.
+        String[] mimeTypes = new String[] {"image/*"};
+        ViewCompat.setOnReceiveContentListener(mEditText, mimeTypes, mMockReceiver);
+
+        // Trigger the "Paste" action and assert that the custom receiver was executed. This
+        // confirms that the receiver is invoked (give a chance to handle the content via some
+        // fallback) even if the MIME type of the content is not one of the receiver's supported
+        // MIME types.
+        triggerContextMenuAction(android.R.id.paste);
+        verify(mMockReceiver, times(1)).onReceiveContent(
+                eq(mEditText), payloadEq(clip, SOURCE_CLIPBOARD, 0));
+        verifyNoMoreInteractions(mMockReceiver);
+    }
+
+    @SdkSuppress(minSdkVersion = 23) // The action "Paste as plain text" was added in SDK 23.
+    @UiThreadTest
+    @Test
+    public void testPasteAsPlainText_noReceiver() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Copy HTML to the clipboard.
+        ClipData clip = ClipData.newHtmlText("test", "*y*", "<b>y</b>");
+        clip = copyToClipboard(clip);
+
+        // Trigger the "Paste as plain text" action. This should execute the platform paste
+        // handling, so the content should be inserted according to whatever behavior is implemented
+        // in the OS version that's running.
+        boolean result = triggerContextMenuAction(android.R.id.pasteAsPlainText);
+        assertThat(result).isTrue();
+        assertTextAndCursorPosition("x*y*z", 4);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testPasteAsPlainText_withReceiver() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Copy text to the clipboard.
+        ClipData clip = ClipData.newPlainText("test", "y");
+        clip = copyToClipboard(clip);
+
+        // Setup: Configure to use the mock receiver.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the "Paste as plain text" action and assert that the custom receiver was
+        // executed.
+        triggerContextMenuAction(android.R.id.pasteAsPlainText);
+        verify(mMockReceiver, times(1)).onReceiveContent(
+                eq(mEditText), payloadEq(clip, SOURCE_CLIPBOARD, FLAG_CONVERT_TO_PLAIN_TEXT));
+        verifyNoMoreInteractions(mMockReceiver);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testPasteAsPlainText_withReceiver_unsupportedMimeType() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Copy a URI to the clipboard with a MIME type that's not supported by the receiver.
+        ClipData clip = new ClipData("test", new String[]{"video/mp4"},
+                new ClipData.Item("text", null, Uri.parse("content://com.example/path")));
+        clip = copyToClipboard(clip);
+
+        // Setup: Configure to use the mock receiver.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the "Paste as plain text" action and assert that the custom receiver was
+        // executed. This confirms that the receiver is invoked (given a chance to handle the
+        // content via some fallback) even if the MIME type of the content is not one of the
+        // receiver's supported MIME types.
+        triggerContextMenuAction(android.R.id.pasteAsPlainText);
+        verify(mMockReceiver, times(1)).onReceiveContent(
+                eq(mEditText), payloadEq(clip, SOURCE_CLIPBOARD, FLAG_CONVERT_TO_PLAIN_TEXT));
+        verifyNoMoreInteractions(mMockReceiver);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testImeCommitContent_noReceiver() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Trigger the IME's commitContent() call and assert its outcome.
+        boolean result = triggerImeCommitContentViaCompat("image/png");
+        assertThat(result).isFalse();
+        assertTextAndCursorPosition("xz", 1);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testImeCommitContent_withReceiver() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Configure the receiver to a custom impl.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the IME's commitContent() call and assert that the custom receiver was executed.
+        triggerImeCommitContentViaCompat("image/png");
+        ClipData clip = ClipData.newRawUri("", SAMPLE_CONTENT_URI);
+        verify(mMockReceiver, times(1)).onReceiveContent(
+                eq(mEditText), payloadEq(clip, SOURCE_INPUT_METHOD, 0));
+        verifyNoMoreInteractions(mMockReceiver);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testImeCommitContent_withReceiver_resultBoolean() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Configure the receiver to a custom impl.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the IME's commitContent() call and assert the boolean value it returns.
+        when(mMockReceiver.onReceiveContent(eq(mEditText), any(ContentInfoCompat.class)))
+                .thenReturn(null);
+        boolean result1 = triggerImeCommitContentViaCompat("image/png");
+        ClipData clip = ClipData.newRawUri("", SAMPLE_CONTENT_URI);
+        ContentInfoCompat payloadToReturn =
+                new ContentInfoCompat.Builder(clip, SOURCE_INPUT_METHOD).build();
+        when(mMockReceiver.onReceiveContent(eq(mEditText), any(ContentInfoCompat.class)))
+                .thenReturn(payloadToReturn);
+        boolean result2 = triggerImeCommitContentViaCompat("image/png");
+        verify(mMockReceiver, times(2)).onReceiveContent(
+                eq(mEditText), payloadEq(clip, SOURCE_INPUT_METHOD, 0));
+        if (Build.VERSION.SDK_INT >= 25) {
+            // On SDK >= 25, the boolean result depends on the return value from the receiver.
+            assertThat(result1).isTrue();
+            assertThat(result2).isFalse();
+        } else {
+            // On SDK <= 24, commitContent() is handled via InputConnection.performPrivateCommand().
+            // This ends up returning true whenever the command is sent, regardless of the return
+            // value of the underlying operation.
+            // Relevant code links:
+            // https://osscs.corp.google.com/androidx/platform/frameworks/support/+/androidx-master-dev:core/core/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java;l=294;drc=0c365e84832f5ec5e393be28ab1c618eb18bab1e
+            // https://cs.android.com/android/platform/superproject/+/android-7.0.0_r6:frameworks/base/core/java/com/android/internal/widget/EditableInputConnection.java;l=168
+            assertThat(result1).isTrue();
+            assertThat(result2).isTrue();
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    public void testImeCommitContent_withReceiver_unsupportedMimeType() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Configure the receiver to a custom impl.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the IME's commitContent() call and assert that the custom receiver was not
+        // executed. This is because InputConnectionCompat.commitContent() checks the supported MIME
+        // types before proceeding.
+        triggerImeCommitContentViaCompat("video/mp4");
+        verifyZeroInteractions(mMockReceiver);
+    }
+
+    @SdkSuppress(minSdkVersion = 25) // InputConnection.commitContent() was added in SDK 25.
+    @UiThreadTest
+    @Test
+    public void testImeCommitContent_direct_withReceiver_unsupportedMimeType() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Configure the receiver to a custom impl.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the IME's commitContent() call and assert that the custom receiver was executed.
+        triggerImeCommitContentDirect("video/mp4");
+        ClipData clip = ClipData.newRawUri("", SAMPLE_CONTENT_URI);
+        verify(mMockReceiver, times(1)).onReceiveContent(
+                eq(mEditText), payloadEq(clip, SOURCE_INPUT_METHOD, 0));
+        verifyNoMoreInteractions(mMockReceiver);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testImeCommitContent_linkUri() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Configure the receiver to a mock impl.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the IME's commitContent() call with a linkUri and assert receiver extras.
+        Uri sampleLinkUri = Uri.parse("http://example.com");
+        triggerImeCommitContentViaCompat("image/png", sampleLinkUri, null);
+        ClipData clip = ClipData.newRawUri("expected", SAMPLE_CONTENT_URI);
+        verify(mMockReceiver, times(1)).onReceiveContent(
+                eq(mEditText),
+                payloadEq(clip, SOURCE_INPUT_METHOD, 0, sampleLinkUri, null));
+    }
+
+    @UiThreadTest
+    @Test
+    public void testImeCommitContent_opts() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Configure the receiver to a mock impl.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the IME's commitContent() call with opts and assert receiver extras.
+        String sampleOptValue = "sampleOptValue";
+        triggerImeCommitContentViaCompat("image/png", null, sampleOptValue);
+        ClipData clip = ClipData.newRawUri("expected", SAMPLE_CONTENT_URI);
+        verify(mMockReceiver, times(1)).onReceiveContent(
+                eq(mEditText),
+                payloadEq(clip, SOURCE_INPUT_METHOD, 0, null, sampleOptValue));
+    }
+
+    @UiThreadTest
+    @Test
+    public void testImeCommitContent_linkUriAndOpts() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        // Setup: Configure the receiver to a mock impl.
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        // Trigger the IME's commitContent() call with a linkUri & opts and assert receiver extras.
+        Uri sampleLinkUri = Uri.parse("http://example.com");
+        String sampleOptValue = "sampleOptValue";
+        triggerImeCommitContentViaCompat("image/png", sampleLinkUri, sampleOptValue);
+        ClipData clip = ClipData.newRawUri("expected", SAMPLE_CONTENT_URI);
+        verify(mMockReceiver, times(1)).onReceiveContent(
+                eq(mEditText),
+                payloadEq(clip, SOURCE_INPUT_METHOD, 0, sampleLinkUri, sampleOptValue));
+    }
+
+    @UiThreadTest
+    @Test
+    public void testDragAndDrop_noReceiver() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        ClipData clip = ClipData.newPlainText("test", "a");
+        clip.addItem(new ClipData.Item("b"));
+        boolean result = triggerDropEvent(clip);
+
+        assertThat(result).isTrue();
+        if (Build.VERSION.SDK_INT <= 20) {
+            // The platform code on Android K and earlier had logic to insert a space before and
+            // after the inserted content (if no space was already present). See
+            // https://cs.android.com/android/platform/superproject/+/android-4.4.4_r2:frameworks/base/core/java/android/widget/TextView.java;l=8526,8527,8528,8545,8546
+            assertTextAndCursorPosition("ab xz", 2);
+        } else {
+            assertTextAndCursorPosition("abxz", 2);
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    public void testDragAndDrop_withReceiver() throws Exception {
+        setTextAndCursor("xz", 1);
+
+        ViewCompat.setOnReceiveContentListener(mEditText, MIME_TYPES_IMAGES, mMockReceiver);
+
+        ClipData clip = ClipData.newPlainText("test", "a");
+        clip.addItem(new ClipData.Item("b"));
+        boolean result = triggerDropEvent(clip);
+
+        assertThat(result).isTrue();
+        if (Build.VERSION.SDK_INT >= 24) {
+            verify(mMockReceiver, times(1)).onReceiveContent(
+                    eq(mEditText), payloadEq(clip, SOURCE_DRAG_AND_DROP, 0));
+            verifyNoMoreInteractions(mMockReceiver);
+            // Note: The cursor is moved to the location of the drop before calling the receiver.
+            assertTextAndCursorPosition("xz", 0);
+        } else {
+            if (Build.VERSION.SDK_INT <= 20) {
+                // The platform code on Android K and earlier had logic to insert a space before and
+                // after the inserted content (if no space was already present). See
+                // https://cs.android.com/android/platform/superproject/+/android-4.4.4_r2:frameworks/base/core/java/android/widget/TextView.java;l=8526,8527,8528,8545,8546
+                assertTextAndCursorPosition("ab xz", 2);
+            } else {
+                assertTextAndCursorPosition("abxz", 2);
+            }
+        }
+    }
+
+    private boolean triggerContextMenuAction(final int actionId) {
+        return mEditText.onTextContextMenuItem(actionId);
+    }
+
+    private boolean triggerImeCommitContentViaCompat(String mimeType) {
+        return triggerImeCommitContentViaCompat(mimeType, null, null);
+    }
+
+    private boolean triggerImeCommitContentViaCompat(String mimeType, Uri linkUri, String extra) {
+        final InputContentInfoCompat contentInfo = new InputContentInfoCompat(
+                SAMPLE_CONTENT_URI,
+                new ClipDescription("from test", new String[]{mimeType}),
+                linkUri);
+        final Bundle opts;
+        if (extra == null) {
+            opts = null;
+        } else {
+            opts = new Bundle();
+            opts.putString(PayloadArgumentMatcher.EXTRA_KEY, extra);
+        }
+        EditorInfo editorInfo = new EditorInfo();
+        InputConnection ic = mEditText.onCreateInputConnection(editorInfo);
+        return InputConnectionCompat.commitContent(ic, editorInfo, contentInfo, 0, opts);
+    }
+
+    private boolean triggerImeCommitContentDirect(String mimeType) {
+        final InputContentInfo contentInfo = new InputContentInfo(
+                SAMPLE_CONTENT_URI,
+                new ClipDescription("from test", new String[]{mimeType}),
+                null);
+        EditorInfo editorInfo = new EditorInfo();
+        InputConnection ic = mEditText.onCreateInputConnection(editorInfo);
+        return ic.commitContent(contentInfo, 0, null);
+    }
+
+    private boolean triggerDropEvent(ClipData clip) {
+        DragEvent dropEvent = createDragEvent(DragEvent.ACTION_DROP, mEditText.getX(),
+                mEditText.getY(), clip);
+        return mEditText.onDragEvent(dropEvent);
+    }
+
+    private static DragEvent createDragEvent(int action, float x, float y, ClipData clip) {
+        DragEvent dragEvent = mock(DragEvent.class);
+        when(dragEvent.getAction()).thenReturn(action);
+        when(dragEvent.getX()).thenReturn(x);
+        when(dragEvent.getY()).thenReturn(y);
+        when(dragEvent.getClipData()).thenReturn(clip);
+        return dragEvent;
+    }
+
+    private void setTextAndCursor(final String text, final int cursorPosition) {
+        mEditText.requestFocus();
+        SpannableStringBuilder ssb = new SpannableStringBuilder(text);
+        mEditText.setText(ssb);
+        mEditText.setSelection(cursorPosition);
+        assertThat(mEditText.hasFocus()).isTrue();
+        assertTextAndCursorPosition(text, cursorPosition);
+    }
+
+    private void assertTextAndCursorPosition(String expectedText, int cursorPosition) {
+        assertThat(mEditText.getText().toString()).isEqualTo(expectedText);
+        assertThat(mEditText.getSelectionStart()).isEqualTo(cursorPosition);
+        assertThat(mEditText.getSelectionEnd()).isEqualTo(cursorPosition);
+    }
+
+    private ClipData copyToClipboard(final ClipData clip) {
+        mClipboardManager.setPrimaryClip(clip);
+        ClipData primaryClip = mClipboardManager.getPrimaryClip();
+        assertThat(primaryClip).isNotNull();
+        return primaryClip;
+    }
+
+    private static ContentInfoCompat payloadEq(@NonNull ClipData clip, int source, int flags) {
+        return argThat(new PayloadArgumentMatcher(clip, source, flags, null, null));
+    }
+
+    private static ContentInfoCompat payloadEq(@NonNull ClipData clip, int source, int flags,
+            @Nullable Uri linkUri, @Nullable String extra) {
+        return argThat(new PayloadArgumentMatcher(clip, source, flags, linkUri, extra));
+    }
+
+    private static class PayloadArgumentMatcher implements ArgumentMatcher<ContentInfoCompat> {
+        public static final String EXTRA_KEY = "testExtra";
+
+        @NonNull
+        private final ClipData mClip;
+        private final int mSource;
+        private final int mFlags;
+        @Nullable
+        private final Uri mLinkUri;
+        @Nullable
+        private final String mExtra;
+
+        private PayloadArgumentMatcher(@NonNull ClipData clip, int source, int flags,
+                @Nullable Uri linkUri, @Nullable String extra) {
+            mClip = clip;
+            mSource = source;
+            mFlags = flags;
+            mLinkUri = linkUri;
+            mExtra = extra;
+        }
+
+        @Override
+        public boolean matches(ContentInfoCompat actual) {
+            ClipData.Item expectedItem = mClip.getItemAt(0);
+            ClipData.Item actualItem = actual.getClip().getItemAt(0);
+            return ObjectsCompat.equals(expectedItem.getText(), actualItem.getText())
+                    && ObjectsCompat.equals(expectedItem.getUri(), actualItem.getUri())
+                    && mSource == actual.getSource()
+                    && mFlags == actual.getFlags()
+                    && ObjectsCompat.equals(mLinkUri, actual.getLinkUri())
+                    && extrasMatch(actual.getExtras());
+        }
+
+        private boolean extrasMatch(Bundle actualExtras) {
+            if (mExtra == null) {
+                return actualExtras == null;
+            }
+            String actualExtraValue = actualExtras.getString(EXTRA_KEY);
+            return ObjectsCompat.equals(mExtra, actualExtraValue);
+        }
+    }
+}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextRichContentReceiverTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextRichContentReceiverTest.java
deleted file mode 100644
index 5f68405..0000000
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextRichContentReceiverTest.java
+++ /dev/null
@@ -1,489 +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.appcompat.widget;
-
-import static androidx.core.widget.RichContentReceiverCompat.FLAG_CONVERT_TO_PLAIN_TEXT;
-import static androidx.core.widget.RichContentReceiverCompat.SOURCE_CLIPBOARD;
-import static androidx.core.widget.RichContentReceiverCompat.SOURCE_INPUT_METHOD;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Build;
-import android.text.SpannableStringBuilder;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputContentInfo;
-import android.widget.TextView;
-
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.test.R;
-import androidx.core.view.inputmethod.EditorInfoCompat;
-import androidx.core.view.inputmethod.InputConnectionCompat;
-import androidx.core.view.inputmethod.InputContentInfoCompat;
-import androidx.core.widget.RichContentReceiverCompat;
-import androidx.core.widget.TextViewRichContentReceiverCompat;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.MediumTest;
-import androidx.test.filters.SdkSuppress;
-import androidx.test.rule.ActivityTestRule;
-
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mockito;
-
-import java.util.Set;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class AppCompatEditTextRichContentReceiverTest {
-    private static final Set<String> ALL_TEXT_AND_IMAGE_MIME_TYPES = ImmutableSet.of(
-            "text/*", "image/*");
-
-    @Rule
-    public final ActivityTestRule<AppCompatEditTextRichContentReceiverActivity> mActivityTestRule =
-            new ActivityTestRule<>(AppCompatEditTextRichContentReceiverActivity.class);
-
-    private Context mContext;
-    private AppCompatEditText mEditText;
-    private RichContentReceiverCompat<TextView> mMockReceiver;
-    private ClipboardManager mClipboardManager;
-
-    @UiThreadTest
-    @Before
-    public void before() {
-        AppCompatActivity activity = mActivityTestRule.getActivity();
-        mContext = activity;
-        mEditText = activity.findViewById(R.id.edit_text_default_values);
-
-        mMockReceiver = Mockito.mock(RichContentReceiverCompat.class);
-
-        mClipboardManager = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
-
-        // Clear the clipboard
-        if (Build.VERSION.SDK_INT >= 28) {
-            mClipboardManager.clearPrimaryClip();
-        } else {
-            mClipboardManager.setPrimaryClip(ClipData.newPlainText("", ""));
-        }
-    }
-
-    // ============================================================================================
-    // Tests to verify APIs/accessors/defaults related to RichContentReceiver.
-    // ============================================================================================
-
-    @UiThreadTest
-    @Test
-    public void testGetAndSetRichContentReceiverCompat() throws Exception {
-        // Verify that by default the getter returns null.
-        assertThat(mEditText.getRichContentReceiverCompat()).isNull();
-
-        // Verify that after setting a custom receiver, the getter returns it.
-        TextViewRichContentReceiverCompat receiver = new TextViewRichContentReceiverCompat() {};
-        mEditText.setRichContentReceiverCompat(receiver);
-        assertThat(mEditText.getRichContentReceiverCompat()).isSameInstanceAs(receiver);
-
-        // Verify that the receiver can be reset by passing null.
-        mEditText.setRichContentReceiverCompat(null);
-        assertThat(mEditText.getRichContentReceiverCompat()).isNull();
-    }
-
-    @UiThreadTest
-    @Test
-    public void testOnCreateInputConnection_nullEditorInfo() throws Exception {
-        setTextAndCursor("xz", 1);
-        try {
-            mEditText.onCreateInputConnection(null);
-            Assert.fail("Expected NullPointerException");
-        } catch (NullPointerException expected) {
-        }
-    }
-
-    @UiThreadTest
-    @Test
-    public void testOnCreateInputConnection_noReceiver() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Call onCreateInputConnection() and assert that contentMimeTypes is not set.
-        EditorInfo editorInfo = new EditorInfo();
-        InputConnection ic = mEditText.onCreateInputConnection(editorInfo);
-        assertThat(ic).isNotNull();
-        assertThat(EditorInfoCompat.getContentMimeTypes(editorInfo)).isEqualTo(new String[0]);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testOnCreateInputConnection_withReceiver() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Configure the receiver to a custom impl.
-        Set<String> receiverMimeTypes = ImmutableSet.of("text/plain", "image/png", "video/mp4");
-        when(mMockReceiver.getSupportedMimeTypes()).thenReturn(receiverMimeTypes);
-        mEditText.setRichContentReceiverCompat(mMockReceiver);
-
-        // Call onCreateInputConnection() and assert that contentMimeTypes is set from the receiver.
-        EditorInfo editorInfo = new EditorInfo();
-        InputConnection ic = mEditText.onCreateInputConnection(editorInfo);
-        assertThat(ic).isNotNull();
-        verify(mMockReceiver, times(1)).getSupportedMimeTypes();
-        verifyNoMoreInteractions(mMockReceiver);
-        assertThat(EditorInfoCompat.getContentMimeTypes(editorInfo))
-                .isEqualTo(receiverMimeTypes.toArray(new String[0]));
-    }
-
-    // ============================================================================================
-    // Tests to verify that the receiver callback is invoked for all the appropriate user
-    // interactions:
-    // * Paste from clipboard ("Paste" and "Paste as plain text" actions)
-    // * Content insertion from IME
-    // ============================================================================================
-
-    @UiThreadTest
-    @Test
-    public void testPaste_noReceiver() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Copy text to the clipboard.
-        ClipData clip = ClipData.newPlainText("test", "y");
-        clip = copyToClipboard(clip);
-
-        // Trigger the "Paste" action. This should execute the platform paste handling, so the
-        // content should be inserted according to whatever behavior is implemented in the OS
-        // version that's running.
-        boolean result = triggerContextMenuAction(android.R.id.paste);
-        assertThat(result).isTrue();
-        if (Build.VERSION.SDK_INT <= 20) {
-            // The platform code on Android K and earlier had logic to insert a space before and
-            // after the pasted content (if no space was already present). See
-            // https://cs.android.com/android/platform/superproject/+/android-4.4.4_r2:frameworks/base/core/java/android/widget/TextView.java;l=8526,8527,8528,8545,8546
-            assertTextAndCursorPosition("x y z", 3);
-        } else {
-            assertTextAndCursorPosition("xyz", 2);
-        }
-    }
-
-    @UiThreadTest
-    @Test
-    public void testPaste_withReceiver() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Copy text to the clipboard.
-        ClipData clip = ClipData.newPlainText("test", "y");
-        clip = copyToClipboard(clip);
-
-        // Setup: Configure to use the mock receiver.
-        mEditText.setRichContentReceiverCompat(mMockReceiver);
-
-        // Trigger the "Paste" action and assert that the custom receiver was executed.
-        triggerContextMenuAction(android.R.id.paste);
-        verify(mMockReceiver, times(1)).onReceive(
-                eq(mEditText), clipEq(clip), eq(SOURCE_CLIPBOARD), eq(0));
-        verifyNoMoreInteractions(mMockReceiver);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testPaste_withReceiver_resultBoolean() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Copy text to the clipboard.
-        ClipData clip = ClipData.newPlainText("test", "y");
-        clip = copyToClipboard(clip);
-
-        // Setup: Configure to use the mock receiver.
-        mEditText.setRichContentReceiverCompat(mMockReceiver);
-
-        // Trigger the "Paste" action and assert that the boolean result is true regardless of
-        // the receiver's return value.
-        when(mMockReceiver.onReceive(eq(mEditText), eq(clip), eq(SOURCE_CLIPBOARD),
-                eq(FLAG_CONVERT_TO_PLAIN_TEXT))).thenReturn(true);
-        boolean result = triggerContextMenuAction(android.R.id.paste);
-        assertThat(result).isTrue();
-
-        when(mMockReceiver.onReceive(eq(mEditText), eq(clip), eq(SOURCE_CLIPBOARD),
-                eq(FLAG_CONVERT_TO_PLAIN_TEXT))).thenReturn(false);
-        result = triggerContextMenuAction(android.R.id.paste);
-        assertThat(result).isTrue();
-    }
-
-    @UiThreadTest
-    @Test
-    public void testPaste_withReceiver_unsupportedMimeType() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Copy a URI to the clipboard with a MIME type that's not supported by the receiver.
-        ClipData clip = new ClipData("test", new String[]{"video/mp4"},
-                new ClipData.Item("text", null, Uri.parse("content://com.example/path")));
-        clip = copyToClipboard(clip);
-
-        // Setup: Configure to use the mock receiver.
-        mEditText.setRichContentReceiverCompat(mMockReceiver);
-
-        // Trigger the "Paste" action and assert that the custom receiver was executed. This
-        // confirms that the receiver is invoked (give a chance to handle the content via some
-        // fallback) even if the MIME type of the content is not one of the receiver's supported
-        // MIME types.
-        triggerContextMenuAction(android.R.id.paste);
-        verify(mMockReceiver, times(1)).onReceive(
-                eq(mEditText), clipEq(clip), eq(SOURCE_CLIPBOARD), eq(0));
-        verifyNoMoreInteractions(mMockReceiver);
-    }
-
-    @SdkSuppress(minSdkVersion = 23) // The action "Paste as plain text" was added in SDK 23.
-    @UiThreadTest
-    @Test
-    public void testPasteAsPlainText_noReceiver() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Copy HTML to the clipboard.
-        ClipData clip = ClipData.newHtmlText("test", "*y*", "<b>y</b>");
-        clip = copyToClipboard(clip);
-
-        // Trigger the "Paste as plain text" action. This should execute the platform paste
-        // handling, so the content should be inserted according to whatever behavior is implemented
-        // in the OS version that's running.
-        boolean result = triggerContextMenuAction(android.R.id.pasteAsPlainText);
-        assertThat(result).isTrue();
-        assertTextAndCursorPosition("x*y*z", 4);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testPasteAsPlainText_withReceiver() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Copy text to the clipboard.
-        ClipData clip = ClipData.newPlainText("test", "y");
-        clip = copyToClipboard(clip);
-
-        // Setup: Configure to use the mock receiver.
-        mEditText.setRichContentReceiverCompat(mMockReceiver);
-
-        // Trigger the "Paste as plain text" action and assert that the custom receiver was
-        // executed.
-        triggerContextMenuAction(android.R.id.pasteAsPlainText);
-        verify(mMockReceiver, times(1)).onReceive(
-                eq(mEditText), clipEq(clip),
-                eq(SOURCE_CLIPBOARD), eq(FLAG_CONVERT_TO_PLAIN_TEXT));
-        verifyNoMoreInteractions(mMockReceiver);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testPasteAsPlainText_withReceiver_unsupportedMimeType() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Copy a URI to the clipboard with a MIME type that's not supported by the receiver.
-        ClipData clip = new ClipData("test", new String[]{"video/mp4"},
-                new ClipData.Item("text", null, Uri.parse("content://com.example/path")));
-        clip = copyToClipboard(clip);
-
-        // Setup: Configure to use the mock receiver.
-        mEditText.setRichContentReceiverCompat(mMockReceiver);
-
-        // Trigger the "Paste as plain text" action and assert that the custom receiver was
-        // executed. This confirms that the receiver is invoked (given a chance to handle the
-        // content via some fallback) even if the MIME type of the content is not one of the
-        // receiver's supported MIME types.
-        triggerContextMenuAction(android.R.id.pasteAsPlainText);
-        verify(mMockReceiver, times(1)).onReceive(
-                eq(mEditText), clipEq(clip),
-                eq(SOURCE_CLIPBOARD), eq(FLAG_CONVERT_TO_PLAIN_TEXT));
-        verifyNoMoreInteractions(mMockReceiver);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testImeCommitContent_noReceiver() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Trigger the IME's commitContent() call and assert its outcome.
-        boolean result = triggerImeCommitContentViaCompat("image/png");
-        assertThat(result).isFalse();
-        assertTextAndCursorPosition("xz", 1);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testImeCommitContent_withReceiver() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Configure the receiver to a custom impl that supports all text and images.
-        when(mMockReceiver.getSupportedMimeTypes()).thenReturn(ALL_TEXT_AND_IMAGE_MIME_TYPES);
-        mEditText.setRichContentReceiverCompat(mMockReceiver);
-
-        // Trigger the IME's commitContent() call and assert that the custom receiver was executed.
-        triggerImeCommitContentViaCompat("image/png");
-        verify(mMockReceiver, times(1)).getSupportedMimeTypes();
-        verify(mMockReceiver, times(1)).onReceive(
-                eq(mEditText), any(ClipData.class), eq(SOURCE_INPUT_METHOD), eq(0));
-        verifyNoMoreInteractions(mMockReceiver);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testImeCommitContent_withReceiver_resultBoolean() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Configure the receiver to a custom impl that supports all text and images.
-        when(mMockReceiver.getSupportedMimeTypes()).thenReturn(ALL_TEXT_AND_IMAGE_MIME_TYPES);
-        mEditText.setRichContentReceiverCompat(mMockReceiver);
-
-        // Trigger the IME's commitContent() call, once when the mock receiver is configured to
-        // return true and once when the mock receiver is configured to return false.
-        when(mMockReceiver.onReceive(eq(mEditText), any(ClipData.class), eq(SOURCE_INPUT_METHOD),
-                eq(0))).thenReturn(true);
-        boolean result1 = triggerImeCommitContentViaCompat("image/png");
-        when(mMockReceiver.onReceive(eq(mEditText), any(ClipData.class), eq(SOURCE_INPUT_METHOD),
-                eq(0))).thenReturn(false);
-        boolean result2 = triggerImeCommitContentViaCompat("image/png");
-        verify(mMockReceiver, times(2)).onReceive(
-                eq(mEditText), any(ClipData.class), eq(SOURCE_INPUT_METHOD), eq(0));
-        if (Build.VERSION.SDK_INT >= 25) {
-            // On SDK 25 and above, the boolean result should match the return value from the
-            // receiver.
-            assertThat(result1).isTrue();
-            assertThat(result2).isFalse();
-        } else {
-            // On SDK 24 and below, commitContent() is handled via
-            // InputConnection.performPrivateCommand(). This ends up returning true whenever the
-            // command is sent, regardless of the return value of the underlying operation.
-            // Relevant code links:
-            // https://osscs.corp.google.com/androidx/platform/frameworks/support/+/androidx-master-dev:core/core/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java;l=294;drc=0c365e84832f5ec5e393be28ab1c618eb18bab1e
-            // https://cs.android.com/android/platform/superproject/+/android-7.0.0_r6:frameworks/base/core/java/com/android/internal/widget/EditableInputConnection.java;l=168
-            assertThat(result1).isTrue();
-            assertThat(result2).isTrue();
-        }
-    }
-
-    @UiThreadTest
-    @Test
-    public void testImeCommitContent_withReceiver_unsupportedMimeType() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Configure the receiver to a custom impl that supports all text and images.
-        when(mMockReceiver.getSupportedMimeTypes()).thenReturn(ALL_TEXT_AND_IMAGE_MIME_TYPES);
-        mEditText.setRichContentReceiverCompat(mMockReceiver);
-
-        // Trigger the IME's commitContent() call and assert that the custom receiver was not
-        // executed. This is because InputConnectionCompat.commitContent() checks the supported MIME
-        // types before proceeding.
-        triggerImeCommitContentViaCompat("video/mp4");
-        verify(mMockReceiver, times(1)).getSupportedMimeTypes();
-        verifyNoMoreInteractions(mMockReceiver);
-    }
-
-    @SdkSuppress(minSdkVersion = 25) // InputConnection.commitContent() was added in SDK 25.
-    @UiThreadTest
-    @Test
-    public void testImeCommitContent_direct_withReceiver_unsupportedMimeType() throws Exception {
-        setTextAndCursor("xz", 1);
-
-        // Setup: Configure the receiver to a custom impl that supports all text and images.
-        when(mMockReceiver.getSupportedMimeTypes()).thenReturn(ALL_TEXT_AND_IMAGE_MIME_TYPES);
-        mEditText.setRichContentReceiverCompat(mMockReceiver);
-
-        // Trigger the IME's commitContent() call and assert that the custom receiver was executed.
-        triggerImeCommitContentDirect("video/mp4");
-        verify(mMockReceiver, times(1)).getSupportedMimeTypes();
-        verify(mMockReceiver, times(1)).onReceive(
-                eq(mEditText), any(ClipData.class), eq(SOURCE_INPUT_METHOD), eq(0));
-        verifyNoMoreInteractions(mMockReceiver);
-    }
-
-    private boolean triggerContextMenuAction(final int actionId) {
-        return mEditText.onTextContextMenuItem(actionId);
-    }
-
-    private boolean triggerImeCommitContentViaCompat(String mimeType) {
-        final InputContentInfoCompat contentInfo = new InputContentInfoCompat(
-                Uri.parse("content://com.example/path"),
-                new ClipDescription("from test", new String[]{mimeType}),
-                Uri.parse("https://example.com"));
-        EditorInfo editorInfo = new EditorInfo();
-        InputConnection ic = mEditText.onCreateInputConnection(editorInfo);
-        return InputConnectionCompat.commitContent(ic, editorInfo, contentInfo, 0, null);
-    }
-
-    private boolean triggerImeCommitContentDirect(String mimeType) {
-        final InputContentInfo contentInfo = new InputContentInfo(
-                Uri.parse("content://com.example/path"),
-                new ClipDescription("from test", new String[]{mimeType}),
-                Uri.parse("https://example.com"));
-        EditorInfo editorInfo = new EditorInfo();
-        InputConnection ic = mEditText.onCreateInputConnection(editorInfo);
-        return ic.commitContent(contentInfo, 0, null);
-    }
-
-    private void setTextAndCursor(final String text, final int cursorPosition) {
-        mEditText.requestFocus();
-        SpannableStringBuilder ssb = new SpannableStringBuilder(text);
-        mEditText.setText(ssb);
-        mEditText.setSelection(cursorPosition);
-        assertThat(mEditText.hasFocus()).isTrue();
-        assertTextAndCursorPosition(text, cursorPosition);
-    }
-
-    private void assertTextAndCursorPosition(String expectedText, int cursorPosition) {
-        assertThat(mEditText.getText().toString()).isEqualTo(expectedText);
-        assertThat(mEditText.getSelectionStart()).isEqualTo(cursorPosition);
-        assertThat(mEditText.getSelectionEnd()).isEqualTo(cursorPosition);
-    }
-
-    private ClipData copyToClipboard(final ClipData clip) {
-        mClipboardManager.setPrimaryClip(clip);
-        ClipData primaryClip = mClipboardManager.getPrimaryClip();
-        assertThat(primaryClip).isNotNull();
-        return primaryClip;
-    }
-
-    private static ClipData clipEq(ClipData expected) {
-        return argThat(new ClipDataArgumentMatcher(expected));
-    }
-
-    private static class ClipDataArgumentMatcher implements ArgumentMatcher<ClipData> {
-        private final ClipData mExpected;
-
-        private ClipDataArgumentMatcher(ClipData expected) {
-            this.mExpected = expected;
-        }
-
-        @Override
-        public boolean matches(ClipData actual) {
-            return mExpected.getItemAt(0).getText().equals(actual.getItemAt(0).getText());
-        }
-    }
-}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/SwitchCompatTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/SwitchCompatTest.java
index 63d04a8..92aba86 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/SwitchCompatTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/SwitchCompatTest.java
@@ -24,8 +24,10 @@
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import android.graphics.Typeface;
+import android.os.Build;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
 
@@ -87,21 +89,37 @@
         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
         switchButton.onInitializeAccessibilityNodeInfo(info);
         assertEquals("android.widget.Switch", info.getClassName());
-        assertEquals(mActivity.getResources().getString(R.string.sample_text1), info.getText());
-        assertEquals(
-                mActivity.getResources().getString(androidx.appcompat.R.string.abc_capital_off),
-                ViewCompat.getStateDescription(switchButton)
-        );
+        final String capitalOff =
+                mActivity.getResources().getString(androidx.appcompat.R.string.abc_capital_off);
+        final String text = mActivity.getResources().getString(R.string.sample_text1);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            assertEquals(text + " " + capitalOff, info.getText());
+            assertNull(ViewCompat.getStateDescription(switchButton));
+        } else {
+            assertEquals(text, info.getText());
+            assertEquals(capitalOff, ViewCompat.getStateDescription(switchButton));
+        }
+        info.recycle();
     }
 
     @Test
     public void testAccessibility_textOnOff() {
         final SwitchCompat switchButton = mContainer.findViewById(R.id.switch_textOnOff);
+        final CharSequence textOn = "testStateOn";
+        final CharSequence textOff = "testStateOff";
         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
         switchButton.onInitializeAccessibilityNodeInfo(info);
         assertEquals("android.widget.Switch", info.getClassName());
-        assertEquals(mActivity.getResources().getString(R.string.sample_text1), info.getText());
-        assertEquals("testStateOff", ViewCompat.getStateDescription(switchButton));
+        final String text = mActivity.getResources().getString(R.string.sample_text1);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            assertEquals(text + " " + textOff, info.getText());
+            assertNull(ViewCompat.getStateDescription(switchButton));
+        } else {
+            assertEquals(text, info.getText());
+            assertEquals(textOff, ViewCompat.getStateDescription(switchButton));
+        }
+        info.recycle();
+
         final CharSequence newTextOff = "new text off";
         final CharSequence newTextOn = "new text on";
         mActivity.runOnUiThread(
@@ -109,12 +127,41 @@
                     @Override
                     public void run() {
                         switchButton.toggle();
-                        assertEquals("testStateOn", ViewCompat.getStateDescription(switchButton));
-                        switchButton.setTextOff(newTextOff);
+                        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+                        switchButton.onInitializeAccessibilityNodeInfo(info);
+                        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+                            assertEquals(text + " " + textOn, info.getText());
+                            assertNull(ViewCompat.getStateDescription(switchButton));
+                        } else {
+                            assertEquals(text, info.getText());
+                            assertEquals(textOn, ViewCompat.getStateDescription(switchButton));
+                        }
+                        info.recycle();
+
                         switchButton.setTextOn(newTextOn);
-                        assertEquals(newTextOn, ViewCompat.getStateDescription(switchButton));
+                        switchButton.setTextOff(newTextOff);
+                        info = AccessibilityNodeInfo.obtain();
+                        switchButton.onInitializeAccessibilityNodeInfo(info);
+                        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+                            assertEquals(text + " " + newTextOn, info.getText());
+                            assertNull(ViewCompat.getStateDescription(switchButton));
+                        } else {
+                            assertEquals(text, info.getText());
+                            assertEquals(newTextOn, ViewCompat.getStateDescription(switchButton));
+                        }
+                        info.recycle();
+
                         switchButton.toggle();
-                        assertEquals(newTextOff, ViewCompat.getStateDescription(switchButton));
+                        info = AccessibilityNodeInfo.obtain();
+                        switchButton.onInitializeAccessibilityNodeInfo(info);
+                        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+                            assertEquals(text + " " + newTextOff, info.getText());
+                            assertNull(ViewCompat.getStateDescription(switchButton));
+                        } else {
+                            assertEquals(text, info.getText());
+                            assertEquals(newTextOff, ViewCompat.getStateDescription(switchButton));
+                        }
+                        info.recycle();
                     }
                 }
         );
diff --git a/appcompat/appcompat/src/androidTest/res/layout/appcompat_edittext_richcontentreceiver_activity.xml b/appcompat/appcompat/src/androidTest/res/layout/appcompat_edittext_receive_content_activity.xml
similarity index 95%
rename from appcompat/appcompat/src/androidTest/res/layout/appcompat_edittext_richcontentreceiver_activity.xml
rename to appcompat/appcompat/src/androidTest/res/layout/appcompat_edittext_receive_content_activity.xml
index b1c4421..c1706b6 100644
--- a/appcompat/appcompat/src/androidTest/res/layout/appcompat_edittext_richcontentreceiver_activity.xml
+++ b/appcompat/appcompat/src/androidTest/res/layout/appcompat_edittext_receive_content_activity.xml
@@ -28,7 +28,7 @@
         android:orientation="vertical">
 
         <androidx.appcompat.widget.AppCompatEditText
-            android:id="@+id/edit_text_default_values"
+            android:id="@+id/edit_text"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"/>
     </LinearLayout>
diff --git a/appcompat/appcompat/src/androidTest/res/values/strings.xml b/appcompat/appcompat/src/androidTest/res/values/strings.xml
index 19145b8..563bc0e 100644
--- a/appcompat/appcompat/src/androidTest/res/values/strings.xml
+++ b/appcompat/appcompat/src/androidTest/res/values/strings.xml
@@ -57,7 +57,9 @@
     <string name="app_compat_text_view_activity">AppCompat text view</string>
     <string name="app_compat_text_view_auto_size_activity">AppCompat text view auto-size</string>
     <string name="app_compat_edit_text_activity">AppCompat edit text</string>
-    <string name="app_compat_edit_text_rich_content_receiver_activity">AppCompat edit text rich content receiver</string>
+    <string name="app_compat_edit_text_receive_content_activity">
+        AppCompat edit text receive content activity
+    </string>
     <string name="app_compat_button_auto_size_activity">AppCompat button auto-size</string>
     <string name="sample_text1">Sample text 1</string>
     <string name="sample_text2">Sample text 2</string>
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java
index 30e4e6f..6a9e587 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java
@@ -17,8 +17,10 @@
 package androidx.appcompat.widget;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
-import static androidx.core.widget.RichContentReceiverCompat.FLAG_CONVERT_TO_PLAIN_TEXT;
-import static androidx.core.widget.RichContentReceiverCompat.SOURCE_CLIPBOARD;
+import static androidx.core.view.ContentInfoCompat.Builder;
+import static androidx.core.view.ContentInfoCompat.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static androidx.core.view.ContentInfoCompat.SOURCE_CLIPBOARD;
+import static androidx.core.view.ContentInfoCompat.SOURCE_INPUT_METHOD;
 
 import android.content.ClipData;
 import android.content.ClipboardManager;
@@ -27,9 +29,13 @@
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.os.Bundle;
 import android.text.Editable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.ActionMode;
+import android.view.DragEvent;
+import android.view.View;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.textclassifier.TextClassifier;
@@ -42,10 +48,17 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
+import androidx.core.view.ContentInfoCompat;
+import androidx.core.view.OnReceiveContentListener;
+import androidx.core.view.OnReceiveContentViewBehavior;
 import androidx.core.view.TintableBackgroundView;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.inputmethod.EditorInfoCompat;
 import androidx.core.view.inputmethod.InputConnectionCompat;
-import androidx.core.widget.RichContentReceiverCompat;
+import androidx.core.view.inputmethod.InputConnectionCompat.OnCommitContentListener;
+import androidx.core.view.inputmethod.InputContentInfoCompat;
 import androidx.core.widget.TextViewCompat;
+import androidx.core.widget.TextViewOnReceiveContentListener;
 
 /**
  * A {@link EditText} which supports compatible features on older versions of the platform,
@@ -55,8 +68,8 @@
  *     {@link androidx.core.view.ViewCompat}.</li>
  *     <li>Allows setting of the background tint using {@link R.attr#backgroundTint} and
  *     {@link R.attr#backgroundTintMode}.</li>
- *     <li>Allows setting a custom {@link RichContentReceiverCompat receiver callback} in order to
- *     handle insertion of content (e.g. pasting text or an image from the clipboard). This callback
+ *     <li>Allows setting a custom {@link OnReceiveContentListener listener} to handle
+ *     insertion of content (e.g. pasting text or an image from the clipboard). This listener
  *     provides the opportunity to implement app-specific handling such as creating an attachment
  *     when an image is pasted.</li>
  * </ul>
@@ -66,13 +79,15 @@
  * <a href="{@docRoot}topic/libraries/support-library/packages.html#v7-appcompat">appcompat</a>.
  * You should only need to manually use this class when writing custom views.</p>
  */
-public class AppCompatEditText extends EditText implements TintableBackgroundView {
+public class AppCompatEditText extends EditText implements TintableBackgroundView,
+        OnReceiveContentViewBehavior {
+    private static final String LOG_TAG = "AppCompatEditText";
 
     private final AppCompatBackgroundHelper mBackgroundTintHelper;
     private final AppCompatTextHelper mTextHelper;
     private final AppCompatTextClassifierHelper mTextClassifierHelper;
-    @Nullable
-    private RichContentReceiverCompat<TextView> mRichContentReceiverCompat;
+    private final TextViewOnReceiveContentListener mDefaultOnReceiveContentListener;
+    private final AppCompatEditor mEditor;
 
     public AppCompatEditText(@NonNull Context context) {
         this(context, null);
@@ -96,6 +111,10 @@
         mTextHelper.applyCompoundDrawablesTints();
 
         mTextClassifierHelper = new AppCompatTextClassifierHelper(this);
+
+        mDefaultOnReceiveContentListener = new TextViewOnReceiveContentListener();
+
+        mEditor = new AppCompatEditor(this);
     }
 
     /**
@@ -204,26 +223,62 @@
     }
 
     /**
-     * If a {@link #setRichContentReceiverCompat receiver callback} is set, the returned
+     * If a {@link ViewCompat#setOnReceiveContentListener listener is set}, the returned
      * {@link InputConnection} will use it to handle calls to {@link InputConnection#commitContent}.
      *
      * {@inheritDoc}
      */
+    @Nullable
     @Override
     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
         InputConnection ic = super.onCreateInputConnection(outAttrs);
         mTextHelper.populateSurroundingTextIfNeeded(this, ic, outAttrs);
         ic = AppCompatHintHelper.onCreateInputConnection(ic, outAttrs, this);
-        if (ic != null && mRichContentReceiverCompat != null) {
-            mRichContentReceiverCompat.populateEditorInfoContentMimeTypes(ic, outAttrs);
-            InputConnectionCompat.OnCommitContentListener callback =
-                    mRichContentReceiverCompat.buildOnCommitContentListener(this);
-            ic = InputConnectionCompat.createWrapper(ic, outAttrs, callback);
+
+        String[] mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this);
+        if (ic != null && mimeTypes != null) {
+            EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes);
+            OnCommitContentListener onCommitContentListener = buildOnCommitContentListener(this);
+            ic = InputConnectionCompat.createWrapper(ic, outAttrs, onCommitContentListener);
         }
         return ic;
     }
 
     /**
+     * Creates an {@link InputConnectionCompat.OnCommitContentListener} that uses
+     * {@link ViewCompat#performReceiveContent} to insert content. The listener returned by this
+     * function should be passed to {@link InputConnectionCompat#createWrapper} when creating the
+     * {@link InputConnection} in {@link View#onCreateInputConnection}.
+     */
+    // TODO(b/150318135): Generalize/extract this so it can be reused for other widgets
+    @NonNull
+    private static InputConnectionCompat.OnCommitContentListener buildOnCommitContentListener(
+            @NonNull final View view) {
+        return new InputConnectionCompat.OnCommitContentListener() {
+            @Override
+            public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags,
+                    Bundle opts) {
+                if ((flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
+                    try {
+                        inputContentInfo.requestPermission();
+                    } catch (Exception e) {
+                        Log.w(LOG_TAG,
+                                "Can't insert content from IME; requestPermission() failed", e);
+                        return false;
+                    }
+                }
+                ClipData clip = new ClipData(inputContentInfo.getDescription(),
+                        new ClipData.Item(inputContentInfo.getContentUri()));
+                ContentInfoCompat payload = new ContentInfoCompat.Builder(clip, SOURCE_INPUT_METHOD)
+                        .setLinkUri(inputContentInfo.getLinkUri())
+                        .setExtras(opts)
+                        .build();
+                return ViewCompat.performReceiveContent(view, payload) == null;
+            }
+        };
+    }
+
+    /**
      * See
      * {@link TextViewCompat#setCustomSelectionActionModeCallback(TextView, ActionMode.Callback)}
      */
@@ -263,24 +318,32 @@
         return mTextClassifierHelper.getTextClassifier();
     }
 
+    @Override
+    public boolean onDragEvent(@SuppressWarnings("MissingNullability") DragEvent event) {
+        if (mEditor.onDragEvent(event)) {
+            return true;
+        }
+        return super.onDragEvent(event);
+    }
+
     /**
-     * If a {@link #setRichContentReceiverCompat receiver callback} is set, uses it to execute the
+     * If a {@link ViewCompat#setOnReceiveContentListener listener is set}, uses it to execute the
      * "Paste" and "Paste as plain text" menu actions.
      *
      * {@inheritDoc}
      */
     @Override
     public boolean onTextContextMenuItem(int id) {
-        if (mRichContentReceiverCompat == null) {
-            return super.onTextContextMenuItem(id);
-        }
-        if (id == android.R.id.paste || id == android.R.id.pasteAsPlainText) {
+        if (ViewCompat.getOnReceiveContentMimeTypes(this) != null
+                && (id == android.R.id.paste || id == android.R.id.pasteAsPlainText)) {
             ClipboardManager cm = (ClipboardManager) getContext().getSystemService(
                     Context.CLIPBOARD_SERVICE);
-            ClipData clip = cm == null ? null : cm.getPrimaryClip();
+            ClipData clip = (cm == null) ? null : cm.getPrimaryClip();
             if (clip != null) {
-                int flags = (id == android.R.id.paste) ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT;
-                mRichContentReceiverCompat.onReceive(this, clip, SOURCE_CLIPBOARD, flags);
+                ContentInfoCompat payload =  new Builder(clip, SOURCE_CLIPBOARD)
+                        .setFlags((id == android.R.id.paste) ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT)
+                        .build();
+                ViewCompat.performReceiveContent(this, payload);
             }
             return true;
         }
@@ -288,39 +351,23 @@
     }
 
     /**
-     * Returns the callback that handles insertion of content into this view (e.g. pasting from
-     * the clipboard). See {@link #setRichContentReceiverCompat} for more info.
+     * Implements the default behavior for receiving content, which coerces all content to text
+     * and inserts into the view.
      *
-     * @return The callback that this view is using to handle insertion of content. Returns
-     * {@code null} if no callback is configured, in which case the platform behavior of the
-     * {@link EditText} component will be used for content insertion.
+     * <p>Subclasses of this widget can override this method to customize the default behavior
+     * for receiving content. Apps wishing to provide custom behavior for receiving content
+     * should set a listener via {@link ViewCompat#setOnReceiveContentListener}.
+     *
+     * <p>See {@link ViewCompat#performReceiveContent} for more info.
+     *
+     * @param payload The content to insert and related metadata.
+     *
+     * @return The portion of the passed-in content that was not handled (may be all, some, or none
+     * of the passed-in content).
      */
     @Nullable
-    public RichContentReceiverCompat<TextView> getRichContentReceiverCompat() {
-        return mRichContentReceiverCompat;
-    }
-
-    /**
-     * Sets the callback to handle insertion of content into this view.
-     *
-     * <p>"Content" and "rich content" here refers to both text and non-text: plain text, styled
-     * text, HTML, images, videos, audio files, etc. The callback configured here should typically
-     * extend from {@link androidx.core.widget.TextViewRichContentReceiverCompat} to provide
-     * consistent behavior for text content.
-     *
-     * <p>This callback will be invoked for the following scenarios:
-     * <ol>
-     *     <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the
-     *     insertion/selection menu)
-     *     <li>Content insertion from the keyboard ({@link InputConnection#commitContent})
-     * </ol>
-     *
-     * @param receiver The callback to use. This can be {@code null} to clear any previously set
-     *                 callback (the platform behavior of the {@link EditText} component will then
-     *                 be used).
-     */
-    public void setRichContentReceiverCompat(
-            @Nullable RichContentReceiverCompat<TextView> receiver) {
-        mRichContentReceiverCompat = receiver;
+    @Override
+    public ContentInfoCompat onReceiveContent(@NonNull ContentInfoCompat payload) {
+        return mDefaultOnReceiveContentListener.onReceiveContent(this, payload);
     }
 }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditor.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditor.java
new file mode 100644
index 0000000..d5686b2
--- /dev/null
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditor.java
@@ -0,0 +1,100 @@
+/*
+ * 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.appcompat.widget;
+
+import static androidx.core.view.ContentInfoCompat.SOURCE_DRAG_AND_DROP;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.os.Build;
+import android.text.Selection;
+import android.text.Spannable;
+import android.util.Log;
+import android.view.DragEvent;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.view.ContentInfoCompat;
+import androidx.core.view.ViewCompat;
+
+class AppCompatEditor {
+    private static final String LOG_TAG = "AppCompatEditor";
+
+    @NonNull
+    private final TextView mTextView;
+
+    AppCompatEditor(@NonNull TextView textView) {
+        mTextView = textView;
+    }
+
+    public boolean onDragEvent(@NonNull DragEvent event) {
+        if (Build.VERSION.SDK_INT < 24
+                || event.getAction() != DragEvent.ACTION_DROP
+                || event.getLocalState() != null
+                || ViewCompat.getOnReceiveContentMimeTypes(mTextView) == null) {
+            return false;
+        }
+        // We make a best effort to find the activity for this view by unwrapping the
+        // context (common case). If we are not able to find it, we skip calling the
+        // OnReceiveContentListener and delegate to the default behavior. Apps can always implement
+        // custom drop handling by overriding onDragEvent() or setting an OnDragListener.
+        final Activity activity = getActivity();
+        if (activity == null) {
+            Log.i(LOG_TAG, "No activity so not calling performReceiveContent: " + mTextView);
+            return false;
+        }
+        return Api24Impl.onDrop(event, mTextView, activity);
+    }
+
+    @Nullable
+    private Activity getActivity() {
+        Context context = mTextView.getContext();
+        while (context instanceof ContextWrapper) {
+            if (context instanceof Activity) {
+                return (Activity) context;
+            }
+            context = ((ContextWrapper) context).getBaseContext();
+        }
+        return null;
+    }
+
+    @RequiresApi(24) // For Activity.requestDragAndDropPermissions()
+    private static final class Api24Impl {
+        private Api24Impl() {}
+
+        static boolean onDrop(@NonNull DragEvent event,
+                @NonNull TextView textView, @NonNull Activity activity) {
+            final int offset = textView.getOffsetForPosition(event.getX(), event.getY());
+            activity.requestDragAndDropPermissions(event);
+            textView.beginBatchEdit();
+            try {
+                Selection.setSelection((Spannable) textView.getText(), offset);
+                final ClipData clip = event.getClipData();
+                final ContentInfoCompat payload =
+                        new ContentInfoCompat.Builder(clip, SOURCE_DRAG_AND_DROP).build();
+                ViewCompat.performReceiveContent(textView, payload);
+            } finally {
+                textView.endBatchEdit();
+            }
+            return true;
+        }
+    }
+}
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/SwitchCompat.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/SwitchCompat.java
index 5e25a81..f4d72a52 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/SwitchCompat.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/SwitchCompat.java
@@ -775,9 +775,11 @@
     public void setTextOn(CharSequence textOn) {
         mTextOn = textOn;
         requestLayout();
-        // Default state is derived from on/off-text, so state has to be updated when on/off-text
-        // are updated.
-        setOnStateDescription();
+        if (isChecked()) {
+            // Default state is derived from on/off-text, so state has to be updated when
+            // on/off-text are updated.
+            setOnStateDescriptionOnRAndAbove();
+        }
     }
 
     /**
@@ -797,9 +799,11 @@
     public void setTextOff(CharSequence textOff) {
         mTextOff = textOff;
         requestLayout();
-        // Default state is derived from on/off-text, so state has to be updated when on/off-text
-        // are updated.
-        setOffStateDescription();
+        if (!isChecked()) {
+            // Default state is derived from on/off-text, so state has to be updated when
+            // on/off-text are updated.
+            setOffStateDescriptionOnRAndAbove();
+        }
     }
 
     /**
@@ -1095,9 +1099,9 @@
         checked = isChecked();
 
         if (checked) {
-            setOnStateDescription();
+            setOnStateDescriptionOnRAndAbove();
         } else {
-            setOffStateDescription();
+            setOffStateDescriptionOnRAndAbove();
         }
 
         if (getWindowToken() != null && ViewCompat.isLaidOut(this)) {
@@ -1433,6 +1437,19 @@
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(ACCESSIBILITY_EVENT_CLASS_NAME);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            CharSequence switchText = isChecked() ? mTextOn : mTextOff;
+            if (!TextUtils.isEmpty(switchText)) {
+                CharSequence oldText = info.getText();
+                if (TextUtils.isEmpty(oldText)) {
+                    info.setText(switchText);
+                } else {
+                    StringBuilder newText = new StringBuilder();
+                    newText.append(oldText).append(' ').append(switchText);
+                    info.setText(newText);
+                }
+            }
+        }
     }
 
     /**
@@ -1452,17 +1469,21 @@
         return amount < low ? low : (amount > high ? high : amount);
     }
 
-    private void setOnStateDescription() {
-        ViewCompat.setStateDescription(
-                this,
-                mTextOn == null ? getResources().getString(R.string.abc_capital_on) : mTextOn
-        );
+    private void setOnStateDescriptionOnRAndAbove() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            ViewCompat.setStateDescription(
+                    this,
+                    mTextOn == null ? getResources().getString(R.string.abc_capital_on) : mTextOn
+            );
+        }
     }
 
-    private void setOffStateDescription() {
-        ViewCompat.setStateDescription(
-                this,
-                mTextOff == null ? getResources().getString(R.string.abc_capital_off) : mTextOff
-        );
+    private void setOffStateDescriptionOnRAndAbove() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            ViewCompat.setStateDescription(
+                    this,
+                    mTextOff == null ? getResources().getString(R.string.abc_capital_off) : mTextOff
+            );
+        }
     }
 }
diff --git a/appsearch/appsearch/api/current.txt b/appsearch/appsearch/api/current.txt
index 32a70b4..e1899f6 100644
--- a/appsearch/appsearch/api/current.txt
+++ b/appsearch/appsearch/api/current.txt
@@ -171,6 +171,12 @@
     method public androidx.appsearch.app.SearchResults query(String, androidx.appsearch.app.SearchSpec);
   }
 
+  public class PackageIdentifier {
+    ctor public PackageIdentifier(String, byte[]);
+    method public String getPackageName();
+    method public byte[] getSha256Certificate();
+  }
+
   public final class PutDocumentsRequest {
     method public java.util.List<androidx.appsearch.app.GenericDocument!> getDocuments();
   }
@@ -260,17 +266,23 @@
 
   public final class SetSchemaRequest {
     method public java.util.Set<androidx.appsearch.app.AppSearchSchema!> getSchemas();
+    method public java.util.Set<java.lang.String!> getSchemasNotVisibleToSystemUi();
+    method public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.PackageIdentifier!>!> getSchemasVisibleToPackages();
     method public boolean isForceOverride();
   }
 
   public static final class SetSchemaRequest.Builder {
     ctor public SetSchemaRequest.Builder();
     method public androidx.appsearch.app.SetSchemaRequest.Builder addDataClass(Class<?>!...) throws androidx.appsearch.exceptions.AppSearchException;
-    method public androidx.appsearch.app.SetSchemaRequest.Builder addDataClass(java.util.Collection<java.lang.Class<?>!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.SetSchemaRequest.Builder addDataClass(java.util.Collection<? extends java.lang.Class<?>>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder addSchema(androidx.appsearch.app.AppSearchSchema!...);
     method public androidx.appsearch.app.SetSchemaRequest.Builder addSchema(java.util.Collection<androidx.appsearch.app.AppSearchSchema!>);
     method public androidx.appsearch.app.SetSchemaRequest build();
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setDataClassVisibilityForPackage(Class<?>, boolean, androidx.appsearch.app.PackageIdentifier) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setDataClassVisibilityForSystemUi(Class<?>, boolean) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder setForceOverride(boolean);
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(String, boolean, androidx.appsearch.app.PackageIdentifier);
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(String, boolean);
   }
 
 }
diff --git a/appsearch/appsearch/api/public_plus_experimental_current.txt b/appsearch/appsearch/api/public_plus_experimental_current.txt
index 32a70b4..e1899f6 100644
--- a/appsearch/appsearch/api/public_plus_experimental_current.txt
+++ b/appsearch/appsearch/api/public_plus_experimental_current.txt
@@ -171,6 +171,12 @@
     method public androidx.appsearch.app.SearchResults query(String, androidx.appsearch.app.SearchSpec);
   }
 
+  public class PackageIdentifier {
+    ctor public PackageIdentifier(String, byte[]);
+    method public String getPackageName();
+    method public byte[] getSha256Certificate();
+  }
+
   public final class PutDocumentsRequest {
     method public java.util.List<androidx.appsearch.app.GenericDocument!> getDocuments();
   }
@@ -260,17 +266,23 @@
 
   public final class SetSchemaRequest {
     method public java.util.Set<androidx.appsearch.app.AppSearchSchema!> getSchemas();
+    method public java.util.Set<java.lang.String!> getSchemasNotVisibleToSystemUi();
+    method public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.PackageIdentifier!>!> getSchemasVisibleToPackages();
     method public boolean isForceOverride();
   }
 
   public static final class SetSchemaRequest.Builder {
     ctor public SetSchemaRequest.Builder();
     method public androidx.appsearch.app.SetSchemaRequest.Builder addDataClass(Class<?>!...) throws androidx.appsearch.exceptions.AppSearchException;
-    method public androidx.appsearch.app.SetSchemaRequest.Builder addDataClass(java.util.Collection<java.lang.Class<?>!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.SetSchemaRequest.Builder addDataClass(java.util.Collection<? extends java.lang.Class<?>>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder addSchema(androidx.appsearch.app.AppSearchSchema!...);
     method public androidx.appsearch.app.SetSchemaRequest.Builder addSchema(java.util.Collection<androidx.appsearch.app.AppSearchSchema!>);
     method public androidx.appsearch.app.SetSchemaRequest build();
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setDataClassVisibilityForPackage(Class<?>, boolean, androidx.appsearch.app.PackageIdentifier) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setDataClassVisibilityForSystemUi(Class<?>, boolean) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder setForceOverride(boolean);
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(String, boolean, androidx.appsearch.app.PackageIdentifier);
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(String, boolean);
   }
 
 }
diff --git a/appsearch/appsearch/api/restricted_current.txt b/appsearch/appsearch/api/restricted_current.txt
index 32a70b4..e1899f6 100644
--- a/appsearch/appsearch/api/restricted_current.txt
+++ b/appsearch/appsearch/api/restricted_current.txt
@@ -171,6 +171,12 @@
     method public androidx.appsearch.app.SearchResults query(String, androidx.appsearch.app.SearchSpec);
   }
 
+  public class PackageIdentifier {
+    ctor public PackageIdentifier(String, byte[]);
+    method public String getPackageName();
+    method public byte[] getSha256Certificate();
+  }
+
   public final class PutDocumentsRequest {
     method public java.util.List<androidx.appsearch.app.GenericDocument!> getDocuments();
   }
@@ -260,17 +266,23 @@
 
   public final class SetSchemaRequest {
     method public java.util.Set<androidx.appsearch.app.AppSearchSchema!> getSchemas();
+    method public java.util.Set<java.lang.String!> getSchemasNotVisibleToSystemUi();
+    method public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.PackageIdentifier!>!> getSchemasVisibleToPackages();
     method public boolean isForceOverride();
   }
 
   public static final class SetSchemaRequest.Builder {
     ctor public SetSchemaRequest.Builder();
     method public androidx.appsearch.app.SetSchemaRequest.Builder addDataClass(Class<?>!...) throws androidx.appsearch.exceptions.AppSearchException;
-    method public androidx.appsearch.app.SetSchemaRequest.Builder addDataClass(java.util.Collection<java.lang.Class<?>!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.SetSchemaRequest.Builder addDataClass(java.util.Collection<? extends java.lang.Class<?>>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder addSchema(androidx.appsearch.app.AppSearchSchema!...);
     method public androidx.appsearch.app.SetSchemaRequest.Builder addSchema(java.util.Collection<androidx.appsearch.app.AppSearchSchema!>);
     method public androidx.appsearch.app.SetSchemaRequest build();
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setDataClassVisibilityForPackage(Class<?>, boolean, androidx.appsearch.app.PackageIdentifier) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setDataClassVisibilityForSystemUi(Class<?>, boolean) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder setForceOverride(boolean);
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(String, boolean, androidx.appsearch.app.PackageIdentifier);
+    method public androidx.appsearch.app.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(String, boolean);
   }
 
 }
diff --git a/appsearch/appsearch/build.gradle b/appsearch/appsearch/build.gradle
index 907de5a..085f8ad 100644
--- a/appsearch/appsearch/build.gradle
+++ b/appsearch/appsearch/build.gradle
@@ -56,8 +56,8 @@
     def suffix = name.capitalize()
     project.tasks.create(name: "jar${suffix}", type: Jar) {
         dependsOn variant.javaCompileProvider.get()
-        from variant.javaCompileProvider.get().destinationDir
-        destinationDir new File(project.buildDir, "libJar")
+        from variant.javaCompileProvider.get().destinationDirectory
+        destinationDirectory.set(new File(project.buildDir, "libJar"))
     }
 }
 
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 6edc6c1..338f2a8 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
@@ -30,6 +30,7 @@
 import androidx.appsearch.localstorage.LocalStorage;
 import androidx.test.core.app.ApplicationProvider;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -39,22 +40,36 @@
 
 public class AnnotationProcessorTest {
     private AppSearchSession mSession;
+    private static final String DB_NAME_1 = LocalStorage.DEFAULT_DATABASE_NAME;
 
     @Before
     public void setUp() throws Exception {
         Context context = ApplicationProvider.getApplicationContext();
+
         mSession = checkIsResultSuccess(LocalStorage.createSearchSession(
                 new LocalStorage.SearchContext.Builder(context)
-                        .setDatabaseName("testDb1").build()));
+                        .setDatabaseName(DB_NAME_1).build()));
 
-        // Remove all documents from any instances that may have been created in the tests.
-        checkIsResultSuccess(
-                mSession.setSchema(new SetSchemaRequest.Builder().setForceOverride(true).build()));
+        // Cleanup whatever documents may still exist in these databases. This is needed in
+        // addition to tearDown in case a test exited without completing properly.
+        cleanup();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Cleanup whatever documents may still exist in these databases.
+        cleanup();
+    }
+
+    private void cleanup() throws Exception {
+        checkIsResultSuccess(mSession.setSchema(
+                new SetSchemaRequest.Builder().setForceOverride(true).build()));
     }
 
     @AppSearchDocument
     static class Card {
-        @AppSearchDocument.Uri String mUri;
+        @AppSearchDocument.Uri
+        String mUri;
         @AppSearchDocument.Property
                 (indexingType = INDEXING_TYPE_PREFIXES, tokenizerType = TOKENIZER_TYPE_PLAIN)
         String mString;        // 3a
@@ -75,48 +90,84 @@
 
     @AppSearchDocument
     static class Gift {
-        @AppSearchDocument.Uri String mUri;
+        @AppSearchDocument.Uri
+        String mUri;
 
         // Collections
-        @AppSearchDocument.Property Collection<Long> mCollectLong;         // 1a
-        @AppSearchDocument.Property Collection<Integer> mCollectInteger;   // 1a
-        @AppSearchDocument.Property Collection<Double> mCollectDouble;     // 1a
-        @AppSearchDocument.Property Collection<Float> mCollectFloat;       // 1a
-        @AppSearchDocument.Property Collection<Boolean> mCollectBoolean;   // 1a
-        @AppSearchDocument.Property Collection<byte[]> mCollectByteArr;    // 1a
-        @AppSearchDocument.Property Collection<String> mCollectString;     // 1b
-        @AppSearchDocument.Property Collection<Card> mCollectCard;         // 1c
+        @AppSearchDocument.Property
+        Collection<Long> mCollectLong;         // 1a
+        @AppSearchDocument.Property
+        Collection<Integer> mCollectInteger;   // 1a
+        @AppSearchDocument.Property
+        Collection<Double> mCollectDouble;     // 1a
+        @AppSearchDocument.Property
+        Collection<Float> mCollectFloat;       // 1a
+        @AppSearchDocument.Property
+        Collection<Boolean> mCollectBoolean;   // 1a
+        @AppSearchDocument.Property
+        Collection<byte[]> mCollectByteArr;    // 1a
+        @AppSearchDocument.Property
+        Collection<String> mCollectString;     // 1b
+        @AppSearchDocument.Property
+        Collection<Card> mCollectCard;         // 1c
 
         // Arrays
-        @AppSearchDocument.Property Long[] mArrBoxLong;         // 2a
-        @AppSearchDocument.Property long[] mArrUnboxLong;       // 2b
-        @AppSearchDocument.Property Integer[] mArrBoxInteger;   // 2a
-        @AppSearchDocument.Property int[] mArrUnboxInt;         // 2a
-        @AppSearchDocument.Property Double[] mArrBoxDouble;     // 2a
-        @AppSearchDocument.Property double[] mArrUnboxDouble;   // 2b
-        @AppSearchDocument.Property Float[] mArrBoxFloat;       // 2a
-        @AppSearchDocument.Property float[] mArrUnboxFloat;     // 2a
-        @AppSearchDocument.Property Boolean[] mArrBoxBoolean;   // 2a
-        @AppSearchDocument.Property boolean[] mArrUnboxBoolean; // 2b
-        @AppSearchDocument.Property byte[][] mArrUnboxByteArr;  // 2b
-        @AppSearchDocument.Property Byte[] mBoxByteArr;         // 2a
-        @AppSearchDocument.Property String[] mArrString;        // 2b
-        @AppSearchDocument.Property Card[] mArrCard;            // 2c
+        @AppSearchDocument.Property
+        Long[] mArrBoxLong;         // 2a
+        @AppSearchDocument.Property
+        long[] mArrUnboxLong;       // 2b
+        @AppSearchDocument.Property
+        Integer[] mArrBoxInteger;   // 2a
+        @AppSearchDocument.Property
+        int[] mArrUnboxInt;         // 2a
+        @AppSearchDocument.Property
+        Double[] mArrBoxDouble;     // 2a
+        @AppSearchDocument.Property
+        double[] mArrUnboxDouble;   // 2b
+        @AppSearchDocument.Property
+        Float[] mArrBoxFloat;       // 2a
+        @AppSearchDocument.Property
+        float[] mArrUnboxFloat;     // 2a
+        @AppSearchDocument.Property
+        Boolean[] mArrBoxBoolean;   // 2a
+        @AppSearchDocument.Property
+        boolean[] mArrUnboxBoolean; // 2b
+        @AppSearchDocument.Property
+        byte[][] mArrUnboxByteArr;  // 2b
+        @AppSearchDocument.Property
+        Byte[] mBoxByteArr;         // 2a
+        @AppSearchDocument.Property
+        String[] mArrString;        // 2b
+        @AppSearchDocument.Property
+        Card[] mArrCard;            // 2c
 
         // Single values
-        @AppSearchDocument.Property String mString;        // 3a
-        @AppSearchDocument.Property Long mBoxLong;         // 3a
-        @AppSearchDocument.Property long mUnboxLong;       // 3b
-        @AppSearchDocument.Property Integer mBoxInteger;   // 3a
-        @AppSearchDocument.Property int mUnboxInt;         // 3b
-        @AppSearchDocument.Property Double mBoxDouble;     // 3a
-        @AppSearchDocument.Property double mUnboxDouble;   // 3b
-        @AppSearchDocument.Property Float mBoxFloat;       // 3a
-        @AppSearchDocument.Property float mUnboxFloat;     // 3b
-        @AppSearchDocument.Property Boolean mBoxBoolean;   // 3a
-        @AppSearchDocument.Property boolean mUnboxBoolean; // 3b
-        @AppSearchDocument.Property byte[] mUnboxByteArr;  // 3a
-        @AppSearchDocument.Property Card mCard;            // 3c
+        @AppSearchDocument.Property
+        String mString;        // 3a
+        @AppSearchDocument.Property
+        Long mBoxLong;         // 3a
+        @AppSearchDocument.Property
+        long mUnboxLong;       // 3b
+        @AppSearchDocument.Property
+        Integer mBoxInteger;   // 3a
+        @AppSearchDocument.Property
+        int mUnboxInt;         // 3b
+        @AppSearchDocument.Property
+        Double mBoxDouble;     // 3a
+        @AppSearchDocument.Property
+        double mUnboxDouble;   // 3b
+        @AppSearchDocument.Property
+        Float mBoxFloat;       // 3a
+        @AppSearchDocument.Property
+        float mUnboxFloat;     // 3b
+        @AppSearchDocument.Property
+        Boolean mBoxBoolean;   // 3a
+        @AppSearchDocument.Property
+        boolean mUnboxBoolean; // 3b
+        @AppSearchDocument.Property
+        byte[] mUnboxByteArr;  // 3a
+        @AppSearchDocument.Property
+        Card mCard;            // 3c
 
         @Override
         public boolean equals(Object other) {
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaRequestTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaRequestTest.java
index 99ac18e..db721f3 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaRequestTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaRequestTest.java
@@ -24,15 +24,25 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.appsearch.annotation.AppSearchDocument;
+import androidx.collection.ArrayMap;
+
+import com.google.common.collect.ImmutableSet;
 
 import org.junit.Test;
 
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
 public class SetSchemaRequestTest {
 
     @AppSearchDocument
     static class Card {
         @AppSearchDocument.Uri
         String mUri;
+
         @AppSearchDocument.Property
                 (indexingType = INDEXING_TYPE_PREFIXES, tokenizerType = TOKENIZER_TYPE_PLAIN)
         String mString;
@@ -51,62 +61,272 @@
         }
     }
 
+    static class Spade {}
+
+    @AppSearchDocument
+    static class King extends Spade {
+        @AppSearchDocument.Uri
+        String mUri;
+
+        @AppSearchDocument.Property
+                (indexingType = INDEXING_TYPE_PREFIXES, tokenizerType = TOKENIZER_TYPE_PLAIN)
+        String mString;
+    }
+
+    @AppSearchDocument
+    static class Queen extends Spade {
+        @AppSearchDocument.Uri
+        String mUri;
+
+        @AppSearchDocument.Property
+                (indexingType = INDEXING_TYPE_PREFIXES, tokenizerType = TOKENIZER_TYPE_PLAIN)
+        String mString;
+    }
+
+    private static Collection<String> getSchemaTypesFromSetSchemaRequest(SetSchemaRequest request) {
+        HashSet<String> schemaTypes = new HashSet<>();
+        for (AppSearchSchema schema : request.getSchemas()) {
+            schemaTypes.add(schema.getSchemaType());
+        }
+        return schemaTypes;
+    }
+
     @Test
-    public void testInvalidSchemaReferences() {
+    public void testInvalidSchemaReferences_fromSystemUiVisibility() {
         IllegalArgumentException expected = assertThrows(IllegalArgumentException.class,
-                () -> new SetSchemaRequest.Builder().setSchemaTypeVisibilityForSystemUi(false,
-                        "InvalidSchema").build());
+                () -> new SetSchemaRequest.Builder().setSchemaTypeVisibilityForSystemUi(
+                        "InvalidSchema", false).build());
         assertThat(expected).hasMessageThat().contains("referenced, but were not added");
     }
 
     @Test
-    public void testSchemaTypeVisibilityForSystemUi_Visible() {
+    public void testInvalidSchemaReferences_fromPackageVisibility() {
+        IllegalArgumentException expected = assertThrows(IllegalArgumentException.class,
+                () -> new SetSchemaRequest.Builder().setSchemaTypeVisibilityForPackage(
+                        "InvalidSchema", /*visible=*/ true, new PackageIdentifier(
+                                "com.foo.package", /*sha256Certificate=*/ new byte[]{})).build());
+        assertThat(expected).hasMessageThat().contains("referenced, but were not added");
+    }
+
+    @Test
+    public void testSchemaTypeVisibilityForSystemUi_visible() {
         AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
 
         // By default, the schema is visible.
         SetSchemaRequest request =
                 new SetSchemaRequest.Builder().addSchema(schema).build();
-        assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty();
+        assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
 
         request =
                 new SetSchemaRequest.Builder().addSchema(schema).setSchemaTypeVisibilityForSystemUi(
-                        true,
-                        "Schema").build();
-        assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty();
+                        "Schema", true).build();
+        assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
     }
 
     @Test
-    public void testSchemaTypeVisibilityForSystemUi_NotVisible() {
+    public void testSchemaTypeVisibilityForSystemUi_notVisible() {
         AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
         SetSchemaRequest request =
                 new SetSchemaRequest.Builder().addSchema(schema).setSchemaTypeVisibilityForSystemUi(
-                        false,
-                        "Schema").build();
-        assertThat(request.getSchemasNotPlatformSurfaceable()).containsExactly("Schema");
+                        "Schema", false).build();
+        assertThat(request.getSchemasNotVisibleToSystemUi()).containsExactly("Schema");
     }
 
     @Test
-    public void testDataClassVisibilityForSystemUi_Visible() throws Exception {
+    public void testDataClassVisibilityForSystemUi_visible() throws Exception {
         // By default, the schema is visible.
         SetSchemaRequest request =
                 new SetSchemaRequest.Builder().addDataClass(Card.class).build();
-        assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty();
+        assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
 
         request =
                 new SetSchemaRequest.Builder().addDataClass(
                         Card.class).setDataClassVisibilityForSystemUi(
-                        true,
-                        Card.class).build();
-        assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty();
+                        Card.class, true).build();
+        assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
     }
 
     @Test
-    public void testDataClassVisibilityForSystemUi_NotVisible() throws Exception {
+    public void testDataClassVisibilityForSystemUi_notVisible() throws Exception {
         SetSchemaRequest request =
                 new SetSchemaRequest.Builder().addDataClass(
                         Card.class).setDataClassVisibilityForSystemUi(
-                        false,
-                        Card.class).build();
-        assertThat(request.getSchemasNotPlatformSurfaceable()).containsExactly("Card");
+                        Card.class, false).build();
+        assertThat(request.getSchemasNotVisibleToSystemUi()).containsExactly("Card");
+    }
+
+    @Test
+    public void testSchemaTypeVisibilityForPackage_visible() {
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+        // By default, the schema is not visible.
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addSchema(schema).build();
+        assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+
+        PackageIdentifier packageIdentifier = new PackageIdentifier("com.package.foo",
+                new byte[]{100});
+        Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>();
+        expectedVisibleToPackagesMap.put("Schema", Collections.singleton(packageIdentifier));
+
+        request =
+                new SetSchemaRequest.Builder().addSchema(schema).setSchemaTypeVisibilityForPackage(
+                        "Schema", /*visible=*/ true, packageIdentifier).build();
+        assertThat(request.getSchemasVisibleToPackages()).containsExactlyEntriesIn(
+                expectedVisibleToPackagesMap);
+    }
+
+    @Test
+    public void testSchemaTypeVisibilityForPackage_notVisible() {
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addSchema(schema).setSchemaTypeVisibilityForPackage(
+                        "Schema", /*visible=*/ false, new PackageIdentifier("com.package.foo",
+                                /*sha256Certificate=*/ new byte[]{})).build();
+        assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+    }
+
+    @Test
+    public void testSchemaTypeVisibilityForPackage_deduped() throws Exception {
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+        PackageIdentifier packageIdentifier = new PackageIdentifier("com.package.foo",
+                new byte[]{100});
+        Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>();
+        expectedVisibleToPackagesMap.put("Schema", Collections.singleton(packageIdentifier));
+
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder()
+                        .addSchema(schema)
+                        // Set it visible for "Schema"
+                        .setSchemaTypeVisibilityForPackage("Schema", /*visible=*/
+                                true, packageIdentifier)
+                        // Set it visible for "Schema" again, which should be a no-op
+                        .setSchemaTypeVisibilityForPackage("Schema", /*visible=*/
+                                true, packageIdentifier)
+                        .build();
+        assertThat(request.getSchemasVisibleToPackages()).containsExactlyEntriesIn(
+                expectedVisibleToPackagesMap);
+    }
+
+    @Test
+    public void testSchemaTypeVisibilityForPackage_removed() throws Exception {
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder()
+                        .addSchema(schema)
+                        // First set it as visible
+                        .setSchemaTypeVisibilityForPackage("Schema", /*visible=*/
+                                true, new PackageIdentifier("com.package.foo",
+                                        /*sha256Certificate=*/ new byte[]{100}))
+                        // Then make it not visible
+                        .setSchemaTypeVisibilityForPackage("Schema", /*visible=*/
+                                false, new PackageIdentifier("com.package.foo",
+                                        /*sha256Certificate=*/ new byte[]{100}))
+                        .build();
+
+        // Nothing should be visible.
+        assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+    }
+
+    @Test
+    public void testDataClassVisibilityForPackage_visible() throws Exception {
+        // By default, the schema is not visible.
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addDataClass(Card.class).build();
+        assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+
+        PackageIdentifier packageIdentifier = new PackageIdentifier("com.package.foo",
+                new byte[]{100});
+        Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>();
+        expectedVisibleToPackagesMap.put("Card", Collections.singleton(packageIdentifier));
+
+        request =
+                new SetSchemaRequest.Builder().addDataClass(
+                        Card.class).setDataClassVisibilityForPackage(
+                        Card.class, /*visible=*/ true, packageIdentifier).build();
+        assertThat(request.getSchemasVisibleToPackages()).containsExactlyEntriesIn(
+                expectedVisibleToPackagesMap);
+    }
+
+    @Test
+    public void testDataClassVisibilityForPackage_notVisible() throws Exception {
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addDataClass(
+                        Card.class).setDataClassVisibilityForPackage(
+                        Card.class, /*visible=*/ false,
+                        new PackageIdentifier("com.package.foo", /*sha256Certificate=*/
+                                new byte[]{})).build();
+        assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+    }
+
+    @Test
+    public void testDataClassVisibilityForPackage_deduped() throws Exception {
+        // By default, the schema is not visible.
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addDataClass(Card.class).build();
+        assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+
+        PackageIdentifier packageIdentifier = new PackageIdentifier("com.package.foo",
+                new byte[]{100});
+        Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>();
+        expectedVisibleToPackagesMap.put("Card", Collections.singleton(packageIdentifier));
+
+        request =
+                new SetSchemaRequest.Builder()
+                        .addDataClass(Card.class)
+                        .setDataClassVisibilityForPackage(Card.class, /*visible=*/
+                                true, packageIdentifier)
+                        .setDataClassVisibilityForPackage(Card.class, /*visible=*/
+                                true, packageIdentifier)
+                        .build();
+        assertThat(request.getSchemasVisibleToPackages()).containsExactlyEntriesIn(
+                expectedVisibleToPackagesMap);
+    }
+
+    @Test
+    public void testDataClassVisibilityForPackage_removed() throws Exception {
+        // By default, the schema is not visible.
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addDataClass(Card.class).build();
+        assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+
+        request =
+                new SetSchemaRequest.Builder()
+                        .addDataClass(Card.class)
+                        // First set it as visible
+                        .setDataClassVisibilityForPackage(Card.class, /*visible=*/
+                                true, new PackageIdentifier("com.package.foo",
+                                        /*sha256Certificate=*/ new byte[]{100}))
+                        // Then make it not visible
+                        .setDataClassVisibilityForPackage(Card.class, /*visible=*/
+                                false, new PackageIdentifier("com.package.foo",
+                                        /*sha256Certificate=*/ new byte[]{100}))
+                        .build();
+
+        // Nothing should be visible.
+        assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+    }
+
+    @Test
+    public void testAddDataClass_byCollection() throws Exception {
+        Set<Class<? extends Spade>> cardClasses = ImmutableSet.of(Queen.class, King.class);
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addDataClass(cardClasses)
+                        .build();
+        assertThat(getSchemaTypesFromSetSchemaRequest(request)).containsExactly("Queen",
+                "King");
+    }
+
+    @Test
+    public void testAddDataClass_byCollectionWithSeparateCalls() throws
+            Exception {
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addDataClass(ImmutableSet.of(Queen.class))
+                        .addDataClass(ImmutableSet.of(King.class)).build();
+        assertThat(getSchemaTypesFromSetSchemaRequest(request)).containsExactly("Queen",
+                "King");
     }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTest.java
index 91af24c..32738eb 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTest.java
@@ -43,6 +43,7 @@
 import androidx.appsearch.localstorage.LocalStorage;
 import androidx.test.core.app.ApplicationProvider;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -53,23 +54,37 @@
 
 public class AppSearchSessionCtsTest {
     private AppSearchSession mDb1;
+    private static final String DB_NAME_1 = LocalStorage.DEFAULT_DATABASE_NAME;
     private AppSearchSession mDb2;
+    private static final String DB_NAME_2 = "testDb2";
 
     @Before
     public void setUp() throws Exception {
         Context context = ApplicationProvider.getApplicationContext();
+
         mDb1 = checkIsResultSuccess(LocalStorage.createSearchSession(
                 new LocalStorage.SearchContext.Builder(context)
-                        .setDatabaseName("testDb1").build()));
+                        .setDatabaseName(DB_NAME_1).build()));
         mDb2 = checkIsResultSuccess(LocalStorage.createSearchSession(
                 new LocalStorage.SearchContext.Builder(context)
-                        .setDatabaseName("testDb2").build()));
+                        .setDatabaseName(DB_NAME_2).build()));
 
-        // Remove all documents from any instances that may have been created in the tests.
-        checkIsResultSuccess(
-                mDb1.setSchema(new SetSchemaRequest.Builder().setForceOverride(true).build()));
-        checkIsResultSuccess(
-                mDb2.setSchema(new SetSchemaRequest.Builder().setForceOverride(true).build()));
+        // Cleanup whatever documents may still exist in these databases. This is needed in
+        // addition to tearDown in case a test exited without completing properly.
+        cleanup();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Cleanup whatever documents may still exist in these databases.
+        cleanup();
+    }
+
+    private void cleanup() throws Exception {
+        checkIsResultSuccess(mDb1.setSchema(
+                new SetSchemaRequest.Builder().setForceOverride(true).build()));
+        checkIsResultSuccess(mDb2.setSchema(
+                new SetSchemaRequest.Builder().setForceOverride(true).build()));
     }
 
     @Test
@@ -284,7 +299,8 @@
         assertThat(failResult1.isSuccess()).isFalse();
         assertThat(failResult1.getErrorMessage()).contains("Schema is incompatible");
         assertThat(failResult1.getErrorMessage())
-                .contains("Deleted types: [testDb1/builtin:Email]");
+                .contains(
+                        "Deleted types: [androidx.appsearch.test$" + DB_NAME_1 + "/builtin:Email]");
 
         // Try to remove the email schema again, which should now work as we set forceOverride to
         // be true.
@@ -307,10 +323,11 @@
                 .setSubject("testPut example")
                 .build();
         AppSearchBatchResult<String, Void> failResult2 = mDb1.putDocuments(
-                        new PutDocumentsRequest.Builder().addGenericDocument(email2).build()).get();
+                new PutDocumentsRequest.Builder().addGenericDocument(email2).build()).get();
         assertThat(failResult2.isSuccess()).isFalse();
         assertThat(failResult2.getFailures().get("email2").getErrorMessage())
-                .isEqualTo("Schema type config 'testDb1/builtin:Email' not found");
+                .isEqualTo("Schema type config 'androidx.appsearch.test$" + DB_NAME_1
+                        + "/builtin:Email' not found");
     }
 
     @Test
@@ -363,7 +380,8 @@
         assertThat(failResult1.isSuccess()).isFalse();
         assertThat(failResult1.getErrorMessage()).contains("Schema is incompatible");
         assertThat(failResult1.getErrorMessage())
-                .contains("Deleted types: [testDb1/builtin:Email]");
+                .contains(
+                        "Deleted types: [androidx.appsearch.test$" + DB_NAME_1 + "/builtin:Email]");
 
         // Try to remove the email schema again, which should now work as we set forceOverride to
         // be true.
@@ -387,7 +405,8 @@
                 new PutDocumentsRequest.Builder().addGenericDocument(email3).build()).get();
         assertThat(failResult2.isSuccess()).isFalse();
         assertThat(failResult2.getFailures().get("email3").getErrorMessage())
-                .isEqualTo("Schema type config 'testDb1/builtin:Email' not found");
+                .isEqualTo("Schema type config 'androidx.appsearch.test$" + DB_NAME_1
+                        + "/builtin:Email' not found");
 
         // Make sure email in database 2 still present.
         outDocuments = doGet(mDb2, GenericDocument.DEFAULT_NAMESPACE, "email2");
@@ -698,7 +717,7 @@
                 new GenericDocument.Builder<>("uri", "Generic")
                         .setNamespace("document")
                         .setPropertyString("subject", "A commonly used fake word is foo. "
-                                        + "Another nonsense word that’s used a lot is bar")
+                                + "Another nonsense word that’s used a lot is bar")
                         .build();
         checkIsBatchResultSuccess(mDb1.putDocuments(
                 new PutDocumentsRequest.Builder().addGenericDocument(document).build()));
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/GlobalSearchSessionCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/GlobalSearchSessionCtsTest.java
index cc1a8bf..40a6a60 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/GlobalSearchSessionCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/GlobalSearchSessionCtsTest.java
@@ -38,42 +38,88 @@
 import androidx.appsearch.localstorage.LocalStorage;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.google.common.collect.ImmutableList;
+
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 
 public class GlobalSearchSessionCtsTest {
     private AppSearchSession mDb1;
+    private static final String DB_NAME_1 = LocalStorage.DEFAULT_DATABASE_NAME;
     private AppSearchSession mDb2;
+    private static final String DB_NAME_2 = "testDb2";
 
     private GlobalSearchSession mGlobalAppSearchManager;
 
     @Before
     public void setUp() throws Exception {
         Context context = ApplicationProvider.getApplicationContext();
+
         mDb1 = checkIsResultSuccess(LocalStorage.createSearchSession(
                 new LocalStorage.SearchContext.Builder(context)
-                        .setDatabaseName("testDb1").build()));
+                        .setDatabaseName(DB_NAME_1).build()));
         mDb2 = checkIsResultSuccess(LocalStorage.createSearchSession(
                 new LocalStorage.SearchContext.Builder(context)
-                        .setDatabaseName("testDb2").build()));
+                        .setDatabaseName(DB_NAME_2).build()));
+
+        // Cleanup whatever documents may still exist in these databases. This is needed in
+        // addition to tearDown in case a test exited without completing properly.
+        cleanup();
 
         mGlobalAppSearchManager = checkIsResultSuccess(LocalStorage.createGlobalSearchSession(
                 new LocalStorage.GlobalSearchContext.Builder(context).build()));
+    }
 
-        // Remove all documents from any instances that may have been created in the tests.
-        checkIsResultSuccess(
-                mDb1.setSchema(new SetSchemaRequest.Builder().setForceOverride(true).build()));
-        checkIsResultSuccess(
-                mDb2.setSchema(new SetSchemaRequest.Builder().setForceOverride(true).build()));
+    @After
+    public void tearDown() throws Exception {
+        // Cleanup whatever documents may still exist in these databases.
+        cleanup();
+    }
+
+    private void cleanup() throws Exception {
+        checkIsResultSuccess(mDb1.setSchema(
+                new SetSchemaRequest.Builder().setForceOverride(true).build()));
+        checkIsResultSuccess(mDb2.setSchema(
+                new SetSchemaRequest.Builder().setForceOverride(true).build()));
+    }
+
+    private List<GenericDocument> snapshotResults(String queryExpression, SearchSpec spec)
+            throws Exception {
+        SearchResults searchResults = mGlobalAppSearchManager.query(queryExpression, spec);
+        return convertSearchResultsToDocuments(searchResults);
+    }
+
+    /**
+     * Asserts that the union of {@code addedDocuments} and {@code beforeDocuments} is exactly
+     * equivalent to {@code afterDocuments}. Order doesn't matter.
+     *
+     * @param beforeDocuments Documents that existed first.
+     * @param afterDocuments  The total collection of documents that should exist now.
+     * @param addedDocuments  The collection of documents that were expected to be added.
+     */
+    private void assertAddedBetweenSnapshots(List<? extends GenericDocument> beforeDocuments,
+            List<? extends GenericDocument> afterDocuments,
+            List<? extends GenericDocument> addedDocuments) throws Exception {
+        List<GenericDocument> expectedDocuments = new ArrayList<>(beforeDocuments);
+        expectedDocuments.addAll(addedDocuments);
+        assertThat(afterDocuments).containsExactlyElementsIn(expectedDocuments);
     }
 
     @Test
     public void testGlobalQuery_oneInstance() throws Exception {
+        // Snapshot what documents may already exist on the device.
+        SearchSpec exactSearchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .build();
+        List<GenericDocument> beforeBodyDocuments = snapshotResults("body", exactSearchSpec);
+        List<GenericDocument> beforeBodyEmailDocuments = snapshotResults("body email",
+                exactSearchSpec);
+
         // Schema registration
         checkIsResultSuccess(mDb1.setSchema(
                 new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build()));
@@ -90,24 +136,25 @@
                 new PutDocumentsRequest.Builder().addGenericDocument(inEmail).build()));
 
         // Query for the document
-        SearchResults searchResults = mGlobalAppSearchManager.query("body",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).hasSize(1);
-        assertThat(documents).containsExactly(inEmail);
+        List<GenericDocument> afterBodyDocuments = snapshotResults("body", exactSearchSpec);
+        assertAddedBetweenSnapshots(beforeBodyDocuments, afterBodyDocuments,
+                Collections.singletonList(inEmail));
 
         // Multi-term query
-        searchResults = mGlobalAppSearchManager.query("body email", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inEmail);
+        List<GenericDocument> afterBodyEmailDocuments = snapshotResults("body email",
+                exactSearchSpec);
+        assertAddedBetweenSnapshots(beforeBodyEmailDocuments, afterBodyEmailDocuments,
+                Collections.singletonList(inEmail));
     }
 
     @Test
     public void testGlobalQuery_twoInstances() throws Exception {
+        // Snapshot what documents may already exist on the device.
+        SearchSpec exactSearchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .build();
+        List<GenericDocument> beforeBodyDocuments = snapshotResults("body", exactSearchSpec);
+
         // Schema registration
         checkIsResultSuccess(mDb1.setSchema(new SetSchemaRequest.Builder()
                 .addSchema(AppSearchEmail.SCHEMA).build()));
@@ -137,20 +184,23 @@
                 new PutDocumentsRequest.Builder().addGenericDocument(inEmail2).build()));
 
         // Query across all instances
-        SearchResults searchResults = mGlobalAppSearchManager.query("body",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inEmail1, inEmail2);
+        List<GenericDocument> afterBodyDocuments = snapshotResults("body", exactSearchSpec);
+        assertAddedBetweenSnapshots(beforeBodyDocuments, afterBodyDocuments,
+                ImmutableList.of(inEmail1, inEmail2));
     }
 
     @Test
     public void testGlobalQuery_getNextPage() throws Exception {
+        // Snapshot what documents may already exist on the device.
+        SearchSpec exactSearchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .build();
+        List<GenericDocument> beforeBodyDocuments = snapshotResults("body", exactSearchSpec);
+
         // Schema registration
         checkIsResultSuccess(mDb1.setSchema(
                 new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build()));
-        Set<AppSearchEmail> emailSet = new HashSet<>();
+        List<AppSearchEmail> emailList = new ArrayList<>();
         PutDocumentsRequest.Builder putDocumentsRequestBuilder = new PutDocumentsRequest.Builder();
 
         // Index 31 documents
@@ -162,16 +212,17 @@
                             .setSubject("testPut example")
                             .setBody("This is the body of the testPut email")
                             .build();
-            emailSet.add(inEmail);
+            emailList.add(inEmail);
             putDocumentsRequestBuilder.addGenericDocument(inEmail);
         }
         checkIsBatchResultSuccess(mDb1.putDocuments(putDocumentsRequestBuilder.build()));
 
         // Set number of results per page is 7.
+        int pageSize = 7;
         SearchResults searchResults = mGlobalAppSearchManager.query("body",
                 new SearchSpec.Builder()
                         .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .setResultCountPerPage(7)
+                        .setResultCountPerPage(pageSize)
                         .build());
         List<GenericDocument> documents = new ArrayList<>();
 
@@ -188,12 +239,31 @@
         } while (results.size() > 0);
 
         // check all document presents
-        assertThat(documents).containsExactlyElementsIn(emailSet);
-        assertThat(pageNumber).isEqualTo(6); // 5 (upper(31/7)) + 1 (final empty page)
+        assertAddedBetweenSnapshots(beforeBodyDocuments, documents, emailList);
+
+        int totalDocuments = beforeBodyDocuments.size() + documents.size();
+
+        // +1 for final empty page
+        int expectedPages = (int) Math.ceil(totalDocuments * 1.0 / pageSize) + 1;
+        assertThat(pageNumber).isEqualTo(expectedPages);
     }
 
     @Test
     public void testGlobalQuery_acrossTypes() throws Exception {
+        // Snapshot what documents may already exist on the device.
+        SearchSpec exactSearchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .build();
+        List<GenericDocument> beforeBodyDocuments = snapshotResults("body", exactSearchSpec);
+
+        SearchSpec exactEmailSearchSpec =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                        .addSchemaType(AppSearchEmail.SCHEMA_TYPE)
+                        .build();
+        List<GenericDocument> beforeBodyEmailDocuments = snapshotResults("body",
+                exactEmailSearchSpec);
+
         // Schema registration
         AppSearchSchema genericSchema = new AppSearchSchema.Builder("Generic")
                 .addProperty(new PropertyConfig.Builder("foo")
@@ -235,24 +305,33 @@
                 new PutDocumentsRequest.Builder().addGenericDocument(email).build()));
 
         // Query for all documents across types
-        SearchResults searchResults = mGlobalAppSearchManager.query("body",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(genericDocument, email, email);
+        List<GenericDocument> afterBodyDocuments = snapshotResults("body", exactSearchSpec);
+        assertAddedBetweenSnapshots(beforeBodyDocuments, afterBodyDocuments,
+                ImmutableList.of(genericDocument, email, email));
 
         // Query only for email documents
-        searchResults = mGlobalAppSearchManager.query("body", new SearchSpec.Builder()
-                .addSchemaType(AppSearchEmail.SCHEMA_TYPE)
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(email, email);
+        List<GenericDocument> afterBodyEmailDocuments = snapshotResults("body",
+                exactEmailSearchSpec);
+        assertAddedBetweenSnapshots(beforeBodyEmailDocuments, afterBodyEmailDocuments,
+                ImmutableList.of(email, email));
     }
 
     @Test
     public void testGlobalQuery_namespaceFilter() throws Exception {
+        // Snapshot what documents may already exist on the device.
+        SearchSpec exactSearchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .build();
+        List<GenericDocument> beforeBodyDocuments = snapshotResults("body", exactSearchSpec);
+
+        SearchSpec exactNamespace1SearchSpec =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                        .addNamespace("namespace1")
+                        .build();
+        List<GenericDocument> beforeBodyNamespace1Documents = snapshotResults("body",
+                exactNamespace1SearchSpec);
+
         // Schema registration
         checkIsResultSuccess(mDb1.setSchema(new SetSchemaRequest.Builder()
                 .addSchema(AppSearchEmail.SCHEMA).build()));
@@ -284,20 +363,14 @@
                 new PutDocumentsRequest.Builder().addGenericDocument(document2).build()));
 
         // Query for all namespaces
-        SearchResults searchResults = mGlobalAppSearchManager.query("body",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(document1, document2);
+        List<GenericDocument> afterBodyDocuments = snapshotResults("body", exactSearchSpec);
+        assertAddedBetweenSnapshots(beforeBodyDocuments, afterBodyDocuments,
+                ImmutableList.of(document1, document2));
 
         // Query only for "namespace1"
-        searchResults = mGlobalAppSearchManager.query("body",
-                new SearchSpec.Builder()
-                        .addNamespace("namespace1")
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(document1);
+        List<GenericDocument> afterBodyNamespace1Documents = snapshotResults("body",
+                exactNamespace1SearchSpec);
+        assertAddedBetweenSnapshots(beforeBodyNamespace1Documents, afterBodyNamespace1Documents,
+                ImmutableList.of(document1));
     }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/util/BundleUtilTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/util/BundleUtilTest.java
index 3830b9d..389c3ee 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/util/BundleUtilTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/util/BundleUtilTest.java
@@ -39,7 +39,6 @@
     public void testDeepEquals_self() {
         Bundle one = new Bundle();
         one.putString("a", "a");
-        assertThat(one).isEqualTo(one);
         assertThat(BundleUtil.deepEquals(one, one)).isTrue();
     }
 
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 ffc73249..0a46469 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
@@ -192,11 +192,12 @@
 
     /**
      * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they
-     * match the query expression in given namespaces and schemaTypes.
+     * match the {@code queryExpression} in given namespaces and schemaTypes which is set via
+     * {@link SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchemaType}.
      *
-     * <p> An empty query matches all documents.
+     * <p> An empty {@code queryExpression} matches all documents.
      *
-     * <p> An empty set of namespaces or of schemaTypes matches all namespaces or schemaTypes in
+     * <p> An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in
      * the current database.
      *
      * @param queryExpression Query String to search.
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/PackageIdentifier.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/PackageIdentifier.java
new file mode 100644
index 0000000..17d6fae
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/PackageIdentifier.java
@@ -0,0 +1,68 @@
+/*
+ * 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.app;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.ObjectsCompat;
+import androidx.core.util.Preconditions;
+
+import java.util.Arrays;
+
+/** This class represents a uniquely identifiable package. */
+public class PackageIdentifier {
+    private final String mPackageName;
+    private final byte[] mSha256Certificate;
+
+    /**
+     * Creates a unique identifier for a package.
+     *
+     * @param packageName Name of the package.
+     * @param sha256Certificate SHA256 certificate digest of the package.
+     */
+    public PackageIdentifier(@NonNull String packageName, @NonNull byte[] sha256Certificate) {
+        mPackageName = Preconditions.checkNotNull(packageName);
+        mSha256Certificate = Preconditions.checkNotNull(sha256Certificate);
+    }
+
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @NonNull
+    public byte[] getSha256Certificate() {
+        return mSha256Certificate;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || !(obj instanceof PackageIdentifier)) {
+            return false;
+        }
+        final PackageIdentifier other = (PackageIdentifier) obj;
+        return this.mPackageName.equals(other.mPackageName)
+                && Arrays.equals(this.mSha256Certificate, other.mSha256Certificate);
+    }
+
+    @Override
+    public int hashCode() {
+        return ObjectsCompat.hash(mPackageName, Arrays.hashCode(mSha256Certificate));
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByUriRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByUriRequest.java
index 4093c8b..ed7cad9 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByUriRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByUriRequest.java
@@ -45,7 +45,7 @@
         return mNamespace;
     }
 
-    /** Returns the URIs to remove from the namespace. */
+    /** Returns the URIs of documents to remove from the namespace. */
     @NonNull
     public Set<String> getUris() {
         return Collections.unmodifiableSet(mUris);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java
index a918106..9f65a80 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java
@@ -21,6 +21,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 
@@ -29,6 +30,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -38,13 +40,17 @@
  */
 public final class SetSchemaRequest {
     private final Set<AppSearchSchema> mSchemas;
-    private final Set<String> mSchemasNotPlatformSurfaceable;
+    private final Set<String> mSchemasNotVisibleToSystemUi;
+    private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages;
     private final boolean mForceOverride;
 
     SetSchemaRequest(@NonNull Set<AppSearchSchema> schemas,
-            @NonNull Set<String> schemasNotPlatformSurfaceable, boolean forceOverride) {
+            @NonNull Set<String> schemasNotVisibleToSystemUi,
+            @NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages,
+            boolean forceOverride) {
         mSchemas = Preconditions.checkNotNull(schemas);
-        mSchemasNotPlatformSurfaceable = Preconditions.checkNotNull(schemasNotPlatformSurfaceable);
+        mSchemasNotVisibleToSystemUi = Preconditions.checkNotNull(schemasNotVisibleToSystemUi);
+        mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages);
         mForceOverride = forceOverride;
     }
 
@@ -56,13 +62,43 @@
 
     /**
      * Returns the set of schema types that have opted out of being visible on system UI surfaces.
+     */
+    @NonNull
+    public Set<String> getSchemasNotVisibleToSystemUi() {
+        return Collections.unmodifiableSet(mSchemasNotVisibleToSystemUi);
+    }
+
+    /**
+     * Returns a mapping of schema types to the set of packages that have access
+     * to that schema type. Each package is represented by a {@link PackageIdentifier}.
+     * name and byte[] certificate.
+     *
+     * This method is inefficient to call repeatedly.
+     */
+    @NonNull
+    public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackages() {
+        Map<String, Set<PackageIdentifier>> copy = new ArrayMap<>();
+        for (String key : mSchemasVisibleToPackages.keySet()) {
+            copy.put(key, new ArraySet<>(mSchemasVisibleToPackages.get(key)));
+        }
+        return copy;
+    }
+
+    /**
+     * Returns a mapping of schema types to the set of packages that have access
+     * to that schema type. Each package is represented by a {@link PackageIdentifier}.
+     * name and byte[] certificate.
+     *
+     * A more efficient version of {@link #getSchemasVisibleToPackages}, but it returns a
+     * modifiable map. This is not meant to be unhidden and should only be used by internal
+     * classes.
      *
      * @hide
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @NonNull
-    public Set<String> getSchemasNotPlatformSurfaceable() {
-        return Collections.unmodifiableSet(mSchemasNotPlatformSurfaceable);
+    public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackagesInternal() {
+        return mSchemasVisibleToPackages;
     }
 
     /** Returns whether this request will force the schema to be overridden. */
@@ -73,7 +109,9 @@
     /** Builder for {@link SetSchemaRequest} objects. */
     public static final class Builder {
         private final Set<AppSearchSchema> mSchemas = new ArraySet<>();
-        private final Set<String> mSchemasNotPlatformSurfaceable = new ArraySet<>();
+        private final Set<String> mSchemasNotVisibleToSystemUi = new ArraySet<>();
+        private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
+                new ArrayMap<>();
         private boolean mForceOverride = false;
         private boolean mBuilt = false;
 
@@ -131,7 +169,7 @@
          */
         @SuppressLint("MissingGetterMatchingBuilder")  // Merged list available from getSchemas()
         @NonNull
-        public Builder addDataClass(@NonNull Collection<Class<?>> dataClasses)
+        public Builder addDataClass(@NonNull Collection<? extends Class<?>> dataClasses)
                 throws AppSearchException {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             Preconditions.checkNotNull(dataClasses);
@@ -145,78 +183,112 @@
         }
 
         /**
-         * Sets visibility on system UI surfaces for schema types.
+         * Sets visibility on system UI surfaces for the given {@code schemaType}.
          *
-         * @hide
+         * @param schemaType The schema type to set visibility on.
+         * @param visible    Whether the {@code schemaType} will be visible or not.
          */
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        // Merged list available from getSchemasNotVisibleToSystemUi
+        @SuppressLint("MissingGetterMatchingBuilder")
         @NonNull
-        public Builder setSchemaTypeVisibilityForSystemUi(boolean visible,
-                @NonNull String... schemaTypes) {
-            Preconditions.checkNotNull(schemaTypes);
-            return this.setSchemaTypeVisibilityForSystemUi(visible, Arrays.asList(schemaTypes));
-        }
-
-        /**
-         * Sets visibility on system UI surfaces for schema types.
-         *
-         * @hide
-         */
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-        @NonNull
-        public Builder setSchemaTypeVisibilityForSystemUi(boolean visible,
-                @NonNull Collection<String> schemaTypes) {
+        public Builder setSchemaTypeVisibilityForSystemUi(@NonNull String schemaType,
+                boolean visible) {
+            Preconditions.checkNotNull(schemaType);
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkNotNull(schemaTypes);
+
             if (visible) {
-                mSchemasNotPlatformSurfaceable.removeAll(schemaTypes);
+                mSchemasNotVisibleToSystemUi.remove(schemaType);
             } else {
-                mSchemasNotPlatformSurfaceable.addAll(schemaTypes);
+                mSchemasNotVisibleToSystemUi.add(schemaType);
             }
             return this;
         }
 
         /**
-         * Sets visibility on system UI surfaces for schema types.
+         * Sets visibility for a package for the given {@code schemaType}.
          *
-         * @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler}
-         *                            has not generated a schema for the given data classes.
-         * @hide
+         * @param schemaType        The schema type to set visibility on.
+         * @param visible           Whether the {@code schemaType} will be visible or not.
+         * @param packageIdentifier Represents the package that will be granted visibility.
          */
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        // Merged list available from getSchemasVisibleToPackages
+        @SuppressLint("MissingGetterMatchingBuilder")
         @NonNull
-        public Builder setDataClassVisibilityForSystemUi(boolean visible,
-                @NonNull Class<?>... dataClasses) throws AppSearchException {
-            Preconditions.checkNotNull(dataClasses);
-            return setDataClassVisibilityForSystemUi(visible, Arrays.asList(dataClasses));
-        }
-
-        /**
-         * Sets visibility on system UI surfaces for schema types.
-         *
-         * @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler}
-         *                            has not generated a schema for the given data classes.
-         * @hide
-         */
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-        @NonNull
-        public Builder setDataClassVisibilityForSystemUi(boolean visible,
-                @NonNull Collection<Class<?>> dataClasses) throws AppSearchException {
+        public Builder setSchemaTypeVisibilityForPackage(@NonNull String schemaType,
+                boolean visible, @NonNull PackageIdentifier packageIdentifier) {
+            Preconditions.checkNotNull(schemaType);
+            Preconditions.checkNotNull(packageIdentifier);
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkNotNull(dataClasses);
-            DataClassFactoryRegistry registry = DataClassFactoryRegistry.getInstance();
-            for (Class<?> dataClass : dataClasses) {
-                DataClassFactory<?> factory = registry.getOrCreateFactory(dataClass);
-                if (visible) {
-                    mSchemasNotPlatformSurfaceable.remove(factory.getSchemaType());
-                } else {
-                    mSchemasNotPlatformSurfaceable.add(factory.getSchemaType());
+
+            Set<PackageIdentifier> packageIdentifiers =
+                    mSchemasVisibleToPackages.get(schemaType);
+            if (visible) {
+                if (packageIdentifiers == null) {
+                    packageIdentifiers = new ArraySet<>();
+                }
+                packageIdentifiers.add(packageIdentifier);
+                mSchemasVisibleToPackages.put(schemaType, packageIdentifiers);
+            } else {
+                if (packageIdentifiers == null) {
+                    // Return early since there was nothing set to begin with.
+                    return this;
+                }
+                packageIdentifiers.remove(packageIdentifier);
+                if (packageIdentifiers.isEmpty()) {
+                    // Remove the entire key so that we don't have empty sets as values.
+                    mSchemasVisibleToPackages.remove(schemaType);
                 }
             }
+
             return this;
         }
 
         /**
+         * Sets visibility on system UI surfaces for the given {@code dataClass}.
+         *
+         * @param dataClass The schema to set visibility on.
+         * @param visible   Whether the {@code schemaType} will be visible or not.
+         * @return {@link SetSchemaRequest.Builder}
+         * @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler}
+         *                            has not generated a schema for the given data classes.
+         */
+        // Merged list available from getSchemasNotVisibleToSystemUi
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setDataClassVisibilityForSystemUi(@NonNull Class<?> dataClass,
+                boolean visible) throws AppSearchException {
+            Preconditions.checkNotNull(dataClass);
+
+            DataClassFactoryRegistry registry = DataClassFactoryRegistry.getInstance();
+            DataClassFactory<?> factory = registry.getOrCreateFactory(dataClass);
+            return setSchemaTypeVisibilityForSystemUi(factory.getSchemaType(), visible);
+        }
+
+        /**
+         * Sets visibility for a package for the given {@code dataClass}.
+         *
+         * @param dataClass         The schema to set visibility on.
+         * @param visible           Whether the {@code schemaType} will be visible or not.
+         * @param packageIdentifier Represents the package that will be granted visibility
+         * @return {@link SetSchemaRequest.Builder}
+         * @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler}
+         *                            has not generated a schema for the given data classes.
+         */
+        // Merged list available from getSchemasVisibleToPackages
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setDataClassVisibilityForPackage(@NonNull Class<?> dataClass,
+                boolean visible, @NonNull PackageIdentifier packageIdentifier)
+                throws AppSearchException {
+            Preconditions.checkNotNull(dataClass);
+
+            DataClassFactoryRegistry registry = DataClassFactoryRegistry.getInstance();
+            DataClassFactory<?> factory = registry.getOrCreateFactory(dataClass);
+            return setSchemaTypeVisibilityForPackage(factory.getSchemaType(), visible,
+                    packageIdentifier);
+        }
+
+        /**
          * Configures the {@link SetSchemaRequest} to delete any existing documents that don't
          * follow the new schema.
          *
@@ -244,20 +316,23 @@
 
             // Verify that any schema types with visibility settings refer to a real schema.
             // Create a copy because we're going to remove from the set for verification purposes.
-            Set<String> schemasNotPlatformSurfaceableCopy = new ArraySet<>(
-                    mSchemasNotPlatformSurfaceable);
+            Set<String> referencedSchemas = new ArraySet<>(
+                    mSchemasNotVisibleToSystemUi);
+            referencedSchemas.addAll(mSchemasVisibleToPackages.keySet());
+
             for (AppSearchSchema schema : mSchemas) {
-                schemasNotPlatformSurfaceableCopy.remove(schema.getSchemaType());
+                referencedSchemas.remove(schema.getSchemaType());
             }
-            if (!schemasNotPlatformSurfaceableCopy.isEmpty()) {
+            if (!referencedSchemas.isEmpty()) {
                 // We still have schema types that weren't seen in our mSchemas set. This means
                 // there wasn't a corresponding AppSearchSchema.
                 throw new IllegalArgumentException(
-                        "Schema types " + schemasNotPlatformSurfaceableCopy
+                        "Schema types " + referencedSchemas
                                 + " referenced, but were not added.");
             }
 
-            return new SetSchemaRequest(mSchemas, mSchemasNotPlatformSurfaceable,
+            return new SetSchemaRequest(mSchemas, mSchemasNotVisibleToSystemUi,
+                    mSchemasVisibleToPackages,
                     mForceOverride);
         }
     }
diff --git a/appsearch/exportToFramework.py b/appsearch/exportToFramework.py
index 33f9429..1a646e2 100755
--- a/appsearch/exportToFramework.py
+++ b/appsearch/exportToFramework.py
@@ -32,7 +32,7 @@
 JETPACK_IMPL_TEST_ROOT = 'local-storage/src/androidTest/java/androidx/appsearch'
 
 # Framework paths relative to frameworks/base/apex/appsearch
-FRAMEWORK_API_ROOT = 'framework/java/android/app/appsearch'
+FRAMEWORK_API_ROOT = 'framework/java/external/android/app/appsearch'
 FRAMEWORK_API_TEST_ROOT = (
         '../../core/tests/coretests/src/'
         'android/app/appsearch/external')
@@ -52,18 +52,10 @@
         self._jetpack_appsearch_root = jetpack_appsearch_root
         self._framework_appsearch_root = framework_appsearch_root
 
-    def _PruneDir(self, dir_to_prune, allow_list=None):
-        all_files = []
+    def _PruneDir(self, dir_to_prune):
         for walk_path, walk_folders, walk_files in os.walk(dir_to_prune):
             for walk_filename in walk_files:
                 abs_path = os.path.join(walk_path, walk_filename)
-                all_files.append(abs_path)
-
-        for abs_path in all_files:
-            rel_path = os.path.relpath(abs_path, dir_to_prune)
-            if allow_list and rel_path in allow_list:
-                print('Prune: skip "%s"' % abs_path)
-            else:
                 print('Prune: remove "%s"' % abs_path)
                 os.remove(abs_path)
 
@@ -137,14 +129,7 @@
         api_test_dest_dir = os.path.join(self._framework_appsearch_root, FRAMEWORK_API_TEST_ROOT)
 
         # Prune existing files
-        self._PruneDir(api_dest_dir, allow_list=[
-            'AppSearchBatchResult.java',
-            'AppSearchManager.java',
-            'AppSearchManagerFrameworkInitializer.java',
-            'AppSearchResult.java',
-            'IAppSearchManager.aidl',
-            'SearchResults.java',
-        ])
+        self._PruneDir(api_dest_dir)
         self._PruneDir(api_test_dest_dir)
 
         # Copy api classes. We can't use _TransformAndCopyFolder here because we
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 cad539b..92e2f7c 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
@@ -36,6 +36,7 @@
 import com.google.android.icing.proto.SearchSpecProto;
 import com.google.android.icing.proto.StringIndexingConfig;
 import com.google.android.icing.proto.TermMatchType;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
 import org.junit.Before;
@@ -45,9 +46,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 public class AppSearchImplTest {
     @Rule
@@ -59,20 +58,23 @@
     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.NOT_PLATFORM_SURFACEABLE_PROPERTY)
-                                .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
-                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                                .build())
-                        .build();
+        AppSearchSchema visibilitySchema = VisibilityStore.SCHEMA;
+
+        // We need to rewrite the schema type to follow AppSearchImpl's prefixing scheme.
+        AppSearchSchema.Builder rewrittenVisibilitySchema =
+                new AppSearchSchema.Builder(AppSearchImpl.createPrefix(VisibilityStore.PACKAGE_NAME,
+                        VisibilityStore.DATABASE_NAME) + VisibilityStore.SCHEMA_TYPE);
+        List<AppSearchSchema.PropertyConfig> visibilityProperties =
+                visibilitySchema.getProperties();
+        for (AppSearchSchema.PropertyConfig property : visibilityProperties) {
+            rewrittenVisibilitySchema.addProperty(property);
+        }
         mVisibilitySchemaProto =
-                SchemaToProtoConverter.toSchemaTypeConfigProto(visibilityAppSearchSchema);
+                SchemaToProtoConverter.toSchemaTypeConfigProto(rewrittenVisibilitySchema.build());
     }
 
+    //TODO(b/175430168) add test to verify reset is working properly.
+
     /**
      * Ensure that we can rewrite an incoming schema type by adding the database as a prefix. While
      * also keeping any other existing schema types that may already be part of Icing's persisted
@@ -82,7 +84,7 @@
     public void testRewriteSchema_addType() throws Exception {
         SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("existingDatabase/Foo").build());
+                        .setSchemaType("package$existingDatabase/Foo").build());
 
         // Create a copy so we can modify it.
         List<SchemaTypeConfigProto> existingTypes =
@@ -113,19 +115,19 @@
                 ).build();
 
         AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
-                "newDatabase", existingSchemaBuilder,
+                AppSearchImpl.createPrefix("package", "newDatabase"), existingSchemaBuilder,
                 newSchema);
 
         // 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();
+        assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
+                .containsExactly("package$newDatabase/Foo", "package$newDatabase/TestType");
+        assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
 
         SchemaProto expectedSchema = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("newDatabase/Foo").build())
+                        .setSchemaType("package$newDatabase/Foo").build())
                 .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("newDatabase/TestType")
+                        .setSchemaType("package$newDatabase/TestType")
                         .addProperties(PropertyConfigProto.newBuilder()
                                 .setPropertyName("subject")
                                 .setDataType(PropertyConfigProto.DataType.Code.STRING)
@@ -140,7 +142,7 @@
                                 .setPropertyName("link")
                                 .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
                                 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                                .setSchemaType("newDatabase/RefType")
+                                .setSchemaType("package$newDatabase/RefType")
                                 .build()
                         ).build())
                 .build();
@@ -157,7 +159,7 @@
     public void testRewriteSchema_rewriteType() throws Exception {
         SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("existingDatabase/Foo").build());
+                        .setSchemaType("package$existingDatabase/Foo").build());
 
         SchemaProto newSchema = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder()
@@ -165,12 +167,13 @@
                 .build();
 
         AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
-                "existingDatabase", existingSchemaBuilder, newSchema);
+                AppSearchImpl.createPrefix("package", "existingDatabase"), existingSchemaBuilder,
+                newSchema);
 
         // Nothing was removed, but the method did rewrite the type name.
-        assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
-                .containsExactly("existingDatabase/Foo");
-        assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty();
+        assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
+                .containsExactly("package$existingDatabase/Foo");
+        assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
 
         // Same schema since nothing was added.
         SchemaProto expectedSchema = existingSchemaBuilder.build();
@@ -186,7 +189,7 @@
     public void testRewriteSchema_deleteType() throws Exception {
         SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("existingDatabase/Foo").build());
+                        .setSchemaType("package$existingDatabase/Foo").build());
 
         SchemaProto newSchema = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder()
@@ -194,19 +197,20 @@
                 .build();
 
         AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
-                "existingDatabase", existingSchemaBuilder, newSchema);
+                AppSearchImpl.createPrefix("package", "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");
+        assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
+                .containsExactly("package$existingDatabase/Bar");
+        assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes)
+                .containsExactly("package$existingDatabase/Foo");
 
         // Same schema since nothing was added.
         SchemaProto expectedSchema = SchemaProto.newBuilder()
                 .addTypes(SchemaTypeConfigProto.newBuilder()
-                        .setSchemaType("existingDatabase/Bar").build())
+                        .setSchemaType("package$existingDatabase/Bar").build())
                 .build();
 
         assertThat(existingSchemaBuilder.getTypesList())
@@ -229,18 +233,19 @@
 
         DocumentProto expectedInsideDocument = DocumentProto.newBuilder()
                 .setUri("inside-uri")
-                .setSchema("databaseName/type")
-                .setNamespace("databaseName/namespace")
+                .setSchema("package$databaseName/type")
+                .setNamespace("package$databaseName/namespace")
                 .build();
         DocumentProto expectedDocumentProto = DocumentProto.newBuilder()
                 .setUri("uri")
-                .setSchema("databaseName/type")
-                .setNamespace("databaseName/namespace")
+                .setSchema("package$databaseName/type")
+                .setNamespace("package$databaseName/namespace")
                 .addProperties(PropertyProto.newBuilder().addDocumentValues(expectedInsideDocument))
                 .build();
 
         DocumentProto.Builder actualDocument = documentProto.toBuilder();
-        mAppSearchImpl.addPrefixToDocument(actualDocument, "databaseName/");
+        mAppSearchImpl.addPrefixToDocument(actualDocument, AppSearchImpl.createPrefix("package",
+                "databaseName"));
         assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
     }
 
@@ -248,13 +253,13 @@
     public void testRemoveDocumentTypePrefixes() throws Exception {
         DocumentProto insideDocument = DocumentProto.newBuilder()
                 .setUri("inside-uri")
-                .setSchema("databaseName1/type")
-                .setNamespace("databaseName2/namespace")
+                .setSchema("package$databaseName1/type")
+                .setNamespace("package$databaseName2/namespace")
                 .build();
         DocumentProto documentProto = DocumentProto.newBuilder()
                 .setUri("uri")
-                .setSchema("databaseName2/type")
-                .setNamespace("databaseName3/namespace")
+                .setSchema("package$databaseName2/type")
+                .setNamespace("package$databaseName3/namespace")
                 .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
                 .build();
 
@@ -263,7 +268,7 @@
                 .setSchema("type")
                 .setNamespace("namespace")
                 .build();
-        // Since we don't pass in "databaseName3/" as a prefix to remove, it stays on the Document.
+
         DocumentProto expectedDocumentProto = DocumentProto.newBuilder()
                 .setUri("uri")
                 .setSchema("type")
@@ -272,25 +277,25 @@
                 .build();
 
         DocumentProto.Builder actualDocument = documentProto.toBuilder();
-        mAppSearchImpl.removeDatabasesFromDocument(actualDocument);
+        mAppSearchImpl.removePrefixesFromDocument(actualDocument);
         assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
     }
 
     @Test
     public void testOptimize() throws Exception {
         // Insert schema
-        Set<AppSearchSchema> schemas =
-                Collections.singleton(new AppSearchSchema.Builder("type").build());
-        mAppSearchImpl.setSchema("database", schemas, /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
+        List<AppSearchSchema> schemas =
+                Collections.singletonList(new AppSearchSchema.Builder("type").build());
+        mAppSearchImpl.setSchema("package", "database", schemas, /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ false);
 
         // Insert enough documents.
         for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
                 + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) {
             GenericDocument document =
-                    new GenericDocument.Builder("uri" + i, "type").setNamespace(
+                    new GenericDocument.Builder<>("uri" + i, "type").setNamespace(
                             "namespace").build();
-            mAppSearchImpl.putDocument("database", document);
+            mAppSearchImpl.putDocument("package", "database", document);
         }
 
         // Check optimize() will release 0 docs since there is no deletion.
@@ -300,7 +305,7 @@
         // delete 999 documents , we will reach the threshold to trigger optimize() in next
         // deletion.
         for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1; i++) {
-            mAppSearchImpl.remove("database", "namespace", "uri" + i);
+            mAppSearchImpl.remove("package", "database", "namespace", "uri" + i);
         }
 
         // optimize() still not be triggered since we are in the interval to call getOptimizeInfo()
@@ -312,7 +317,7 @@
         for (int i = AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT;
                 i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
                         + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) {
-            mAppSearchImpl.remove("database", "namespace", "uri" + i);
+            mAppSearchImpl.remove("package", "database", "namespace", "uri" + i);
         }
 
         // Verify optimize() is triggered
@@ -327,22 +332,23 @@
                 SearchSpecProto.newBuilder().setQuery("");
 
         // Insert schema
-        Set<AppSearchSchema> schemas =
-                Collections.singleton(new AppSearchSchema.Builder("type").build());
-        mAppSearchImpl.setSchema("database", schemas, /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
+        List<AppSearchSchema> schemas =
+                Collections.singletonList(new AppSearchSchema.Builder("type").build());
+        mAppSearchImpl.setSchema("package", "database", schemas, /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ false);
 
         // Insert document
-        GenericDocument document = new GenericDocument.Builder("uri", "type").setNamespace(
+        GenericDocument document = new GenericDocument.Builder<>("uri", "type").setNamespace(
                 "namespace").build();
-        mAppSearchImpl.putDocument("database", document);
+        mAppSearchImpl.putDocument("package", "database", document);
 
         // Rewrite SearchSpec
-        mAppSearchImpl.rewriteSearchSpecForDatabasesLocked(searchSpecProto,
-                Collections.singleton(
-                        "database"));
-        assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly("database/type");
-        assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly("database/namespace");
+        mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(searchSpecProto,
+                Collections.singleton(AppSearchImpl.createPrefix("package", "database")));
+        assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
+                "package$database/type");
+        assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly(
+                "package$database/namespace");
     }
 
     @Test
@@ -351,39 +357,40 @@
                 SearchSpecProto.newBuilder().setQuery("");
 
         // Insert schema
-        Set<AppSearchSchema> schemas = Set.of(
+        List<AppSearchSchema> schemas = ImmutableList.of(
                 new AppSearchSchema.Builder("typeA").build(),
                 new AppSearchSchema.Builder("typeB").build());
-        mAppSearchImpl.setSchema("database1", schemas, /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
-        mAppSearchImpl.setSchema("database2", schemas, /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
+        mAppSearchImpl.setSchema("package", "database1", schemas, /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ false);
+        mAppSearchImpl.setSchema("package", "database2", schemas, /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ false);
 
         // Insert documents
-        GenericDocument document1 = new GenericDocument.Builder("uri", "typeA").setNamespace(
+        GenericDocument document1 = new GenericDocument.Builder<>("uri", "typeA").setNamespace(
                 "namespace").build();
-        mAppSearchImpl.putDocument("database1", document1);
+        mAppSearchImpl.putDocument("package", "database1", document1);
 
-        GenericDocument document2 = new GenericDocument.Builder("uri", "typeB").setNamespace(
+        GenericDocument document2 = new GenericDocument.Builder<>("uri", "typeB").setNamespace(
                 "namespace").build();
-        mAppSearchImpl.putDocument("database2", document2);
+        mAppSearchImpl.putDocument("package", "database2", document2);
 
         // Rewrite SearchSpec
-        mAppSearchImpl.rewriteSearchSpecForDatabasesLocked(searchSpecProto,
-                ImmutableSet.of("database1", "database2"));
+        mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(searchSpecProto,
+                ImmutableSet.of(AppSearchImpl.createPrefix("package", "database1"),
+                        AppSearchImpl.createPrefix("package", "database2")));
         assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
-                "database1/typeA", "database1/typeB", "database2/typeA", "database2/typeB");
+                "package$database1/typeA", "package$database1/typeB", "package$database2/typeA",
+                "package$database2/typeB");
         assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly(
-                "database1/namespace", "database2/namespace");
+                "package$database1/namespace", "package$database2/namespace");
     }
 
     @Test
     public void testQueryEmptyDatabase() throws Exception {
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
-        SearchResultPage searchResultPage = mAppSearchImpl.query(
-                "EmptyDatabase",
-                "", searchSpec);
+        SearchResultPage searchResultPage = mAppSearchImpl.query("package", "EmptyDatabase", "",
+                searchSpec);
         assertThat(searchResultPage.getResults()).isEmpty();
     }
 
@@ -391,9 +398,7 @@
     public void testGlobalQueryEmptyDatabase() throws Exception {
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
-        SearchResultPage searchResultPage = mAppSearchImpl.query(
-                "EmptyDatabase",
-                "", searchSpec);
+        SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
         assertThat(searchResultPage.getResults()).isEmpty();
     }
 
@@ -402,30 +407,31 @@
         SearchSpec searchSpec =
                 new SearchSpec.Builder().addSchemaType("FakeType").setTermMatch(
                         TermMatchType.Code.PREFIX_VALUE).build();
-        mAppSearchImpl.removeByQuery("EmptyDatabase",
+        mAppSearchImpl.removeByQuery("package", "EmptyDatabase",
                 "", searchSpec);
 
         searchSpec =
                 new SearchSpec.Builder().addNamespace("FakeNamespace").setTermMatch(
                         TermMatchType.Code.PREFIX_VALUE).build();
-        mAppSearchImpl.removeByQuery("EmptyDatabase",
+        mAppSearchImpl.removeByQuery("package", "EmptyDatabase",
                 "", searchSpec);
 
         searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
-        mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec);
+        mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
     }
 
     @Test
     public void testSetSchema() throws Exception {
-        Set<AppSearchSchema> schemas =
-                Collections.singleton(new AppSearchSchema.Builder("Email").build());
+        List<AppSearchSchema> schemas =
+                Collections.singletonList(new AppSearchSchema.Builder("Email").build());
         // Set schema Email to AppSearch database1
-        mAppSearchImpl.setSchema("database1", schemas, /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
+        mAppSearchImpl.setSchema("package", "database1", schemas, /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ false);
 
         // Create expected schemaType proto.
         SchemaProto expectedProto = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder().setSchemaType("package$database1/Email"))
                 .build();
 
         List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
@@ -437,39 +443,48 @@
 
     @Test
     public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
-        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+        String prefix = AppSearchImpl.createPrefix("package", "database");
+        mAppSearchImpl.setSchema("package", "database",
+                Collections.singletonList(new AppSearchSchema.Builder(
                         "schema1").build()), /*schemasNotPlatformSurfaceable=*/
-                Collections.singleton("schema1"), /*forceOverride=*/ false);
+                Collections.singletonList("schema1"), /*forceOverride=*/ false);
 
         // "schema1" is platform hidden now
-        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
-                "database")).containsExactly("database/schema1");
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+                prefix, prefix + "schema1")).isFalse();
 
         // 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()), /*schemasNotPlatformSurfaceable=*/
-                Collections.singleton("schema1"), /*forceOverride=*/ false);
+        mAppSearchImpl.setSchema(
+                "package", "database",
+                ImmutableList.of(
+                        new AppSearchSchema.Builder("schema1").build(),
+                        new AppSearchSchema.Builder("schema2").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
+                /*forceOverride=*/ false);
 
         // Check that "schema1" is still platform hidden, but "schema2" is the default platform
         // visible.
-        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
-                "database")).containsExactly("database/schema1");
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+                prefix, prefix + "schema1")).isFalse();
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+                prefix, prefix + "schema2")).isTrue();
     }
 
     @Test
     public void testRemoveSchema() throws Exception {
-        Set<AppSearchSchema> schemas = new HashSet<>();
-        schemas.add(new AppSearchSchema.Builder("Email").build());
-        schemas.add(new AppSearchSchema.Builder("Document").build());
+        List<AppSearchSchema> schemas = ImmutableList.of(
+                new AppSearchSchema.Builder("Email").build(),
+                new AppSearchSchema.Builder("Document").build());
         // Set schema Email and Document to AppSearch database1
-        mAppSearchImpl.setSchema("database1", schemas, /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
+        mAppSearchImpl.setSchema("package", "database1", schemas, /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ false);
 
         // Create expected schemaType proto.
         SchemaProto expectedProto = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document"))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder().setSchemaType("package$database1/Email"))
+                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
+                        "package$database1/Document"))
                 .build();
 
         // Check both schema Email and Document saved correctly.
@@ -479,23 +494,26 @@
         assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
                 .containsExactlyElementsIn(expectedTypes);
 
-        final Set<AppSearchSchema> finalSchemas = Collections.singleton(new AppSearchSchema.Builder(
-                "Email").build());
+        final List<AppSearchSchema> finalSchemas = Collections.singletonList(
+                new AppSearchSchema.Builder(
+                        "Email").build());
         // Check the incompatible error has been thrown.
         AppSearchException e = assertThrows(AppSearchException.class, () ->
-                mAppSearchImpl.setSchema("database1",
+                mAppSearchImpl.setSchema("package", "database1",
                         finalSchemas, /*schemasNotPlatformSurfaceable=*/
-                        Collections.emptySet(), /*forceOverride=*/ false));
+                        Collections.emptyList(), /*forceOverride=*/ false));
         assertThat(e).hasMessageThat().contains("Schema is incompatible");
-        assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]");
+        assertThat(e).hasMessageThat().contains("Deleted types: [package$database1/Document]");
 
         // ForceOverride to delete.
-        mAppSearchImpl.setSchema("database1", finalSchemas, /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ true);
+        mAppSearchImpl.setSchema("package", "database1",
+                finalSchemas, /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ true);
 
         // Check Document schema is removed.
         expectedProto = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder().setSchemaType("package$database1/Email"))
                 .build();
 
         expectedTypes = new ArrayList<>();
@@ -508,22 +526,26 @@
     @Test
     public void testRemoveSchema_differentDataBase() throws Exception {
         // Create schemas
-        Set<AppSearchSchema> schemas = new HashSet<>();
-        schemas.add(new AppSearchSchema.Builder("Email").build());
-        schemas.add(new AppSearchSchema.Builder("Document").build());
+        List<AppSearchSchema> schemas = ImmutableList.of(
+                new AppSearchSchema.Builder("Email").build(),
+                new AppSearchSchema.Builder("Document").build());
 
         // Set schema Email and Document to AppSearch database1 and 2
-        mAppSearchImpl.setSchema("database1", schemas, /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
-        mAppSearchImpl.setSchema("database2", schemas, /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
+        mAppSearchImpl.setSchema("package", "database1", schemas, /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ false);
+        mAppSearchImpl.setSchema("package", "database2", schemas, /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ false);
 
         // 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"))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document"))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder().setSchemaType("package$database1/Email"))
+                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
+                        "package$database1/Document"))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder().setSchemaType("package$database2/Email"))
+                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
+                        "package$database2/Document"))
                 .build();
 
         // Check Email and Document is saved in database 1 and 2 correctly.
@@ -534,16 +556,19 @@
                 .containsExactlyElementsIn(expectedTypes);
 
         // Save only Email to database1 this time.
-        schemas = Collections.singleton(new AppSearchSchema.Builder("Email").build());
-        mAppSearchImpl.setSchema("database1", schemas, /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ true);
+        schemas = Collections.singletonList(new AppSearchSchema.Builder("Email").build());
+        mAppSearchImpl.setSchema("package", "database1", schemas, /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ true);
 
         // Create expected schemaType list, database 1 should only contain Email but database 2
         // remains in same.
         expectedProto = SchemaProto.newBuilder()
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document"))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder().setSchemaType("package$database1/Email"))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder().setSchemaType("package$database2/Email"))
+                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
+                        "package$database2/Document"))
                 .build();
 
         // Check nothing changed in database2.
@@ -557,84 +582,101 @@
 
     @Test
     public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
-        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+        String prefix = AppSearchImpl.createPrefix("package", "database");
+        mAppSearchImpl.setSchema("package", "database",
+                Collections.singletonList(new AppSearchSchema.Builder(
                         "schema1").build()), /*schemasNotPlatformSurfaceable=*/
-                Collections.singleton("schema1"), /*forceOverride=*/ false);
+                Collections.singletonList("schema1"), /*forceOverride=*/ false);
 
         // "schema1" is platform hidden now
-        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
-                "database")).containsExactly("database/schema1");
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+                prefix, prefix + "schema1")).isFalse();
 
         // Remove "schema1" by force overriding
-        mAppSearchImpl.setSchema("database",
-                Collections.emptySet(), /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ true);
+        mAppSearchImpl.setSchema("package", "database",
+                Collections.emptyList(), /*schemasNotPlatformSurfaceable=*/
+                Collections.emptyList(), /*forceOverride=*/ true);
 
         // Check that "schema1" is no longer considered platform hidden
         assertThat(
-                mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
-                        "database")).isEmpty();
+                mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+                        prefix, prefix + "schema1")).isTrue();
 
         // Add "schema1" back, it gets default visibility settings which means it's not platform
         // hidden.
-        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+        mAppSearchImpl.setSchema("package", "database",
+                Collections.singletonList(new AppSearchSchema.Builder(
                         "schema1").build()), /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
+                Collections.emptyList(), /*forceOverride=*/ false);
         assertThat(
-                mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
-                        "database")).isEmpty();
+                mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+                        prefix, prefix + "schema1")).isTrue();
     }
 
     @Test
     public void testSetSchema_defaultPlatformVisible() throws Exception {
-        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+        String prefix = AppSearchImpl.createPrefix("package", "database");
+        mAppSearchImpl.setSchema("package", "database",
+                Collections.singletonList(new AppSearchSchema.Builder(
                         "Schema").build()), /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
+                Collections.emptyList(), /*forceOverride=*/ false);
         assertThat(
-                mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
-                        "database")).isEmpty();
+                mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+                        prefix, prefix + "Schema")).isTrue();
     }
 
     @Test
     public void testSetSchema_platformHidden() throws Exception {
-        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+        String prefix = AppSearchImpl.createPrefix("package", "database");
+        mAppSearchImpl.setSchema("package", "database",
+                Collections.singletonList(new AppSearchSchema.Builder(
                         "Schema").build()), /*schemasNotPlatformSurfaceable=*/
-                Collections.singleton("Schema"), /*forceOverride=*/ false);
-        assertThat(mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
-                "database")).containsExactly("database/Schema");
+                Collections.singletonList("Schema"), /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+                prefix, prefix + "Schema")).isFalse();
     }
 
     @Test
     public void testHasSchemaType() throws Exception {
         // Nothing exists yet
-        assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isFalse();
+        assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isFalse();
 
-        mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
+        mAppSearchImpl.setSchema("package", "database",
+                Collections.singletonList(new AppSearchSchema.Builder(
                         "Schema").build()), /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
-        assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isTrue();
+                Collections.emptyList(), /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isTrue();
 
-        assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "UnknownSchema")).isFalse();
+        assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database",
+                "UnknownSchema")).isFalse();
     }
 
     @Test
     public void testGetDatabases() throws Exception {
         // No client databases exist yet, but the VisibilityStore's does
-        assertThat(mAppSearchImpl.getDatabasesLocked()).containsExactly(
-                VisibilityStore.DATABASE_NAME);
+        assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactly(
+                AppSearchImpl.createPrefix(VisibilityStore.PACKAGE_NAME,
+                        VisibilityStore.DATABASE_NAME));
 
         // Has database1
-        mAppSearchImpl.setSchema("database1", Collections.singleton(new AppSearchSchema.Builder(
+        mAppSearchImpl.setSchema("package", "database1",
+                Collections.singletonList(new AppSearchSchema.Builder(
                         "schema").build()), /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
-        assertThat(mAppSearchImpl.getDatabasesLocked()).containsExactly(
-                VisibilityStore.DATABASE_NAME, "database1");
+                Collections.emptyList(), /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactly(
+                AppSearchImpl.createPrefix(VisibilityStore.PACKAGE_NAME,
+                        VisibilityStore.DATABASE_NAME),
+                AppSearchImpl.createPrefix("package", "database1"));
 
         // Has both databases
-        mAppSearchImpl.setSchema("database2", Collections.singleton(new AppSearchSchema.Builder(
+        mAppSearchImpl.setSchema("package", "database2",
+                Collections.singletonList(new AppSearchSchema.Builder(
                         "schema").build()), /*schemasNotPlatformSurfaceable=*/
-                Collections.emptySet(), /*forceOverride=*/ false);
-        assertThat(mAppSearchImpl.getDatabasesLocked()).containsExactly(
-                VisibilityStore.DATABASE_NAME, "database1", "database2");
+                Collections.emptyList(), /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactly(
+                AppSearchImpl.createPrefix(VisibilityStore.PACKAGE_NAME,
+                        VisibilityStore.DATABASE_NAME),
+                AppSearchImpl.createPrefix("package", "database1"), AppSearchImpl.createPrefix(
+                        "package", "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
index d4a30f4..4577f5a 100644
--- a/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/VisibilityStoreTest.java
+++ b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/VisibilityStoreTest.java
@@ -18,13 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.common.collect.ImmutableSet;
+
 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 {
 
@@ -39,22 +40,69 @@
         mVisibilityStore = mAppSearchImpl.getVisibilityStoreLocked();
     }
 
+    /**
+     * Make sure that we don't conflict with any special characters that AppSearchImpl has
+     * reserved.
+     */
+    @Test
+    public void testValidPackageName() {
+        assertThat(VisibilityStore.PACKAGE_NAME).doesNotContain(
+                "" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences
+        assertThat(VisibilityStore.PACKAGE_NAME).doesNotContain(
+                "" + AppSearchImpl.DATABASE_DELIMITER); // Convert the chars to CharSequences
+    }
+
+    /**
+     * Make sure that we don't conflict with any special characters that AppSearchImpl has
+     * reserved.
+     */
+    @Test
+    public void testValidDatabaseName() {
+        assertThat(VisibilityStore.DATABASE_NAME).doesNotContain(
+                "" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences
+        assertThat(VisibilityStore.DATABASE_NAME).doesNotContain(
+                "" + AppSearchImpl.DATABASE_DELIMITER); // Convert the chars to CharSequences
+    }
+
     @Test
     public void testSetVisibility() throws Exception {
-        mVisibilityStore.setVisibility(
-                "database", /*schemasNotPlatformSurfaceable=*/ Set.of("schema1", "schema2"));
-        assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database"))
-                .containsExactly("schema1", "schema2");
+        mVisibilityStore.setVisibility("prefix",
+                /*schemasNotPlatformSurfaceable=*/
+                ImmutableSet.of("prefix/schema1", "prefix/schema2"));
+        assertThat(
+                mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1")).isFalse();
+        assertThat(
+                mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2")).isFalse();
 
         // New .setVisibility() call completely overrides previous visibility settings. So
-        // "schema1" isn't preserved.
-        mVisibilityStore.setVisibility(
-                "database", /*schemasNotPlatformSurfaceable=*/ Set.of("schema1", "schema3"));
-        assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database"))
-                .containsExactly("schema1", "schema3");
+        // "schema2" isn't preserved.
+        mVisibilityStore.setVisibility("prefix",
+                /*schemasNotPlatformSurfaceable=*/
+                ImmutableSet.of("prefix/schema1", "prefix/schema3"));
+        assertThat(
+                mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1")).isFalse();
+        assertThat(
+                mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2")).isTrue();
+        assertThat(
+                mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema3")).isFalse();
 
         mVisibilityStore.setVisibility(
-                "database", /*schemasNotPlatformSurfaceable=*/ Collections.emptySet());
-        assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database")).isEmpty();
+                "prefix", /*schemasNotPlatformSurfaceable=*/ Collections.emptySet());
+        assertThat(
+                mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1")).isTrue();
+        assertThat(
+                mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2")).isTrue();
+        assertThat(
+                mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema3")).isTrue();
+    }
+
+    @Test
+    public void testEmptyPrefix() throws Exception {
+        mVisibilityStore.setVisibility(/*prefix=*/ "",
+                /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of("schema1", "schema2"));
+        assertThat(
+                mVisibilityStore.isSchemaPlatformSurfaceable(/*prefix=*/ "", "schema1")).isFalse();
+        assertThat(
+                mVisibilityStore.isSchemaPlatformSurfaceable(/*prefix=*/ "", "schema2")).isFalse();
     }
 }
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 2ec2d82..627e87a 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
@@ -61,6 +61,7 @@
 import com.google.android.icing.proto.StatusProto;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -75,14 +76,16 @@
  *
  * <p>Never create two instances using the same folder.
  *
- * <p>A single instance of {@link AppSearchImpl} can support all databases. Schemas and documents
- * are physically saved together in {@link IcingSearchEngine}, but logically isolated:
+ * <p>A single instance of {@link AppSearchImpl} can support all packages and databases.
+ * This is done by combining the package and database name into a unique prefix and
+ * prefixing the schemas and documents stored under that owner. Schemas and documents are
+ * physically saved together in {@link IcingSearchEngine}, but logically isolated:
  * <ul>
- *      <li>Rewrite SchemaType in SchemaProto by adding database name prefix and save into
+ *      <li>Rewrite SchemaType in SchemaProto by adding the package-database prefix and save into
  *          SchemaTypes set in {@link #setSchema}.
- *      <li>Rewrite namespace and SchemaType in DocumentProto by adding database name prefix and
+ *      <li>Rewrite namespace and SchemaType in DocumentProto by adding package-database prefix and
  *          save to namespaces set in {@link #putDocument}.
- *      <li>Remove database name prefix when retrieve documents in {@link #getDocument} and
+ *      <li>Remove package-database prefix when retrieving documents in {@link #getDocument} and
  *          {@link #query}.
  *      <li>Rewrite filters in {@link SearchSpecProto} to have all namespaces and schema types of
  *          the queried database when user using empty filters in {@link #query}.
@@ -109,6 +112,9 @@
     static final char DATABASE_DELIMITER = '/';
 
     @VisibleForTesting
+    static final char PACKAGE_DELIMITER = '$';
+
+    @VisibleForTesting
     static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000;
     @VisibleForTesting
     static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB
@@ -123,12 +129,14 @@
     @GuardedBy("mReadWriteLock")
     private final VisibilityStore mVisibilityStoreLocked;
 
-    // The map contains schemaTypes and namespaces for all database. All values in the map have
-    // the database name prefix.
+    // This map contains schemaTypes for all package-database prefixes. All values in the map are
+    // prefixed with the package-database prefix.
     // TODO(b/172360376): Check if this can be replaced with an ArrayMap
     @GuardedBy("mReadWriteLock")
     private final Map<String, Set<String>> mSchemaMapLocked = new HashMap<>();
 
+    // This map contains namespaces for all package-database prefixes. All values in the map are
+    // prefixed with the package-database prefix.
     // TODO(b/172360376): Check if this can be replaced with an ArrayMap
     @GuardedBy("mReadWriteLock")
     private final Map<String, Set<String>> mNamespaceMapLocked = new HashMap<>();
@@ -154,7 +162,6 @@
     }
 
     private AppSearchImpl(@NonNull File icingDir) throws AppSearchException {
-        boolean isReset = false;
         mReadWriteLock.writeLock().lock();
 
         try {
@@ -164,9 +171,11 @@
                     .setBaseDir(icingDir.getAbsolutePath()).build();
             mIcingSearchEngineLocked = new IcingSearchEngine(options);
 
+            mVisibilityStoreLocked = new VisibilityStore(this);
+
             InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
-            SchemaProto schemaProto = null;
-            GetAllNamespacesResultProto getAllNamespacesResultProto = null;
+            SchemaProto schemaProto;
+            GetAllNamespacesResultProto getAllNamespacesResultProto;
             try {
                 checkSuccess(initializeResultProto.getStatus());
                 schemaProto = getSchemaProtoLocked();
@@ -176,29 +185,26 @@
                 Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e);
                 // Some error. Reset and see if it fixes it.
                 reset();
-                isReset = true;
+                return;
             }
 
             // Populate schema map
             for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
-                String qualifiedSchemaType = schema.getSchemaType();
-                addToMap(mSchemaMapLocked, getDatabaseName(qualifiedSchemaType),
-                        qualifiedSchemaType);
+                String prefixedSchemaType = schema.getSchemaType();
+                addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType),
+                        prefixedSchemaType);
             }
 
             // Populate namespace map
-            for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
-                addToMap(mNamespaceMapLocked, getDatabaseName(qualifiedNamespace),
-                        qualifiedNamespace);
+            for (String prefixedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
+                addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace),
+                        prefixedNamespace);
             }
 
             // 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) {
-                checkForOptimizeLocked(/* force= */ true);
-            }
+            checkForOptimizeLocked(/* force= */ true);
 
-            mVisibilityStoreLocked = new VisibilityStore(this);
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
@@ -223,6 +229,7 @@
      *
      * <p>This method belongs to mutate group.
      *
+     * @param packageName                   The package name that owns the schemas.
      * @param databaseName                  The name of the database where this schema lives.
      * @param schemas                       Schemas to set for this app.
      * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
@@ -232,23 +239,27 @@
      *                                      which do not comply with the new schema will be deleted.
      * @throws AppSearchException on IcingSearchEngine error.
      */
-    public void setSchema(@NonNull String databaseName, @NonNull Set<AppSearchSchema> schemas,
-            @NonNull Set<String> schemasNotPlatformSurfaceable,
+    public void setSchema(
+            @NonNull String packageName,
+            @NonNull String databaseName,
+            @NonNull List<AppSearchSchema> schemas,
+            @NonNull List<String> schemasNotPlatformSurfaceable,
             boolean forceOverride) throws AppSearchException {
         mReadWriteLock.writeLock().lock();
         try {
             SchemaProto.Builder existingSchemaBuilder = getSchemaProtoLocked().toBuilder();
 
             SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
-            for (AppSearchSchema schema : schemas) {
+            for (int i = 0; i < schemas.size(); i++) {
                 SchemaTypeConfigProto schemaTypeProto =
-                        SchemaToProtoConverter.toSchemaTypeConfigProto(schema);
+                        SchemaToProtoConverter.toSchemaTypeConfigProto(schemas.get(i));
                 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,
+            String prefix = createPrefix(packageName, databaseName);
+            // Combine the existing schema (which may have types from other prefixes) with this
+            // prefix's new schema. Modifies the existingSchemaBuilder.
+            RewrittenSchemaResults rewrittenSchemaResults = rewriteSchema(prefix,
                     existingSchemaBuilder,
                     newSchemaBuilder.build());
 
@@ -276,16 +287,16 @@
             }
 
             // Update derived data structures.
-            mSchemaMapLocked.put(databaseName, rewrittenSchemaResults.mRewrittenQualifiedTypes);
+            mSchemaMapLocked.put(prefix, rewrittenSchemaResults.mRewrittenPrefixedTypes);
 
-            String databasePrefix = getDatabasePrefix(databaseName);
-            Set<String> qualifiedSchemasNotPlatformSurfaceable =
+            Set<String> prefixedSchemasNotPlatformSurfaceable =
                     new ArraySet<>(schemasNotPlatformSurfaceable.size());
-            for (String schema : schemasNotPlatformSurfaceable) {
-                qualifiedSchemasNotPlatformSurfaceable.add(databasePrefix + schema);
+            for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) {
+                prefixedSchemasNotPlatformSurfaceable.add(
+                        prefix + schemasNotPlatformSurfaceable.get(i));
             }
-            mVisibilityStoreLocked.setVisibility(databaseName,
-                    qualifiedSchemasNotPlatformSurfaceable);
+            mVisibilityStoreLocked.setVisibility(prefix,
+                    prefixedSchemasNotPlatformSurfaceable);
 
             // Determine whether to schedule an immediate optimize.
             if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
@@ -302,15 +313,17 @@
     }
 
     /**
-     * Retrieves the AppSearch schema for this database.
+     * Retrieves the AppSearch schema for this package name, database.
      *
      * <p>This method belongs to query group.
      *
-     * @param databaseName  The name of the database where this schema lives.
+     * @param packageName  Package name that owns this schema
+     * @param databaseName The name of the database where this schema lives.
      * @throws AppSearchException on IcingSearchEngine error.
      */
     @NonNull
-    public Set<AppSearchSchema> getSchema(@NonNull String databaseName) throws AppSearchException {
+    public List<AppSearchSchema> getSchema(@NonNull String packageName,
+            @NonNull String databaseName) throws AppSearchException {
         SchemaProto fullSchema;
         mReadWriteLock.readLock().lock();
         try {
@@ -319,16 +332,17 @@
             mReadWriteLock.readLock().unlock();
         }
 
-        Set<AppSearchSchema> result = new ArraySet<>();
+        String prefix = createPrefix(packageName, databaseName);
+        List<AppSearchSchema> result = new ArrayList<>();
         for (int i = 0; i < fullSchema.getTypesCount(); i++) {
-            String typeDatabase = getDatabaseName(fullSchema.getTypes(i).getSchemaType());
-            if (!databaseName.equals(typeDatabase)) {
+            String typePrefix = getPrefix(fullSchema.getTypes(i).getSchemaType());
+            if (!prefix.equals(typePrefix)) {
                 continue;
             }
             // Rewrite SchemaProto.types.schema_type
             SchemaTypeConfigProto.Builder typeConfigBuilder = fullSchema.getTypes(i).toBuilder();
             String newSchemaType =
-                    typeConfigBuilder.getSchemaType().substring(databaseName.length() + 1);
+                    typeConfigBuilder.getSchemaType().substring(prefix.length());
             typeConfigBuilder.setSchemaType(newSchemaType);
 
             // Rewrite SchemaProto.types.properties.schema_type
@@ -339,7 +353,7 @@
                         typeConfigBuilder.getProperties(propertyIdx).toBuilder();
                 if (!propertyConfigBuilder.getSchemaType().isEmpty()) {
                     String newPropertySchemaType = propertyConfigBuilder.getSchemaType()
-                            .substring(databaseName.length() + 1);
+                            .substring(prefix.length());
                     propertyConfigBuilder.setSchemaType(newPropertySchemaType);
                     typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder);
                 }
@@ -356,24 +370,27 @@
      *
      * <p>This method belongs to mutate group.
      *
+     * @param packageName  The package name that owns this document.
      * @param databaseName The databaseName this document resides in.
      * @param document     The document to index.
      * @throws AppSearchException on IcingSearchEngine error.
      */
-    public void putDocument(@NonNull String databaseName, @NonNull GenericDocument document)
+    public void putDocument(@NonNull String packageName, @NonNull String databaseName,
+            @NonNull GenericDocument document)
             throws AppSearchException {
         DocumentProto.Builder documentBuilder = GenericDocumentToProtoConverter.toDocumentProto(
                 document).toBuilder();
-        addPrefixToDocument(documentBuilder, getDatabasePrefix(databaseName));
+        String prefix = createPrefix(packageName, databaseName);
+        addPrefixToDocument(documentBuilder, prefix);
 
         PutResultProto putResultProto;
         mReadWriteLock.writeLock().lock();
         try {
             putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build());
-            addToMap(mNamespaceMapLocked, databaseName, documentBuilder.getNamespace());
+            addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace());
             // The existing documents with same URI will be deleted, so there maybe some resources
             // could be released after optimize().
-            checkForOptimizeLocked(/* force= */false);
+            checkForOptimizeLocked(/* force= */ false);
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
@@ -385,6 +402,7 @@
      *
      * <p>This method belongs to query group.
      *
+     * @param packageName  The package that owns this document.
      * @param databaseName The databaseName this document resides in.
      * @param namespace    The namespace this document resides in.
      * @param uri          The URI of the document to get.
@@ -392,20 +410,21 @@
      * @throws AppSearchException on IcingSearchEngine error.
      */
     @NonNull
-    public GenericDocument getDocument(@NonNull String databaseName, @NonNull String namespace,
+    public GenericDocument getDocument(@NonNull String packageName, @NonNull String databaseName,
+            @NonNull String namespace,
             @NonNull String uri) throws AppSearchException {
         GetResultProto getResultProto;
         mReadWriteLock.readLock().lock();
         try {
             getResultProto = mIcingSearchEngineLocked.get(
-                    getDatabasePrefix(databaseName) + namespace, uri);
+                    createPrefix(packageName, databaseName) + namespace, uri);
         } finally {
             mReadWriteLock.readLock().unlock();
         }
         checkSuccess(getResultProto.getStatus());
 
         DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder();
-        removeDatabasesFromDocument(documentBuilder);
+        removePrefixesFromDocument(documentBuilder);
         return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build());
     }
 
@@ -414,6 +433,7 @@
      *
      * <p>This method belongs to query group.
      *
+     * @param packageName     The package name that is performing the query.
      * @param databaseName    The databaseName this query for.
      * @param queryExpression Query String to search.
      * @param searchSpec      Spec for setting filters, raw query etc.
@@ -423,12 +443,14 @@
      */
     @NonNull
     public SearchResultPage query(
+            @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec) throws AppSearchException {
         mReadWriteLock.readLock().lock();
         try {
-            return doQueryLocked(Collections.singleton(databaseName), queryExpression,
+            return doQueryLocked(Collections.singleton(createPrefix(packageName, databaseName)),
+                    queryExpression,
                     searchSpec);
         } finally {
             mReadWriteLock.readLock().unlock();
@@ -436,7 +458,7 @@
     }
 
     /**
-     * Executes a global query, i.e. over all permitted databases, against the AppSearch index and
+     * Executes a global query, i.e. over all permitted prefixes, against the AppSearch index and
      * returns results.
      *
      * <p>This method belongs to query group.
@@ -456,9 +478,15 @@
         //  verified.
         mReadWriteLock.readLock().lock();
         try {
-            // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases
+            // We use the mNamespaceMap.keySet here because it's the smaller set of valid prefixes
             // that could exist.
-            return doQueryLocked(mNamespaceMapLocked.keySet(), queryExpression, searchSpec);
+            Set<String> prefixes = mNamespaceMapLocked.keySet();
+
+            // Filter out any VisibilityStore documents which are AppSearch-internal only.
+            prefixes.remove(createPrefix(VisibilityStore.PACKAGE_NAME,
+                    VisibilityStore.DATABASE_NAME));
+
+            return doQueryLocked(prefixes, queryExpression, searchSpec);
         } finally {
             mReadWriteLock.readLock().unlock();
         }
@@ -466,7 +494,7 @@
 
     @GuardedBy("mReadWriteLock")
     private SearchResultPage doQueryLocked(
-            @NonNull Set<String> databases, @NonNull String queryExpression,
+            @NonNull Set<String> prefixes, @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec)
             throws AppSearchException {
         SearchSpecProto searchSpecProto =
@@ -478,12 +506,10 @@
         ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
         SearchResultProto searchResultProto;
 
-        // rewriteSearchSpecForDatabases will return false if none of the databases that the
+        // rewriteSearchSpecForPrefixesLocked will return false if none of the prefixes that the
         // client is trying to search on exist, so we can return an empty SearchResult and skip
         // sending request to Icing.
-        // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases
-        // that could exist.
-        if (!rewriteSearchSpecForDatabasesLocked(searchSpecBuilder, databases)) {
+        if (!rewriteSearchSpecForPrefixesLocked(searchSpecBuilder, prefixes)) {
             return new SearchResultPage(Bundle.EMPTY);
         }
         searchResultProto = mIcingSearchEngineLocked.search(
@@ -539,18 +565,20 @@
      *
      * <p>This method belongs to mutate group.
      *
+     * @param packageName  The package name that owns the document.
      * @param databaseName The databaseName the document is in.
      * @param namespace    Namespace of the document to remove.
      * @param uri          URI of the document to remove.
      * @throws AppSearchException on IcingSearchEngine error.
      */
-    public void remove(@NonNull String databaseName, @NonNull String namespace,
+    public void remove(@NonNull String packageName, @NonNull String databaseName,
+            @NonNull String namespace,
             @NonNull String uri) throws AppSearchException {
-        String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
+        String prefixedNamespace = createPrefix(packageName, databaseName) + namespace;
         DeleteResultProto deleteResultProto;
         mReadWriteLock.writeLock().lock();
         try {
-            deleteResultProto = mIcingSearchEngineLocked.delete(qualifiedNamespace, uri);
+            deleteResultProto = mIcingSearchEngineLocked.delete(prefixedNamespace, uri);
             checkForOptimizeLocked(/* force= */false);
         } finally {
             mReadWriteLock.writeLock().unlock();
@@ -563,12 +591,14 @@
      *
      * <p>This method belongs to mutate group.
      *
+     * @param packageName     The package name that owns the documents.
      * @param databaseName    The databaseName the document is in.
      * @param queryExpression Query String to search.
      * @param searchSpec      Defines what and how to remove
      * @throws AppSearchException on IcingSearchEngine error.
      */
-    public void removeByQuery(@NonNull String databaseName, @NonNull String queryExpression,
+    public void removeByQuery(@NonNull String packageName, @NonNull String databaseName,
+            @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec)
             throws AppSearchException {
         SearchSpecProto searchSpecProto =
@@ -578,11 +608,11 @@
         DeleteResultProto deleteResultProto;
         mReadWriteLock.writeLock().lock();
         try {
-            // Only rewrite SearchSpec for non empty database.
-            // rewriteSearchSpecForNonEmptyDatabase will return false for empty database, we
+            // Only rewrite SearchSpec for non empty prefixes.
+            // rewriteSearchSpecForPrefixesLocked will return false for empty prefixes, we
             // should skip sending request to Icing and return in here.
-            if (!rewriteSearchSpecForDatabasesLocked(searchSpecBuilder,
-                    Collections.singleton(databaseName))) {
+            if (!rewriteSearchSpecForPrefixesLocked(searchSpecBuilder,
+                    Collections.singleton(createPrefix(packageName, databaseName)))) {
                 return;
             }
             deleteResultProto = mIcingSearchEngineLocked.deleteByQuery(
@@ -598,7 +628,10 @@
     }
 
     /**
-     * Clears documents and schema across all databaseNames.
+     * Clears documents and schema across all packages and databaseNames.
+     *
+     * <p>This method also clear all data in {@link VisibilityStore}, an
+     * {@link #initializeVisibilityStore()} must be called after this.
      *
      * <p>This method belongs to mutate group.
      *
@@ -625,31 +658,29 @@
     /** 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<>();
+        // Any prefixed types that used to exist in the schema, but are deleted in the new one.
+        final Set<String> mDeletedPrefixedTypes = new ArraySet<>();
 
-        // Database-qualified types that were part of the new schema.
-        final Set<String> mRewrittenQualifiedTypes = new ArraySet<>();
+        // Prefixed types that were part of the new schema.
+        final Set<String> mRewrittenPrefixedTypes = new ArraySet<>();
     }
 
     /**
      * Rewrites all types mentioned in the given {@code newSchema} to prepend {@code prefix}.
      * Rewritten types will be added to the {@code existingSchema}.
      *
-     * @param databaseName   The name of the database where this schema lives.
-     * @param existingSchema A schema that may contain existing types from across all database
-     *                       instances. Will be mutated to contain the properly rewritten schema
+     * @param prefix         The full prefix to prepend to the schema.
+     * @param existingSchema A schema that may contain existing types from across all prefixes.
+     *                       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 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.
+     * @return a RewrittenSchemaResults that contains all prefixed schema type names in the given
+     * prefix as well as a set of schema types that were deleted.
      */
     @VisibleForTesting
-    static RewrittenSchemaResults rewriteSchema(@NonNull String databaseName,
+    static RewrittenSchemaResults rewriteSchema(@NonNull String prefix,
             @NonNull SchemaProto.Builder existingSchema,
             @NonNull SchemaProto newSchema) throws AppSearchException {
-        String prefix = getDatabasePrefix(databaseName);
         HashMap<String, SchemaTypeConfigProto> newTypesToProto = new HashMap<>();
         // Rewrite the schema type to include the typePrefix.
         for (int typeIdx = 0; typeIdx < newSchema.getTypesCount(); typeIdx++) {
@@ -679,10 +710,10 @@
 
         // newTypesToProto is modified below, so we need a copy first
         RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults();
-        rewrittenSchemaResults.mRewrittenQualifiedTypes.addAll(newTypesToProto.keySet());
+        rewrittenSchemaResults.mRewrittenPrefixedTypes.addAll(newTypesToProto.keySet());
 
-        // Combine the existing schema (which may have types from other databases) with this
-        // database's new schema. Modifies the existingSchemaBuilder.
+        // Combine the existing schema (which may have types from other prefixes) with this
+        // prefix's new schema. Modifies the existingSchemaBuilder.
         // Check if we need to replace any old schema types with the new ones.
         for (int i = 0; i < existingSchema.getTypesCount(); i++) {
             String schemaType = existingSchema.getTypes(i).getSchemaType();
@@ -690,11 +721,11 @@
             if (newProto != null) {
                 // Replacement
                 existingSchema.setTypes(i, newProto);
-            } else if (databaseName.equals(getDatabaseName(schemaType))) {
+            } else if (prefix.equals(getPrefix(schemaType))) {
                 // All types existing before but not in newSchema should be removed.
                 existingSchema.removeTypes(i);
                 --i;
-                rewrittenSchemaResults.mDeletedQualifiedTypes.add(schemaType);
+                rewrittenSchemaResults.mDeletedPrefixedTypes.add(schemaType);
             }
         }
         // We've been removing existing types from newTypesToProto, so everything that remains is
@@ -742,17 +773,17 @@
     }
 
     /**
-     * Removes any database names from types and namespaces mentioned anywhere in
+     * Removes any prefixes from types and namespaces mentioned anywhere in
      * {@code documentBuilder}.
      *
      * @param documentBuilder The document to mutate
      */
     @VisibleForTesting
-    static void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder)
+    static void removePrefixesFromDocument(@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()));
+        documentBuilder.setSchema(removePrefix(documentBuilder.getSchema()));
+        documentBuilder.setNamespace(removePrefix(documentBuilder.getNamespace()));
 
         // Recurse into derived documents
         for (int propertyIdx = 0;
@@ -765,7 +796,7 @@
                 for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
                     DocumentProto.Builder derivedDocumentBuilder =
                             propertyBuilder.getDocumentValues(documentIdx).toBuilder();
-                    removeDatabasesFromDocument(derivedDocumentBuilder);
+                    removePrefixesFromDocument(derivedDocumentBuilder);
                     propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
                 }
                 documentBuilder.setProperties(propertyIdx, propertyBuilder);
@@ -774,26 +805,25 @@
     }
 
     /**
-     * Rewrites the schemaTypeFilters and namespacesFilters that exist in {@code databaseNames}.
+     * Rewrites the schemaTypeFilters and namespacesFilters that exist with {@code prefixes}.
      *
-     * <p>If the searchSpec has empty filter lists, all existing databases from
-     * {@code databaseNames} will be added.
+     * <p>If the searchSpec has empty filter lists, all prefixes filters will be added.
      * <p>This method should be only called in query methods and get the READ lock to keep thread
      * safety.
      *
-     * @return false if none of the requested databases exist.
+     * @return false if none of the requested prefixes exist.
      */
     @VisibleForTesting
     @GuardedBy("mReadWriteLock")
-    boolean rewriteSearchSpecForDatabasesLocked(
+    boolean rewriteSearchSpecForPrefixesLocked(
             @NonNull SearchSpecProto.Builder searchSpecBuilder,
-            @NonNull Set<String> databaseNames) {
+            @NonNull Set<String> prefixes) {
         // Create a copy since retainAll() modifies the original set.
-        Set<String> existingDatabases = new ArraySet<>(mNamespaceMapLocked.keySet());
-        existingDatabases.retainAll(databaseNames);
+        Set<String> existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet());
+        existingPrefixes.retainAll(prefixes);
 
-        if (existingDatabases.isEmpty()) {
-            // None of the databases exist, empty query.
+        if (existingPrefixes.isEmpty()) {
+            // None of the prefixes exist, empty query.
             return false;
         }
 
@@ -804,33 +834,32 @@
         List<String> namespaceFilters = searchSpecBuilder.getNamespaceFiltersList();
         searchSpecBuilder.clearNamespaceFilters();
 
-        // Rewrite filters to include a database prefix.
-        for (String databaseName : existingDatabases) {
-            Set<String> existingSchemaTypes = mSchemaMapLocked.get(databaseName);
-            String databaseNamePrefix = getDatabasePrefix(databaseName);
+        // Rewrite filters to include a prefix.
+        for (String prefix : existingPrefixes) {
+            Set<String> existingSchemaTypes = mSchemaMapLocked.get(prefix);
             if (schemaTypeFilters.isEmpty()) {
                 // Include all schema types
                 searchSpecBuilder.addAllSchemaTypeFilters(existingSchemaTypes);
             } else {
-                // Qualify the given schema types
+                // Add the prefix to the given schema types
                 for (int i = 0; i < schemaTypeFilters.size(); i++) {
-                    String qualifiedType = databaseNamePrefix + schemaTypeFilters.get(i);
-                    if (existingSchemaTypes.contains(qualifiedType)) {
-                        searchSpecBuilder.addSchemaTypeFilters(qualifiedType);
+                    String prefixedType = prefix + schemaTypeFilters.get(i);
+                    if (existingSchemaTypes.contains(prefixedType)) {
+                        searchSpecBuilder.addSchemaTypeFilters(prefixedType);
                     }
                 }
             }
 
-            Set<String> existingNamespaces = mNamespaceMapLocked.get(databaseName);
+            Set<String> existingNamespaces = mNamespaceMapLocked.get(prefix);
             if (namespaceFilters.isEmpty()) {
                 // Include all namespaces
                 searchSpecBuilder.addAllNamespaceFilters(existingNamespaces);
             } else {
-                // Qualify the given namespaces.
+                // Prefix the given namespaces.
                 for (int i = 0; i < namespaceFilters.size(); i++) {
-                    String qualifiedNamespace = databaseNamePrefix + namespaceFilters.get(i);
-                    if (existingNamespaces.contains(qualifiedNamespace)) {
-                        searchSpecBuilder.addNamespaceFilters(qualifiedNamespace);
+                    String prefixedNamespace = prefix + namespaceFilters.get(i);
+                    if (existingNamespaces.contains(prefixedNamespace)) {
+                        searchSpecBuilder.addNamespaceFilters(prefixedNamespace);
                     }
                 }
             }
@@ -849,36 +878,43 @@
         return schemaProto.getSchema();
     }
 
-    /** Returns true if {@code databaseName} has a {@code schemaType} */
+    /**
+     * Returns true if the {@code packageName} and {@code databaseName} has the
+     * {@code schemaType}
+     */
     @GuardedBy("mReadWriteLock")
-    boolean hasSchemaTypeLocked(@NonNull String databaseName, @NonNull String schemaType) {
+    boolean hasSchemaTypeLocked(@NonNull String packageName, @NonNull String databaseName,
+            @NonNull String schemaType) {
+        Preconditions.checkNotNull(packageName);
         Preconditions.checkNotNull(databaseName);
         Preconditions.checkNotNull(schemaType);
 
-        Set<String> schemaTypes = mSchemaMapLocked.get(databaseName);
+        String prefix = createPrefix(packageName, databaseName);
+        Set<String> schemaTypes = mSchemaMapLocked.get(prefix);
         if (schemaTypes == null) {
             return false;
         }
 
-        return schemaTypes.contains(getDatabasePrefix(databaseName) + schemaType);
+        return schemaTypes.contains(prefix + schemaType);
     }
 
-    /** Returns a set of all databases AppSearchImpl knows about. */
+    /** Returns a set of all prefixes AppSearchImpl knows about. */
     @GuardedBy("mReadWriteLock")
     @NonNull
-    Set<String> getDatabasesLocked() {
+    Set<String> getPrefixesLocked() {
         return mSchemaMapLocked.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;
+    static String createPrefix(@NonNull String packageName, @NonNull String databaseName) {
+        return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER;
     }
 
     @NonNull
-    private static String removeDatabasePrefix(@NonNull String prefixedString)
+    private static String removePrefix(@NonNull String prefixedString)
             throws AppSearchException {
+        // The prefix is made up of the package, then the database. So we only need to find the
+        // database cutoff.
         int delimiterIndex;
         if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) {
             // Add 1 to include the char size of the DATABASE_DELIMITER
@@ -889,21 +925,23 @@
     }
 
     @NonNull
-    private static String getDatabaseName(@NonNull String prefixedValue) throws AppSearchException {
-        int delimiterIndex = prefixedValue.indexOf(DATABASE_DELIMITER);
-        if (delimiterIndex == -1) {
+    private static String getPrefix(@NonNull String prefixedString) throws AppSearchException {
+        int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER);
+        if (databaseDelimiterIndex == -1) {
             throw new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR,
                     "The databaseName prefixed value doesn't contains a valid database name.");
         }
-        return prefixedValue.substring(0, delimiterIndex);
+
+        // Add 1 to include the char size of the DATABASE_DELIMITER
+        return prefixedString.substring(0, databaseDelimiterIndex + 1);
     }
 
-    private static void addToMap(Map<String, Set<String>> map, String databaseName,
+    private static void addToMap(Map<String, Set<String>> map, String prefix,
             String prefixedValue) {
-        Set<String> values = map.get(databaseName);
+        Set<String> values = map.get(prefix);
         if (values == null) {
             values = new ArraySet<>();
-            map.put(databaseName, values);
+            map.put(prefix, values);
         }
         values.add(prefixedValue);
     }
@@ -983,7 +1021,7 @@
                 SearchResultProto.ResultProto.Builder resultBuilder =
                         searchResultProto.getResults(i).toBuilder();
                 DocumentProto.Builder documentBuilder = resultBuilder.getDocument().toBuilder();
-                removeDatabasesFromDocument(documentBuilder);
+                removePrefixesFromDocument(documentBuilder);
                 resultBuilder.setDocument(documentBuilder);
                 resultsBuilder.setResults(i, resultBuilder);
             }
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java
index b6c2bac..ac8fec2 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java
@@ -52,6 +52,7 @@
         return new SearchResultsImpl(
                 mAppSearchImpl,
                 mExecutorService,
+                /*packageName=*/ null,
                 /*databaseName=*/ null,
                 queryExpression,
                 searchSpec);
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
index 7d87f11..4bb0dbd 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
@@ -23,6 +23,7 @@
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.appsearch.app.AppSearchResult;
 import androidx.appsearch.app.AppSearchSession;
@@ -50,8 +51,13 @@
  * delete, etc..).
  */
 public class LocalStorage {
-    /** The default empty database name.*/
-    private static final String DEFAULT_DATABASE_NAME = "";
+    /**
+     * The default empty database name.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @VisibleForTesting
+    public static final String DEFAULT_DATABASE_NAME = "";
 
     private static volatile ListenableFuture<AppSearchResult<LocalStorage>> sInstance;
 
@@ -262,7 +268,8 @@
     }
 
     AppSearchSession doCreateSearchSession(@NonNull SearchContext context) {
-        return new SearchSessionImpl(mAppSearchImpl, mExecutorService, context.mDatabaseName);
+        return new SearchSessionImpl(mAppSearchImpl, mExecutorService,
+                context.mContext.getPackageName(), context.mDatabaseName);
     }
 
     GlobalSearchSession doCreateGlobalSearchSession() {
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java
index be3876b..4f56f9a 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java
@@ -36,6 +36,11 @@
 
     private final ExecutorService mExecutorService;
 
+    // The package name to search over. If null, this will search over all package names.
+    @Nullable
+    private final String mPackageName;
+
+    // The database name to search over. If null, this will search over all database names.
     @Nullable
     private final String mDatabaseName;
 
@@ -50,11 +55,13 @@
     SearchResultsImpl(
             @NonNull AppSearchImpl appSearchImpl,
             @NonNull ExecutorService executorService,
+            @Nullable String packageName,
             @Nullable String databaseName,
             @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec) {
         mAppSearchImpl = Preconditions.checkNotNull(appSearchImpl);
         mExecutorService = Preconditions.checkNotNull(executorService);
+        mPackageName = packageName;
         mDatabaseName = databaseName;
         mQueryExpression = Preconditions.checkNotNull(queryExpression);
         mSearchSpec = Preconditions.checkNotNull(searchSpec);
@@ -68,14 +75,23 @@
                 SearchResultPage searchResultPage;
                 if (mIsFirstLoad) {
                     mIsFirstLoad = false;
-                    if (mDatabaseName == null) {
-                        // Global query, there's no one database to check.
+                    if (mDatabaseName == null && mPackageName == null) {
+                        // Global query, there's no one package-database combination to check.
                         searchResultPage = mAppSearchImpl.globalQuery(
                                 mQueryExpression, mSearchSpec);
+                    } else if (mPackageName == null) {
+                        return AppSearchResult.newFailedResult(
+                                AppSearchResult.RESULT_INVALID_ARGUMENT,
+                                "Invalid null package name for query");
+                    } else if (mDatabaseName == null) {
+                        return AppSearchResult.newFailedResult(
+                                AppSearchResult.RESULT_INVALID_ARGUMENT,
+                                "Invalid null database name for query");
                     } else {
                         // Normal local query, pass in specified database.
                         searchResultPage = mAppSearchImpl.query(
-                                mDatabaseName, mQueryExpression, mSearchSpec);
+                                mPackageName, mDatabaseName, mQueryExpression, mSearchSpec);
+
                     }
                 } else {
                     searchResultPage = mAppSearchImpl.getNextPage(mNextPageToken);
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 12b25fa..aab31e6 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
@@ -31,10 +31,13 @@
 import androidx.appsearch.app.SearchSpec;
 import androidx.appsearch.app.SetSchemaRequest;
 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.ArrayList;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
@@ -49,14 +52,17 @@
 class SearchSessionImpl implements AppSearchSession {
     private final AppSearchImpl mAppSearchImpl;
     private final ExecutorService mExecutorService;
+    private final String mPackageName;
     private final String mDatabaseName;
 
     SearchSessionImpl(
             @NonNull AppSearchImpl appSearchImpl,
             @NonNull ExecutorService executorService,
+            @NonNull String packageName,
             @NonNull String databaseName) {
         mAppSearchImpl = Preconditions.checkNotNull(appSearchImpl);
         mExecutorService = Preconditions.checkNotNull(executorService);
+        mPackageName = packageName;
         mDatabaseName = Preconditions.checkNotNull(databaseName);
     }
 
@@ -67,8 +73,11 @@
         return execute(() -> {
             try {
                 mAppSearchImpl.setSchema(
-                        mDatabaseName, request.getSchemas(),
-                        request.getSchemasNotPlatformSurfaceable(), request.isForceOverride());
+                        mPackageName,
+                        mDatabaseName,
+                        new ArrayList<>(request.getSchemas()),
+                        new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
+                        request.isForceOverride());
                 return AppSearchResult.newSuccessfulResult(/*value=*/ null);
             } catch (Throwable t) {
                 return throwableToFailedResult(t);
@@ -81,7 +90,9 @@
     public ListenableFuture<AppSearchResult<Set<AppSearchSchema>>> getSchema() {
         return execute(() -> {
             try {
-                return AppSearchResult.newSuccessfulResult(mAppSearchImpl.getSchema(mDatabaseName));
+                List<AppSearchSchema> schemas = mAppSearchImpl.getSchema(mPackageName,
+                        mDatabaseName);
+                return AppSearchResult.newSuccessfulResult(new ArraySet<>(schemas));
             } catch (Throwable t) {
                 return throwableToFailedResult(t);
             }
@@ -99,7 +110,7 @@
             for (int i = 0; i < request.getDocuments().size(); i++) {
                 GenericDocument document = request.getDocuments().get(i);
                 try {
-                    mAppSearchImpl.putDocument(mDatabaseName, document);
+                    mAppSearchImpl.putDocument(mPackageName, mDatabaseName, document);
                     resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
                 } catch (Throwable t) {
                     resultBuilder.setResult(document.getUri(), throwableToFailedResult(t));
@@ -121,7 +132,8 @@
             for (String uri : request.getUris()) {
                 try {
                     GenericDocument document =
-                            mAppSearchImpl.getDocument(mDatabaseName, request.getNamespace(), uri);
+                            mAppSearchImpl.getDocument(mPackageName, mDatabaseName,
+                                    request.getNamespace(), uri);
                     resultBuilder.setSuccess(uri, document);
                 } catch (Throwable t) {
                     resultBuilder.setResult(uri, throwableToFailedResult(t));
@@ -141,6 +153,7 @@
         return new SearchResultsImpl(
                 mAppSearchImpl,
                 mExecutorService,
+                mPackageName,
                 mDatabaseName,
                 queryExpression,
                 searchSpec);
@@ -156,7 +169,7 @@
                     new AppSearchBatchResult.Builder<>();
             for (String uri : request.getUris()) {
                 try {
-                    mAppSearchImpl.remove(mDatabaseName, request.getNamespace(), uri);
+                    mAppSearchImpl.remove(mPackageName, mDatabaseName, request.getNamespace(), uri);
                     resultBuilder.setSuccess(uri, /*result=*/null);
                 } catch (Throwable t) {
                     resultBuilder.setResult(uri, throwableToFailedResult(t));
@@ -174,7 +187,8 @@
         Preconditions.checkNotNull(searchSpec);
         return execute(() -> {
             try {
-                mAppSearchImpl.removeByQuery(mDatabaseName, queryExpression, searchSpec);
+                mAppSearchImpl.removeByQuery(mPackageName, mDatabaseName, queryExpression,
+                        searchSpec);
                 return AppSearchResult.newSuccessfulResult(null);
             } catch (Throwable t) {
                 return throwableToFailedResult(t);
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
index a95231f..186cf93 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/VisibilityStore.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/VisibilityStore.java
@@ -50,26 +50,57 @@
  * 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
+    /** 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.
+    /**
+     * Property that holds the list of platform-hidden schemas, as part of the visibility settings.
+     */
     @VisibleForTesting
     static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
-    // Database name to prefix all visibility schemas and documents with. Special-cased to
-    // minimize the chance of collision with a client-supplied database.
 
+    /** Schema for the VisibilityStore's docuemnts. */
     @VisibleForTesting
-    static final String DATABASE_NAME = "$$__AppSearch__Database";
+    static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
+            .addProperty(new AppSearchSchema.PropertyConfig.Builder(
+                    NOT_PLATFORM_SURFACEABLE_PROPERTY)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(
+                            AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                    .build())
+            .build();
 
-    // Namespace of documents that contain visibility settings
-    private static final String NAMESPACE = "namespace";
+    /**
+     * These cannot have any of the special characters used by AppSearchImpl (e.g.
+     * {@link AppSearchImpl#PACKAGE_DELIMITER} or {@link AppSearchImpl#DATABASE_DELIMITER}.
+     */
+    static final String PACKAGE_NAME = "VS#Pkg";
+    static final String DATABASE_NAME = "VS#Db";
+
+    /**
+     * Prefix that AppSearchImpl creates for the VisibilityStore based on our package name and
+     * database name. Tracked here to tell when we're looking at our own prefix when looking
+     * through AppSearchImpl.
+     */
+    private static final String VISIBILITY_STORE_PREFIX = AppSearchImpl.createPrefix(PACKAGE_NAME,
+            DATABASE_NAME);
+
+    /** Namespace of documents that contain visibility settings */
+    private static final String NAMESPACE = GenericDocument.DEFAULT_NAMESPACE;
+
+    /**
+     * Prefix to add to all visibility document uri's. IcingSearchEngine doesn't allow empty
+     * uri's.
+     */
+    private static final String URI_PREFIX = "uri:";
+
     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.
+    /**
+     * Maps prefixes to the set of schemas that are platform-hidden within that prefix. All schemas
+     * in the map are prefixed.
+     */
     private final Map<String, Set<String>> mNotPlatformSurfaceableMap = new ArrayMap<>();
 
     /**
@@ -94,36 +125,31 @@
      * @throws AppSearchException AppSearchException on AppSearchImpl error.
      */
     public void initialize() throws AppSearchException {
-        if (!mAppSearchImpl.hasSchemaTypeLocked(DATABASE_NAME, SCHEMA_TYPE)) {
+        if (!mAppSearchImpl.hasSchemaTypeLocked(PACKAGE_NAME, 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(
-                                    NOT_PLATFORM_SURFACEABLE_PROPERTY)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
-                                    .setCardinality(
-                                            AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                                    .build())
-                            .build()),
-                    /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
+            mAppSearchImpl.setSchema(PACKAGE_NAME, DATABASE_NAME,
+                    Collections.singletonList(SCHEMA),
+                    /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
                     /*forceOverride=*/ false);
         }
 
-        // Populate visibility settings map
-        for (String database : mAppSearchImpl.getDatabasesLocked()) {
-            if (database.equals(DATABASE_NAME)) {
-                // Our own database. Skip
+        // Populate visibility settings set
+        mNotPlatformSurfaceableMap.clear();
+        for (String prefix : mAppSearchImpl.getPrefixesLocked()) {
+            if (prefix.equals(VISIBILITY_STORE_PREFIX)) {
+                // Our own prefix. Skip
                 continue;
             }
 
             try {
-                // Note: We use the other clients' database names as uris
+                // Note: We use the other clients' prefixed names as uris
                 GenericDocument document = mAppSearchImpl.getDocument(
-                        DATABASE_NAME, NAMESPACE, /*uri=*/ database);
+                        PACKAGE_NAME, DATABASE_NAME, NAMESPACE, /*uri=*/ addUriPrefix(prefix));
 
                 String[] schemas = document.getPropertyStringArray(
                         NOT_PLATFORM_SURFACEABLE_PROPERTY);
-                mNotPlatformSurfaceableMap.put(database, new ArraySet<>(Arrays.asList(schemas)));
+                mNotPlatformSurfaceableMap.put(prefix,
+                        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
@@ -138,61 +164,63 @@
     }
 
     /**
-     * Sets visibility settings for {@code databaseName}. Any previous visibility settings will be
+     * Sets visibility settings for {@code prefix}. Any previous visibility settings will be
      * overwritten.
      *
-     * @param databaseName                  Database name that owns the {@code
+     * @param prefix                        Prefix that identifies who owns the {@code
      *                                      schemasNotPlatformSurfaceable}.
-     * @param schemasNotPlatformSurfaceable Set of database-qualified schemas that should be
-     *                                      hidden from
-     *                                      the platform.
+     * @param schemasNotPlatformSurfaceable Set of prefixed schemas that should be
+     *                                      hidden from the platform.
      * @throws AppSearchException on AppSearchImpl error.
      */
-    public void setVisibility(@NonNull String databaseName,
+    public void setVisibility(@NonNull String prefix,
             @NonNull Set<String> schemasNotPlatformSurfaceable) throws AppSearchException {
-        Preconditions.checkNotNull(databaseName);
+        Preconditions.checkNotNull(prefix);
         Preconditions.checkNotNull(schemasNotPlatformSurfaceable);
 
         // Persist the document
         GenericDocument.Builder visibilityDocument = new GenericDocument.Builder(
-                /*uri=*/ databaseName, SCHEMA_TYPE)
+                /*uri=*/ addUriPrefix(prefix), SCHEMA_TYPE)
                 .setNamespace(NAMESPACE);
         if (!schemasNotPlatformSurfaceable.isEmpty()) {
             visibilityDocument.setPropertyString(NOT_PLATFORM_SURFACEABLE_PROPERTY,
                     schemasNotPlatformSurfaceable.toArray(new String[0]));
         }
-        mAppSearchImpl.putDocument(DATABASE_NAME, visibilityDocument.build());
+        mAppSearchImpl.putDocument(PACKAGE_NAME, DATABASE_NAME, visibilityDocument.build());
 
         // Update derived data structures.
-        mNotPlatformSurfaceableMap.put(databaseName, schemasNotPlatformSurfaceable);
+        mNotPlatformSurfaceableMap.put(prefix, schemasNotPlatformSurfaceable);
     }
 
-    /**
-     * 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.
-     */
+    /** Returns if the schema is surfaceable by the platform. */
     @NonNull
-    public Set<String> getSchemasNotPlatformSurfaceable(@NonNull String databaseName) {
-        Preconditions.checkNotNull(databaseName);
-        Set<String> schemasNotPlatformSurfaceable = mNotPlatformSurfaceableMap.get(databaseName);
-        if (schemasNotPlatformSurfaceable == null) {
-            return Collections.emptySet();
+    public boolean isSchemaPlatformSurfaceable(@NonNull String prefix,
+            @NonNull String prefixedSchema) {
+        Preconditions.checkNotNull(prefix);
+        Preconditions.checkNotNull(prefixedSchema);
+        Set<String> notPlatformSurfaceableSchemas = mNotPlatformSurfaceableMap.get(prefix);
+        if (notPlatformSurfaceableSchemas == null) {
+            return true;
         }
-        return schemasNotPlatformSurfaceable;
+        return !notPlatformSurfaceableSchemas.contains(prefixedSchema);
     }
 
     /**
-     * Handles an {@link AppSearchImpl#reset()} by clearing any cached state and resetting to a
-     * first-initialized state.
+     * Handles an {@code AppSearchImpl#reset()} by clearing any cached state.
      *
-     * @throws AppSearchException on AppSearchImpl error.
+     * <p> {@link #initialize()} must be called after this.
      */
-    public void handleReset() throws AppSearchException {
+    void handleReset() {
         mNotPlatformSurfaceableMap.clear();
-        initialize();
+    }
+
+    /**
+     * Adds a uri prefix to create a visibility store document's uri.
+     *
+     * @param uri Non-prefixed uri
+     * @return Prefixed uri
+     */
+    private static String addUriPrefix(String uri) {
+        return URI_PREFIX + uri;
     }
 }
diff --git a/autofill/autofill/src/androidTest/java/androidx/autofill/inline/RendererTest.java b/autofill/autofill/src/androidTest/java/androidx/autofill/inline/RendererTest.java
index c186e75..cb51c64 100644
--- a/autofill/autofill/src/androidTest/java/androidx/autofill/inline/RendererTest.java
+++ b/autofill/autofill/src/androidTest/java/androidx/autofill/inline/RendererTest.java
@@ -40,7 +40,6 @@
 import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -56,10 +55,11 @@
 @SdkSuppress(minSdkVersion = 30) // Needed only on 30 and above
 public class RendererTest {
 
+    @SuppressWarnings("deprecation")
     @Rule
     @NonNull
-    public final ActivityTestRule<InlineUiActivity> mActivityTestRule =
-            new ActivityTestRule<>(InlineUiActivity.class);
+    public final androidx.test.rule.ActivityTestRule<InlineUiActivity> mActivityTestRule =
+            new androidx.test.rule.ActivityTestRule<>(InlineUiActivity.class);
 
     private Instrumentation mInstrumentation;
     private Context mContext;
diff --git a/autofill/autofill/src/androidTest/java/androidx/autofill/inline/v1/InlineSuggestionUiTest.java b/autofill/autofill/src/androidTest/java/androidx/autofill/inline/v1/InlineSuggestionUiTest.java
index e26e5af..943322b 100644
--- a/autofill/autofill/src/androidTest/java/androidx/autofill/inline/v1/InlineSuggestionUiTest.java
+++ b/autofill/autofill/src/androidTest/java/androidx/autofill/inline/v1/InlineSuggestionUiTest.java
@@ -51,7 +51,6 @@
 import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -71,10 +70,11 @@
     private static final String TITLE = "Hello world!";
     private static final String SUB_TITLE = "From God";
 
+    @SuppressWarnings("deprecation")
     @Rule
     @NonNull
-    public final ActivityTestRule<InlineUiActivity> mActivityTestRule =
-            new ActivityTestRule<>(InlineUiActivity.class);
+    public final androidx.test.rule.ActivityTestRule<InlineUiActivity> mActivityTestRule =
+            new androidx.test.rule.ActivityTestRule<>(InlineUiActivity.class);
 
     private Instrumentation mInstrumentation;
     private Context mContext;
diff --git a/benchmark/common/api/current.txt b/benchmark/common/api/current.txt
index c023743..167ebf4 100644
--- a/benchmark/common/api/current.txt
+++ b/benchmark/common/api/current.txt
@@ -4,6 +4,9 @@
   public final class ArgumentsKt {
   }
 
+  public final class BenchmarkResultKt {
+  }
+
   public final class BenchmarkState {
     method public boolean keepRunning();
     method public void pauseTiming();
diff --git a/benchmark/common/api/public_plus_experimental_current.txt b/benchmark/common/api/public_plus_experimental_current.txt
index dc2f41d..e54d1d6 100644
--- a/benchmark/common/api/public_plus_experimental_current.txt
+++ b/benchmark/common/api/public_plus_experimental_current.txt
@@ -4,6 +4,34 @@
   public final class ArgumentsKt {
   }
 
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class BenchmarkResult {
+    ctor public BenchmarkResult(String className, String testName, long totalRunTimeNs, java.util.List<androidx.benchmark.MetricResult> metrics, int repeatIterations, long thermalThrottleSleepSeconds, int warmupIterations);
+    method public String component1();
+    method public String component2();
+    method public long component3();
+    method public java.util.List<androidx.benchmark.MetricResult> component4();
+    method public int component5();
+    method public long component6();
+    method public int component7();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.benchmark.BenchmarkResult copy(String className, String testName, long totalRunTimeNs, java.util.List<androidx.benchmark.MetricResult> metrics, int repeatIterations, long thermalThrottleSleepSeconds, int warmupIterations);
+    method public String getClassName();
+    method public java.util.List<androidx.benchmark.MetricResult> getMetrics();
+    method public int getRepeatIterations();
+    method public androidx.benchmark.Stats getStats(String which);
+    method public String getTestName();
+    method public int getWarmupIterations();
+    property public final String className;
+    property public final java.util.List<androidx.benchmark.MetricResult> metrics;
+    property public final int repeatIterations;
+    property public final String testName;
+    property public final int warmupIterations;
+    field public final long thermalThrottleSleepSeconds;
+    field public final long totalRunTimeNs;
+  }
+
+  public final class BenchmarkResultKt {
+  }
+
   public final class BenchmarkState {
     ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public BenchmarkState();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public long getMinTimeNanos();
@@ -40,9 +68,26 @@
   public final class MetricNameUtilsKt {
   }
 
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class MetricResult {
+    ctor public MetricResult(java.util.List<java.lang.Long> data, androidx.benchmark.Stats stats);
+    ctor public MetricResult(String name, long[] data);
+    method public java.util.List<java.lang.Long> component1();
+    method public androidx.benchmark.Stats component2();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.benchmark.MetricResult copy(java.util.List<java.lang.Long> data, androidx.benchmark.Stats stats);
+    method public java.util.List<java.lang.Long> getData();
+    method public androidx.benchmark.Stats getStats();
+    property public final java.util.List<java.lang.Long> data;
+    property public final androidx.benchmark.Stats stats;
+  }
+
   public final class ProfilerKt {
   }
 
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class ResultWriter {
+    method public void appendReport(androidx.benchmark.BenchmarkResult benchmarkResult);
+    field public static final androidx.benchmark.ResultWriter INSTANCE;
+  }
+
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class Stats {
     ctor public Stats(long[] data, String name);
     method public long getMax();
diff --git a/benchmark/common/api/restricted_current.txt b/benchmark/common/api/restricted_current.txt
index 84aef03..b59d142 100644
--- a/benchmark/common/api/restricted_current.txt
+++ b/benchmark/common/api/restricted_current.txt
@@ -4,6 +4,9 @@
   public final class ArgumentsKt {
   }
 
+  public final class BenchmarkResultKt {
+  }
+
   public final class BenchmarkState {
     method public boolean keepRunning();
     method @kotlin.PublishedApi internal boolean keepRunningInternal();
diff --git a/benchmark/common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt b/benchmark/common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
index f65209e..ef9b83d 100644
--- a/benchmark/common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
+++ b/benchmark/common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
@@ -262,12 +262,16 @@
             thermalThrottleSleepSeconds = 0,
             repeatIterations = 1
         )
-        val expectedReport = BenchmarkState.Report(
+        val expectedReport = BenchmarkResult(
             className = "className",
             testName = "testName",
             totalRunTimeNs = 900000000,
-            data = listOf(listOf(100L, 200L, 300L)),
-            stats = listOf(Stats(longArrayOf(100, 200, 300), "timeNs")),
+            metrics = listOf(
+                MetricResult(
+                    name = "timeNs",
+                    data = longArrayOf(100, 200, 300)
+                )
+            ),
             repeatIterations = 1,
             thermalThrottleSleepSeconds = 0,
             warmupIterations = 1
diff --git a/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt b/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
index cc7513a..cae9a18 100644
--- a/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
+++ b/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
@@ -28,30 +28,31 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class ResultWriterTest {
-
+public class ResultWriterTest {
     @get:Rule
     val tempFolder = TemporaryFolder()
 
-    private val data = arrayOf(longArrayOf(100, 101, 102))
-    private val names = listOf("timeNs")
+    private val metricResults = listOf(
+        MetricResult(
+            name = "timeNs",
+            data = longArrayOf(100L, 101L, 102L)
+        )
+    )
 
-    private val reportA = BenchmarkState.Report(
+    private val reportA = BenchmarkResult(
         testName = "MethodA",
         className = "package.Class1",
         totalRunTimeNs = 900000000,
-        data = data.map { it.toList() },
-        stats = data.mapIndexed { i, it -> Stats(it, names[i]) },
+        metrics = metricResults,
         repeatIterations = 100000,
         thermalThrottleSleepSeconds = 90000000,
         warmupIterations = 8000
     )
-    private val reportB = BenchmarkState.Report(
+    private val reportB = BenchmarkResult(
         testName = "MethodB",
         className = "package.Class2",
         totalRunTimeNs = 900000000,
-        data = data.map { it.toList() },
-        stats = data.mapIndexed { i, it -> Stats(it, names[i]) },
+        metrics = metricResults,
         repeatIterations = 100000,
         thermalThrottleSleepSeconds = 90000000,
         warmupIterations = 8000
@@ -145,12 +146,11 @@
 
     @Test
     fun validateJsonWithParams() {
-        val reportWithParams = BenchmarkState.Report(
+        val reportWithParams = BenchmarkResult(
             testName = "MethodWithParams[number=2,primeNumber=true]",
             className = "package.Class",
             totalRunTimeNs = 900000000,
-            data = data.map { it.toList() },
-            stats = data.mapIndexed { i, it -> Stats(it, names[i]) },
+            metrics = metricResults,
             repeatIterations = 100000,
             thermalThrottleSleepSeconds = 90000000,
             warmupIterations = 8000
@@ -175,12 +175,11 @@
 
     @Test
     fun validateJsonWithInvalidParams() {
-        val reportWithInvalidParams = BenchmarkState.Report(
+        val reportWithInvalidParams = BenchmarkResult(
             testName = "MethodWithParams[number=2,=true,]",
             className = "package.Class",
             totalRunTimeNs = 900000000,
-            data = data.map { it.toList() },
-            stats = data.mapIndexed { i, it -> Stats(it, names[i]) },
+            metrics = metricResults,
             repeatIterations = 100000,
             thermalThrottleSleepSeconds = 90000000,
             warmupIterations = 8000
diff --git a/benchmark/common/src/main/java/androidx/benchmark/BenchmarkResult.kt b/benchmark/common/src/main/java/androidx/benchmark/BenchmarkResult.kt
new file mode 100644
index 0000000..7129b3c
--- /dev/null
+++ b/benchmark/common/src/main/java/androidx/benchmark/BenchmarkResult.kt
@@ -0,0 +1,63 @@
+/*
+ * 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
+
+import androidx.annotation.RestrictTo
+
+/**
+ * Data for a single metric from a single benchmark test method run.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public data class MetricResult(
+    val data: List<Long>,
+    val stats: Stats
+) {
+    public constructor(
+        name: String,
+        data: LongArray
+    ) : this(data.toList(), Stats(data, name))
+}
+
+/**
+ * Data capture from a single benchmark test method run.
+ *
+ * Each field directly corresponds to JSON output, though not every JSON object may be
+ * represented directly here.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public data class BenchmarkResult(
+    val className: String,
+    val testName: String,
+    @JvmField // Suppress API lint (using JvmField instead of @Suppress to workaround b/175063229))
+    val totalRunTimeNs: Long,
+    val metrics: List<MetricResult>,
+    val repeatIterations: Int,
+    @JvmField // Suppress API lint (using JvmField instead of @Suppress to workaround b/175063229))
+    val thermalThrottleSleepSeconds: Long,
+    val warmupIterations: Int
+) {
+    public fun getStats(which: String): Stats {
+        return metrics.first { it.stats.name == which }.stats
+    }
+}
+
+internal fun metricResultList(stats: List<Stats>, data: List<LongArray>): List<MetricResult> {
+    require(stats.size == data.size)
+    return stats.mapIndexed { index, currentStats ->
+        MetricResult(data[index].toList(), currentStats)
+    }
+}
\ No newline at end of file
diff --git a/benchmark/common/src/main/java/androidx/benchmark/BenchmarkState.kt b/benchmark/common/src/main/java/androidx/benchmark/BenchmarkState.kt
index c74b069..6b66d0a 100644
--- a/benchmark/common/src/main/java/androidx/benchmark/BenchmarkState.kt
+++ b/benchmark/common/src/main/java/androidx/benchmark/BenchmarkState.kt
@@ -445,27 +445,11 @@
             " Call BenchmarkState.resumeTiming() before BenchmarkState.keepRunning()."
     }
 
-    internal data class Report(
-        val className: String,
-        val testName: String,
-        val totalRunTimeNs: Long,
-        val data: List<List<Long>>,
-        val stats: List<Stats>,
-        val repeatIterations: Int,
-        val thermalThrottleSleepSeconds: Long,
-        val warmupIterations: Int
-    ) {
-        fun getStats(which: String): Stats {
-            return stats.first { it.name == which }
-        }
-    }
-
-    private fun getReport(testName: String, className: String) = Report(
+    private fun getReport(testName: String, className: String) = BenchmarkResult(
         className = className,
         testName = testName,
         totalRunTimeNs = totalRunTimeNs,
-        data = allData.map { it.toList() },
-        stats = stats,
+        metrics = metricResultList(stats, allData),
         repeatIterations = iterationsPerRepeat,
         thermalThrottleSleepSeconds = thermalThrottleSleepSeconds,
         warmupIterations = warmupRepeats
@@ -609,12 +593,14 @@
         ) {
             val metricsContainer = MetricsContainer(REPEAT_COUNT = dataNs.size)
             metricsContainer.data[metricsContainer.data.lastIndex] = dataNs.toLongArray()
-            val report = Report(
+            val report = BenchmarkResult(
                 className = className,
                 testName = testName,
                 totalRunTimeNs = totalRunTimeNs,
-                data = metricsContainer.data.map { it.toList() },
-                stats = metricsContainer.captureFinished(maxIterations = 1),
+                metrics = metricResultList(
+                    stats = metricsContainer.captureFinished(maxIterations = 1),
+                    data = metricsContainer.data.toList()
+                ),
                 repeatIterations = repeatIterations,
                 thermalThrottleSleepSeconds = thermalThrottleSleepSeconds,
                 warmupIterations = warmupIterations
diff --git a/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt b/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
index c62a72fd..7f0219c 100644
--- a/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
+++ b/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
@@ -19,17 +19,20 @@
 import android.os.Build
 import android.util.JsonWriter
 import android.util.Log
+import androidx.annotation.RestrictTo
 import androidx.annotation.VisibleForTesting
 import androidx.test.platform.app.InstrumentationRegistry
 import java.io.File
 import java.io.IOException
 
-internal object ResultWriter {
-    @VisibleForTesting
-    internal val reports = ArrayList<BenchmarkState.Report>()
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public object ResultWriter {
 
-    fun appendReport(report: BenchmarkState.Report) {
-        reports.add(report)
+    @VisibleForTesting
+    internal val reports = ArrayList<BenchmarkResult>()
+
+    public fun appendReport(benchmarkResult: BenchmarkResult) {
+        reports.add(benchmarkResult)
 
         if (Arguments.outputEnable) {
             // Currently, we just overwrite the whole file
@@ -59,7 +62,7 @@
     }
 
     @VisibleForTesting
-    internal fun writeReport(file: File, reports: List<BenchmarkState.Report>) {
+    internal fun writeReport(file: File, benchmarkResults: List<BenchmarkResult>) {
         file.run {
             if (!exists()) {
                 parentFile?.mkdirs()
@@ -97,7 +100,7 @@
             writer.endObject()
 
             writer.name("benchmarks").beginArray()
-            reports.forEach { writer.reportObject(it) }
+            benchmarkResults.forEach { writer.reportObject(it) }
             writer.endArray()
 
             writer.endObject()
@@ -116,16 +119,16 @@
         return endObject()
     }
 
-    private fun JsonWriter.reportObject(report: BenchmarkState.Report): JsonWriter {
+    private fun JsonWriter.reportObject(benchmarkResult: BenchmarkResult): JsonWriter {
         beginObject()
-            .name("name").value(report.testName)
-            .name("params").paramsObject(report)
-            .name("className").value(report.className)
-            .name("totalRunTimeNs").value(report.totalRunTimeNs)
-            .name("metrics").metricsContainerObject(report.stats, report.data)
-            .name("warmupIterations").value(report.warmupIterations)
-            .name("repeatIterations").value(report.repeatIterations)
-            .name("thermalThrottleSleepSeconds").value(report.thermalThrottleSleepSeconds)
+            .name("name").value(benchmarkResult.testName)
+            .name("params").paramsObject(benchmarkResult)
+            .name("className").value(benchmarkResult.className)
+            .name("totalRunTimeNs").value(benchmarkResult.totalRunTimeNs)
+            .name("metrics").metricsContainerObject(benchmarkResult.metrics)
+            .name("warmupIterations").value(benchmarkResult.warmupIterations)
+            .name("repeatIterations").value(benchmarkResult.repeatIterations)
+            .name("thermalThrottleSleepSeconds").value(benchmarkResult.thermalThrottleSleepSeconds)
         return endObject()
     }
 
@@ -139,24 +142,23 @@
     }
 
     private fun JsonWriter.metricsContainerObject(
-        stats: List<Stats>,
-        data: List<List<Long>>
+        metricResults: List<MetricResult>
     ): JsonWriter {
         beginObject()
-        for (i in 0..stats.lastIndex) {
-            name(stats[i].name).beginObject()
-            statsObject(stats[i])
+        metricResults.forEach {
+            name(it.stats.name).beginObject()
+            statsObject(it.stats)
             name("runs").beginArray()
-            data[i].forEach { value(it) }
+            it.data.forEach { value(it) }
             endArray()
             endObject()
         }
         return endObject()
     }
 
-    private fun JsonWriter.paramsObject(report: BenchmarkState.Report): JsonWriter {
+    private fun JsonWriter.paramsObject(benchmarkResult: BenchmarkResult): JsonWriter {
         beginObject()
-        getParams(report.testName).forEach { name(it.key).value(it.value) }
+        getParams(benchmarkResult.testName).forEach { name(it.key).value(it.value) }
         return endObject()
     }
 
diff --git a/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh b/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh
index df929f4..b33734b 100755
--- a/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh
+++ b/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh
@@ -94,6 +94,19 @@
     fi
 }
 
+# Disable CPU hotpluging by killing mpdecision service via ctl.stop system property.
+# This helper checks the state and existence of the mpdecision service via init.svc.
+# Possible values from init.svc: "stopped", "stopping", "running", "restarting"
+function_stop_mpdecision() {
+    MPDECISION_STATUS=`getprop init.svc.mpdecision`
+    while [ "$MPDECISION_STATUS" == "running" ] || [ "$MPDECISION_STATUS" == "restarting" ]; do
+        setprop ctl.stop mpdecision
+        # Give initrc some time to kill the mpdecision service.
+        sleep 0.1
+        MPDECISION_STATUS=`getprop init.svc.mpdecision`
+    done
+}
+
 # Find the min or max (little vs big) of CPU max frequency, and lock cores of the selected type to
 # an available frequency that's >= $CPU_TARGET_FREQ_PERCENT% of max. Disable other cores.
 function_lock_cpu() {
@@ -111,10 +124,14 @@
     disableIndices=''
     cpu=0
 
+    # Stop mpdecision (CPU hotplug service) if it exists. Not available on all devices.
+    function_stop_mpdecision
+
     # Loop through all available cores; We have to check by the parent folder
     # "cpu#" instead of cpu#/online or cpu#/cpufreq directly, since they may
     # not be accessible yet.
     while [ -d ${CPU_BASE}/cpu${cpu} ]; do
+
         # Try to enable core, so we can find its frequencies.
         # Note: In cases where the online file is inaccessible, it represents a
         # core which cannot be turned off, so we simply assume it is enabled if
diff --git a/benchmark/integration-tests/crystalball-experiment/src/scripts/copy_crystalball_prebuilts.py b/benchmark/integration-tests/crystalball-experiment/src/scripts/copy_crystalball_prebuilts.py
index 1102366..e3880a7 100755
--- a/benchmark/integration-tests/crystalball-experiment/src/scripts/copy_crystalball_prebuilts.py
+++ b/benchmark/integration-tests/crystalball-experiment/src/scripts/copy_crystalball_prebuilts.py
@@ -13,7 +13,7 @@
 def main():
     parser = argparse.ArgumentParser(description='Copy Crystalball Prebuilts')
     parser.add_argument('soong_path', action="store", help='Looks like "/usr/local/google/home/rahulrav/Workspace/internal_android/out/soong/.intermediates"')
-    parser.add_argument('prebuilts_directory', action="store", help='Looks like "/mnt/Android/Flatfoot/androidx_master/prebuilts/androidx/external/com/android/"')
+    parser.add_argument('prebuilts_directory', action="store", help='Looks like "/mnt/Android/Flatfoot/androidx_main/prebuilts/androidx/external/com/android/"')
     parser.add_argument('--verbose', action="store_true", default=False)
     parse_result = parser.parse_args()
 
diff --git a/benchmark/integration-tests/macrobenchmark-target/build.gradle b/benchmark/integration-tests/macrobenchmark-target/build.gradle
index 1e72999..4de1d5d 100644
--- a/benchmark/integration-tests/macrobenchmark-target/build.gradle
+++ b/benchmark/integration-tests/macrobenchmark-target/build.gradle
@@ -22,8 +22,18 @@
     id("kotlin-android")
 }
 
+android {
+    buildTypes {
+        release {
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
+        }
+    }
+}
+
 dependencies {
-    api(KOTLIN_STDLIB)
+    implementation(KOTLIN_STDLIB)
     implementation(CONSTRAINT_LAYOUT, { transitive = true })
     implementation("androidx.arch.core:core-runtime:2.1.0")
     implementation("androidx.appcompat:appcompat:1.2.0")
diff --git a/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/TrivialStartupActivity.kt b/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/TrivialStartupActivity.kt
index 5cf2081..52792d1 100644
--- a/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/TrivialStartupActivity.kt
+++ b/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/TrivialStartupActivity.kt
@@ -16,11 +16,8 @@
 
 package androidx.benchmark.integration.macrobenchmark.target
 
-import android.app.Activity
-import android.os.Build
 import android.os.Bundle
 import android.widget.TextView
-import androidx.annotation.RequiresApi
 import androidx.appcompat.app.AppCompatActivity
 
 class TrivialStartupActivity : AppCompatActivity() {
@@ -30,18 +27,5 @@
 
         val notice = findViewById<TextView>(R.id.txtNotice)
         notice.setText(R.string.app_notice)
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            KitkatActivityCompat.reportFullyDrawn(this)
-        }
-    }
-}
-
-/**
- * Wrapper avoids UnsafeApiCall lint
- */
-@RequiresApi(19)
-object KitkatActivityCompat {
-    fun reportFullyDrawn(activity: Activity) {
-        activity.reportFullyDrawn()
     }
 }
diff --git a/benchmark/integration-tests/macrobenchmark-target/src/main/res/layout/recycler_row.xml b/benchmark/integration-tests/macrobenchmark-target/src/main/res/layout/recycler_row.xml
index fa8493f..7e77ae3 100644
--- a/benchmark/integration-tests/macrobenchmark-target/src/main/res/layout/recycler_row.xml
+++ b/benchmark/integration-tests/macrobenchmark-target/src/main/res/layout/recycler_row.xml
@@ -13,46 +13,32 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<androidx.cardview.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/card"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="64dp"
-    android:padding="8dp">
-
-    <androidx.cardview.widget.CardView
-        android:id="@+id/card"
+    android:layout_margin="8dp">
+    <androidx.appcompat.widget.LinearLayoutCompat
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:padding="8dp">
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
 
-        <androidx.constraintlayout.widget.ConstraintLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/content"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="16dp"
+            tools:text="Sample text" />
+        <Space
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1" />
 
-            <androidx.appcompat.widget.AppCompatCheckBox
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="end"
-                android:layout_marginEnd="16dp"
-                android:layout_marginRight="16dp"
-                android:layout_marginTop="8dp"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintTop_toTopOf="parent" />
-
-            <androidx.appcompat.widget.AppCompatTextView
-                android:id="@+id/content"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginLeft="16dp"
-                android:layout_marginStart="16dp"
-                android:layout_marginTop="8dp"
-                android:padding="8dp"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toTopOf="parent"
-                tools:text="Sample text" />
-        </androidx.constraintlayout.widget.ConstraintLayout>
-    </androidx.cardview.widget.CardView>
-</LinearLayout>
\ No newline at end of file
+        <androidx.appcompat.widget.AppCompatCheckBox
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="16dp" />
+    </androidx.appcompat.widget.LinearLayoutCompat>
+</androidx.cardview.widget.CardView>
\ No newline at end of file
diff --git a/benchmark/integration-tests/macrobenchmark/build.gradle b/benchmark/integration-tests/macrobenchmark/build.gradle
index 3384fe8..58fb8e8 100644
--- a/benchmark/integration-tests/macrobenchmark/build.gradle
+++ b/benchmark/integration-tests/macrobenchmark/build.gradle
@@ -30,6 +30,7 @@
 android {
     defaultConfig {
         minSdkVersion 28
+        testInstrumentationRunnerArgument 'androidx.benchmark.output.enable', 'true'
     }
 }
 
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml b/benchmark/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
index 2f8615f..6dd48d3 100644
--- a/benchmark/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
+++ b/benchmark/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
@@ -28,4 +28,5 @@
     <queries>
         <package android:name="androidx.benchmark.integration.macrobenchmark.target" />
     </queries>
+    <application android:requestLegacyExternalStorage="true"/>
 </manifest>
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index 4a88391..0586e56 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -18,8 +18,10 @@
 
 import android.content.Intent
 import android.util.Log
+import androidx.benchmark.BenchmarkResult
 import androidx.benchmark.InstrumentationResults
-import androidx.benchmark.Stats
+import androidx.benchmark.MetricResult
+import androidx.benchmark.ResultWriter
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.UiDevice
@@ -93,12 +95,15 @@
  * This function is a building block for public testing APIs
  */
 fun macrobenchmark(
-    benchmarkName: String,
+    uniqueName: String,
+    className: String,
+    testName: String,
     config: MacrobenchmarkConfig,
     launchWithClearTask: Boolean,
     setupBlock: MacrobenchmarkScope.(Boolean) -> Unit,
     measureBlock: MacrobenchmarkScope.() -> Unit
 ) = withPermissiveSeLinuxPolicy {
+    val startTime = System.nanoTime()
     val scope = MacrobenchmarkScope(config.packageName, launchWithClearTask)
 
     // always kill the process at beginning of test
@@ -117,7 +122,7 @@
             it.configure(config)
         }
         var isFirstRun = true
-        val results = List(config.iterations) { iteration ->
+        val metricResults = List(config.iterations) { iteration ->
             setupBlock(scope, isFirstRun)
             isFirstRun = false
             try {
@@ -130,7 +135,7 @@
                 config.metrics.forEach {
                     it.stop()
                 }
-                perfettoCollector.stop(benchmarkName, iteration)
+                perfettoCollector.stop(uniqueName, iteration)
             }
 
             config.metrics
@@ -138,32 +143,55 @@
                 .map { it.getMetrics(config.packageName) }
                 // merge into one map
                 .reduce { sum, element -> sum + element }
-        }
-
-        // merge each independent Map<String,Long> to one Map<String,List<Long>>
-        val setOfAllKeys = results.flatMap { it.keys }.toSet()
-        val listResults = setOfAllKeys.map { key ->
-            // b/174175947
-            key to results.mapNotNull {
-                if (key !in it) {
-                    Log.w(TAG, "Value $key missing from one iteration {$it}")
-                }
-                it[key]
-            }
-        }.toMap()
-        val statsList = listResults.map { (metricName, values) ->
-            Stats(values.toLongArray(), metricName)
-        }.sortedBy { it.name }
+        }.mergeToMetricResults()
 
         InstrumentationResults.instrumentationReport {
-            ideSummaryRecord(ideSummaryString(benchmarkName, statsList))
+            val statsList = metricResults.map { it.stats }
+            ideSummaryRecord(ideSummaryString(uniqueName, statsList))
             statsList.forEach { it.putInBundle(bundle, "") }
         }
+
+        val warmupIterations = if (config.compilationMode is CompilationMode.SpeedProfile) {
+            config.compilationMode.warmupIterations
+        } else {
+            0
+        }
+
+        ResultWriter.appendReport(
+            BenchmarkResult(
+                className = className,
+                testName = testName,
+                totalRunTimeNs = System.nanoTime() - startTime,
+                metrics = metricResults,
+                repeatIterations = config.iterations,
+                thermalThrottleSleepSeconds = 0,
+                warmupIterations = warmupIterations
+            )
+        )
     } finally {
         scope.killProcess()
     }
 }
 
+/**
+ * Merge the Map<String, Long> results from each iteration into one Map<MetricResult>
+ */
+private fun List<Map<String, Long>>.mergeToMetricResults(): List<MetricResult> {
+    val setOfAllKeys = flatMap { it.keys }.toSet()
+    val listResults = setOfAllKeys.map { key ->
+        // b/174175947
+        key to mapNotNull {
+            if (key !in it) {
+                Log.w(TAG, "Value $key missing from one iteration {$it}")
+            }
+            it[key]
+        }
+    }.toMap()
+    return listResults.map { (metricName, values) ->
+        MetricResult(metricName, values.toLongArray())
+    }.sortedBy { it.stats.name }
+}
+
 enum class StartupMode {
     /**
      * Startup from scratch - app's process is not alive, and must be started in addition to
@@ -193,13 +221,17 @@
 }
 
 fun startupMacrobenchmark(
-    benchmarkName: String,
+    uniqueName: String,
+    className: String,
+    testName: String,
     config: MacrobenchmarkConfig,
     startupMode: StartupMode,
     performStartup: MacrobenchmarkScope.() -> Unit
 ) {
     macrobenchmark(
-        benchmarkName = benchmarkName,
+        uniqueName = uniqueName,
+        className = className,
+        testName = testName,
         config = config,
         setupBlock = { firstIterAfterCompile ->
             if (startupMode == StartupMode.COLD) {
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkRule.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkRule.kt
index a5edac8..0bc18a3 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkRule.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkRule.kt
@@ -24,7 +24,7 @@
  * JUnit rule for benchmarking large app operations like startup.
  */
 class MacrobenchmarkRule : TestRule {
-    lateinit var benchmarkName: String
+    lateinit var currentDescription: Description
 
     fun measureRepeated(
         config: MacrobenchmarkConfig,
@@ -32,7 +32,9 @@
         measureBlock: MacrobenchmarkScope.() -> Unit
     ) {
         macrobenchmark(
-            benchmarkName = benchmarkName,
+            uniqueName = currentDescription.toUniqueName(),
+            className = currentDescription.className,
+            testName = currentDescription.methodName,
             config = config,
             launchWithClearTask = true,
             setupBlock = setupBlock,
@@ -46,7 +48,9 @@
         performStartup: MacrobenchmarkScope.() -> Unit
     ) {
         startupMacrobenchmark(
-            benchmarkName = benchmarkName,
+            uniqueName = currentDescription.toUniqueName(),
+            className = currentDescription.className,
+            testName = currentDescription.methodName,
             config = config,
             startupMode = startupMode,
             performStartup = performStartup
@@ -55,7 +59,7 @@
 
     override fun apply(base: Statement, description: Description) = object : Statement() {
         override fun evaluate() {
-            benchmarkName = description.toUniqueName()
+            currentDescription = description
             base.evaluate()
         }
     }
diff --git a/biometric/.idea/codeStyles/Project.xml b/biometric/.idea/codeStyles/Project.xml
new file mode 120000
index 0000000..b52b28c
--- /dev/null
+++ b/biometric/.idea/codeStyles/Project.xml
@@ -0,0 +1 @@
+../../../.idea/codeStyles/Project.xml
\ No newline at end of file
diff --git a/biometric/.idea/codeStyles/codeStyleConfig.xml b/biometric/.idea/codeStyles/codeStyleConfig.xml
new file mode 120000
index 0000000..19c4848
--- /dev/null
+++ b/biometric/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1 @@
+../../../.idea/codeStyles/codeStyleConfig.xml
\ No newline at end of file
diff --git a/biometric/.idea/copyright/AndroidCopyright.xml b/biometric/.idea/copyright/AndroidCopyright.xml
new file mode 120000
index 0000000..afbbd04
--- /dev/null
+++ b/biometric/.idea/copyright/AndroidCopyright.xml
@@ -0,0 +1 @@
+../../../.idea/copyright/AndroidCopyright.xml
\ No newline at end of file
diff --git a/biometric/.idea/copyright/profiles_settings.xml b/biometric/.idea/copyright/profiles_settings.xml
new file mode 120000
index 0000000..5996ccd
--- /dev/null
+++ b/biometric/.idea/copyright/profiles_settings.xml
@@ -0,0 +1 @@
+../../../.idea/copyright/profiles_settings.xml
\ No newline at end of file
diff --git a/biometric/.idea/inspectionProfiles/Project_Default.xml b/biometric/.idea/inspectionProfiles/Project_Default.xml
new file mode 120000
index 0000000..a7481f4
--- /dev/null
+++ b/biometric/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1 @@
+../../../.idea/inspectionProfiles/Project_Default.xml
\ No newline at end of file
diff --git a/biometric/.idea/scopes/Ignore_API_Files.xml b/biometric/.idea/scopes/Ignore_API_Files.xml
new file mode 120000
index 0000000..3361ee1
--- /dev/null
+++ b/biometric/.idea/scopes/Ignore_API_Files.xml
@@ -0,0 +1 @@
+../../../.idea/scopes/Ignore_API_Files.xml
\ No newline at end of file
diff --git a/biometric/.idea/scopes/buildSrc.xml b/biometric/.idea/scopes/buildSrc.xml
new file mode 120000
index 0000000..25b7d3b
--- /dev/null
+++ b/biometric/.idea/scopes/buildSrc.xml
@@ -0,0 +1 @@
+../../../.idea/scopes/buildSrc.xml
\ No newline at end of file
diff --git a/biometric/biometric/src/test/java/androidx/biometric/KeyguardUtilsTest.java b/biometric/biometric/src/test/java/androidx/biometric/KeyguardUtilsTest.java
index 952adaa..453f6a9 100644
--- a/biometric/biometric/src/test/java/androidx/biometric/KeyguardUtilsTest.java
+++ b/biometric/biometric/src/test/java/androidx/biometric/KeyguardUtilsTest.java
@@ -88,6 +88,7 @@
         assertThat(KeyguardUtils.isDeviceSecuredWithCredential(mContext)).isFalse();
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     @Config(minSdk = Build.VERSION_CODES.M)
     public void testIsDeviceSecuredWithCredential_CorrectlyReturnsTrue_OnApi23AndAbove() {
diff --git a/biometric/gradle b/biometric/gradle
new file mode 120000
index 0000000..1c936b3
--- /dev/null
+++ b/biometric/gradle
@@ -0,0 +1 @@
+../playground-common/gradle
\ No newline at end of file
diff --git a/biometric/gradle.properties b/biometric/gradle.properties
new file mode 120000
index 0000000..d952fb0
--- /dev/null
+++ b/biometric/gradle.properties
@@ -0,0 +1 @@
+../playground-common/androidx-shared.properties
\ No newline at end of file
diff --git a/biometric/gradlew b/biometric/gradlew
new file mode 120000
index 0000000..05b75179
--- /dev/null
+++ b/biometric/gradlew
@@ -0,0 +1 @@
+../playground-common/gradlew
\ No newline at end of file
diff --git a/biometric/gradlew.bat b/biometric/gradlew.bat
new file mode 120000
index 0000000..b20877e
--- /dev/null
+++ b/biometric/gradlew.bat
@@ -0,0 +1 @@
+../playground-common/gradlew.bat
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt b/biometric/settings.gradle
similarity index 65%
copy from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
copy to biometric/settings.gradle
index f9cb2fe..f090d5a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
+++ b/biometric/settings.gradle
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.focus
+// see ../playground-common/README.md for details on how this works
+rootProject.name = "paging-playground"
+apply from: "../playground-common/playground-include-settings.gradle"
+setupPlayground(this, "..")
+selectProjectsFromAndroidX({ name ->
+    if (name.startsWith(":biometric")) return true
+    return false
+})
 
-@RequiresOptIn("The Focus API is experimental and is likely to change in the future.")
-annotation class ExperimentalFocus
\ No newline at end of file
diff --git a/browser/OWNERS b/browser/OWNERS
index b6d0e07..2848be3 100644
--- a/browser/OWNERS
+++ b/browser/OWNERS
@@ -1,2 +1,3 @@
 lizeb@google.com
 peconn@google.com
+eirage@google.com
diff --git a/browser/README.md b/browser/README.md
index f4c0498..59bd958 100644
--- a/browser/README.md
+++ b/browser/README.md
@@ -8,7 +8,7 @@
 
 [Library Owners](OWNERS)
 
-[Source Code](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/browser/)
+[Source Code](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/browser/)
 
 [Release Nodes](https://developer.android.com/jetpack/androidx/releases/browser)
 
diff --git a/browser/browser/src/androidTest/java/androidx/browser/browseractions/BrowserActionsFallbackMenuUiTest.java b/browser/browser/src/androidTest/java/androidx/browser/browseractions/BrowserActionsFallbackMenuUiTest.java
index 390df87..689dc8f 100644
--- a/browser/browser/src/androidTest/java/androidx/browser/browseractions/BrowserActionsFallbackMenuUiTest.java
+++ b/browser/browser/src/androidTest/java/androidx/browser/browseractions/BrowserActionsFallbackMenuUiTest.java
@@ -31,7 +31,6 @@
 import androidx.browser.customtabs.TestActivity;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
 import androidx.testutils.PollingCheck;
 
 import org.junit.Before;
@@ -45,6 +44,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+@SuppressWarnings("deprecation")
 /** Unit tests for {@link BrowserActionsFallbackMenuUi}. */
 @RunWith(AndroidJUnit4.class)
 @LargeTest
@@ -53,8 +53,8 @@
     private static final String CUSTOM_ITEM_TITLE_1 = "Open url";
     private static final String CUSTOM_ITEM_TITLE_2 = "Share url";
     @Rule
-    public final ActivityTestRule<TestActivity> mActivityTestRule =
-            new ActivityTestRule<>(TestActivity.class);
+    public final androidx.test.rule.ActivityTestRule<TestActivity> mActivityTestRule =
+            new androidx.test.rule.ActivityTestRule<>(TestActivity.class);
 
     @Rule
     public final EnableComponentsTestRule mEnableComponents = new EnableComponentsTestRule(
diff --git a/browser/browser/src/androidTest/java/androidx/browser/browseractions/BrowserActionsIntentTest.java b/browser/browser/src/androidTest/java/androidx/browser/browseractions/BrowserActionsIntentTest.java
index b8ace06..7dae296 100644
--- a/browser/browser/src/androidTest/java/androidx/browser/browseractions/BrowserActionsIntentTest.java
+++ b/browser/browser/src/androidTest/java/androidx/browser/browseractions/BrowserActionsIntentTest.java
@@ -38,6 +38,7 @@
 import java.util.List;
 
 /** Unit tests for {@link BrowserActionsIntent}. */
+@SuppressWarnings("deprecation")
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public final class BrowserActionsIntentTest {
diff --git a/browser/browser/src/androidTest/java/androidx/browser/customtabs/PostMessageTest.java b/browser/browser/src/androidTest/java/androidx/browser/customtabs/PostMessageTest.java
index ee74d68..ab0d51b 100644
--- a/browser/browser/src/androidTest/java/androidx/browser/customtabs/PostMessageTest.java
+++ b/browser/browser/src/androidTest/java/androidx/browser/customtabs/PostMessageTest.java
@@ -29,7 +29,6 @@
 import androidx.annotation.NonNull;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
 import androidx.test.rule.ServiceTestRule;
 import androidx.testutils.PollingCheck;
 
@@ -40,7 +39,6 @@
 
 import java.util.concurrent.TimeoutException;
 
-
 /**
  * Tests for a complete loop between a browser side {@link CustomTabsService}
  * and a client side {@link PostMessageService}. Both services are bound to through
@@ -52,9 +50,10 @@
 public class PostMessageTest {
     @Rule
     public final ServiceTestRule mServiceRule = new ServiceTestRule();
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<TestActivity> mActivityTestRule =
-            new ActivityTestRule<>(TestActivity.class);
+    public final androidx.test.rule.ActivityTestRule<TestActivity> mActivityTestRule =
+            new androidx.test.rule.ActivityTestRule<>(TestActivity.class);
     @Rule
     public final EnableComponentsTestRule mEnableComponents = new EnableComponentsTestRule(
             TestActivity.class,
diff --git a/browser/browser/src/androidTest/java/androidx/browser/trusted/VerifiedProviderTestRule.java b/browser/browser/src/androidTest/java/androidx/browser/trusted/VerifiedProviderTestRule.java
index d0c433f..e895570 100644
--- a/browser/browser/src/androidTest/java/androidx/browser/trusted/VerifiedProviderTestRule.java
+++ b/browser/browser/src/androidTest/java/androidx/browser/trusted/VerifiedProviderTestRule.java
@@ -18,8 +18,6 @@
 
 import android.content.Context;
 
-import androidx.test.InstrumentationRegistry;
-
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 
@@ -46,8 +44,9 @@
         set(false);
     }
 
+    @SuppressWarnings("deprecation")
     private void set(boolean enabled) {
-        Context context = InstrumentationRegistry.getContext();
+        Context context = androidx.test.InstrumentationRegistry.getContext();
         TestTrustedWebActivityService.setVerifiedProvider(enabled
                 ? Token.create(context.getPackageName(), context.getPackageManager())
                 : null);
diff --git a/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabColorSchemeParamsTest.java b/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabColorSchemeParamsTest.java
index 3def804..ac2f395 100644
--- a/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabColorSchemeParamsTest.java
+++ b/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabColorSchemeParamsTest.java
@@ -37,6 +37,7 @@
  * In particular, for {@link CustomTabsIntent.Builder#setColorSchemeParams} and
  * {@link CustomTabsIntent#getColorSchemeParams}
  */
+@SuppressWarnings("deprecation")
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 public class CustomTabColorSchemeParamsTest {
diff --git a/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java b/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
index 215bbd7..08553dc 100644
--- a/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
+++ b/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
@@ -39,6 +39,7 @@
 /**
  * Tests for CustomTabsIntent.
  */
+@SuppressWarnings("deprecation")
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 // minSdk For Bundle#getBinder
diff --git a/browser/browser/src/test/java/androidx/browser/trusted/TrustedWebActivityIntentBuilderTest.java b/browser/browser/src/test/java/androidx/browser/trusted/TrustedWebActivityIntentBuilderTest.java
index 59a29ff..8b1847a 100644
--- a/browser/browser/src/test/java/androidx/browser/trusted/TrustedWebActivityIntentBuilderTest.java
+++ b/browser/browser/src/test/java/androidx/browser/trusted/TrustedWebActivityIntentBuilderTest.java
@@ -56,6 +56,7 @@
 @Config(minSdk = Build.VERSION_CODES.JELLY_BEAN_MR2)
 public class TrustedWebActivityIntentBuilderTest {
 
+    @SuppressWarnings("deprecation")
     @Test
     public void intentIsConstructedCorrectly() {
         Uri url = Uri.parse("https://test.com/page");
diff --git a/buildSrc-tests/src/test/kotlin/androidx/build/XmlTestConfigVerificationTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/XmlTestConfigVerificationTest.kt
deleted file mode 100644
index 43a6fe0..0000000
--- a/buildSrc-tests/src/test/kotlin/androidx/build/XmlTestConfigVerificationTest.kt
+++ /dev/null
@@ -1,73 +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.build
-
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.xml.sax.InputSource
-import org.xml.sax.helpers.DefaultHandler
-import java.io.StringReader
-import javax.xml.parsers.SAXParserFactory
-
-/**
- * Simple check that the test config templates are able to be parsed as valid xml.
- */
-@RunWith(JUnit4::class)
-class XmlTestConfigVerificationTest {
-
-    @Test
-    fun testValidTestConfigXml_TEMPLATE() {
-        val parser = SAXParserFactory.newInstance().newSAXParser()
-        parser.parse(
-            InputSource(StringReader(TEMPLATE.replace("TEST_BLOCK", FULL_TEST))),
-            DefaultHandler()
-        )
-    }
-
-    @Test
-    fun testValidTestConfigXml_SELF_INSTRUMENTING_TEMPLATE() {
-        val parser = SAXParserFactory.newInstance().newSAXParser()
-        parser.parse(
-            InputSource(
-                StringReader(
-                    SELF_INSTRUMENTING_TEMPLATE.replace(
-                        "TEST_BLOCK",
-                        DEPENDENT_TESTS
-                    )
-                )
-            ),
-            DefaultHandler()
-        )
-    }
-
-    @Test
-    fun testValidTestConfigXml_MEDIA_TEMPLATE() {
-        val parser = SAXParserFactory.newInstance().newSAXParser()
-        parser.parse(
-            InputSource(
-                StringReader(
-                    MEDIA_TEMPLATE.replace(
-                        "INSTRUMENTATION_ARGS",
-                        CLIENT_PREVIOUS + SERVICE_PREVIOUS
-                    )
-                )
-            ),
-            DefaultHandler()
-        )
-    }
-}
\ No newline at end of file
diff --git a/buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt
index c187d48..cef9eac 100644
--- a/buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt
+++ b/buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt
@@ -48,7 +48,6 @@
     val tmpFolder2 = TemporaryFolder()
 
     private lateinit var root: Project
-    private lateinit var root2: Project
     private lateinit var p1: Project
     private lateinit var p2: Project
     private lateinit var p3: Project
diff --git a/buildSrc-tests/src/test/kotlin/androidx/build/testConfiguration/XmlTestConfigVerificationTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/testConfiguration/XmlTestConfigVerificationTest.kt
new file mode 100644
index 0000000..d35f5e8
--- /dev/null
+++ b/buildSrc-tests/src/test/kotlin/androidx/build/testConfiguration/XmlTestConfigVerificationTest.kt
@@ -0,0 +1,208 @@
+/*
+ * 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.testConfiguration
+
+import org.hamcrest.CoreMatchers
+import org.hamcrest.MatcherAssert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.xml.sax.InputSource
+import org.xml.sax.helpers.DefaultHandler
+import java.io.StringReader
+import javax.xml.parsers.SAXParserFactory
+
+/**
+ * Simple check that the test config templates are able to be parsed as valid xml.
+ */
+@RunWith(JUnit4::class)
+class XmlTestConfigVerificationTest {
+
+    private lateinit var builder: ConfigBuilder
+    private lateinit var mediaBuilder: MediaConfigBuilder
+
+    @Before
+    fun init() {
+        builder = ConfigBuilder()
+        builder.isBenchmark(false)
+            .applicationId("com.androidx.placeholder.Placeholder")
+            .isPostsubmit(true)
+            .minSdk("15")
+            .tag("placeholder_tag")
+            .testApkName("placeholder.apk")
+            .testRunner("com.example.Runner")
+        mediaBuilder = MediaConfigBuilder()
+        mediaBuilder.clientApplicationId("com.androidx.client.Placeholder")
+            .clientApkName("clientPlaceholder.apk")
+            .serviceApplicationId("com.androidx.service.Placeholder")
+            .serviceApkName("servicePlaceholder.apk")
+            .minSdk("15")
+            .tag("placeholder_tag")
+            .testRunner("com.example.Runner")
+            .isClientPrevious(true)
+            .isServicePrevious(false)
+    }
+
+    @Test
+    fun testAgainstGoldenDefault() {
+        MatcherAssert.assertThat(
+            builder.build(),
+            CoreMatchers.`is`(goldenDefaultConfig)
+        )
+    }
+
+    @Test
+    fun testAgainstMediaGoldenDefault() {
+        MatcherAssert.assertThat(
+            mediaBuilder.build(),
+            CoreMatchers.`is`(goldenMediaDefaultConfig)
+        )
+    }
+
+    @Test
+    fun testValidTestConfigXml_default() {
+        validate(builder.build())
+    }
+
+    @Test
+    fun testValidTestConfigXml_benchmarkTrue() {
+        builder.isBenchmark(true)
+        validate(builder.build())
+    }
+
+    @Test
+    fun testValidTestConfigXml_withAppApk() {
+        builder.appApkName("Placeholder.apk")
+        validate(builder.build())
+    }
+
+    @Test
+    fun testValidTestConfigXml_presubmitWithAppApk() {
+        builder.isPostsubmit(false)
+            .appApkName("Placeholder.apk")
+        validate(builder.build())
+    }
+
+    @Test
+    fun testValidTestConfigXml_presubmit() {
+        builder.isPostsubmit(false)
+        validate(builder.build())
+    }
+
+    @Test
+    fun testValidTestConfigXml_presubmitBenchmark() {
+        builder.isPostsubmit(false)
+            .isBenchmark(true)
+        validate(builder.build())
+    }
+
+    @Test
+    fun testValidMediaConfigXml_default() {
+        validate(mediaBuilder.build())
+    }
+
+    @Test
+    fun testValidMediaConfigXml_presubmit() {
+        mediaBuilder.isPostsubmit(false)
+        validate(mediaBuilder.build())
+    }
+
+    private fun validate(xml: String) {
+        val parser = SAXParserFactory.newInstance().newSAXParser()
+        return parser.parse(
+            InputSource(
+                StringReader(
+                    xml
+                )
+            ),
+            DefaultHandler()
+        )
+    }
+}
+
+private val goldenDefaultConfig = """
+    <?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.-->
+    <configuration description="Runs tests for the module">
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MinApiLevelModuleController">
+    <option name="min-api-level" value="15" />
+    </object>
+    <option name="test-suite-tag" value="placeholder_tag" />
+    <option name="config-descriptor:metadata" key="applicationId" value="com.androidx.placeholder.Placeholder" />
+    <option name="wifi:disable" value="true" />
+    <include name="google/unbundled/common/setup" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+    <option name="cleanup-apks" value="true" />
+    <option name="test-file-name" value="placeholder.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+    <option name="runner" value="com.example.Runner"/>
+    <option name="package" value="com.androidx.placeholder.Placeholder" />
+    </test>
+    </configuration>
+""".trimIndent()
+
+private val goldenMediaDefaultConfig = """
+    <?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.-->
+    <configuration description="Runs tests for the module">
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MinApiLevelModuleController">
+    <option name="min-api-level" value="15" />
+    </object>
+    <option name="test-suite-tag" value="placeholder_tag" />
+    <option name="test-suite-tag" value="media_compat" />
+    <option name="config-descriptor:metadata" key="applicationId" value="com.androidx.client.Placeholder;com.androidx.service.Placeholder" />
+    <option name="wifi:disable" value="true" />
+    <include name="google/unbundled/common/setup" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+    <option name="cleanup-apks" value="true" />
+    <option name="test-file-name" value="clientPlaceholder.apk" />
+    <option name="test-file-name" value="servicePlaceholder.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+    <option name="runner" value="com.example.Runner"/>
+    <option name="package" value="com.androidx.client.Placeholder" />
+    <option name="instrumentation-arg" key="client_version" value="previous" />
+    <option name="instrumentation-arg" key="service_version" value="tot" />
+    </test>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+    <option name="runner" value="com.example.Runner"/>
+    <option name="package" value="com.androidx.service.Placeholder" />
+    <option name="instrumentation-arg" key="client_version" value="previous" />
+    <option name="instrumentation-arg" key="service_version" value="tot" />
+    </test>
+    </configuration>
+""".trimIndent()
\ No newline at end of file
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index bad8aa5..b2122db5 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -60,9 +60,7 @@
     cacheableImplementation {
         extendsFrom(project.configurations.cacheableApi)
     }
-    cacheableRuntime {
-        extendsFrom(project.configurations.cacheableImplementation)
-    }
+    cacheableRuntimeOnly
 }
 
 dependencies {
@@ -74,12 +72,12 @@
     cacheableApi build_libs.dokka_gradle
     // needed by inspection plugin
     cacheableImplementation "com.google.protobuf:protobuf-gradle-plugin:0.8.13"
-    // TODO(aurimas): remove when b/173417030 is fixed
+    // TODO(aurimas): remove when b/174658825 is fixed
     cacheableImplementation "org.anarres.jarjar:jarjar-gradle:1.0.1"
     cacheableImplementation "com.github.jengelman.gradle.plugins:shadow:5.2.0"
     // dependencies that aren't used by buildSrc directly but that we resolve here so that the
     // root project doesn't need to re-resolve them and their dependencies on every build
-    cacheableRuntime build_libs.hilt_plugin
+    cacheableRuntimeOnly build_libs.hilt_plugin
     // dependencies whose resolutions we don't need to cache
     compileOnly(findGradleKotlinDsl()) // Only one file in this configuration, no need to cache it
     implementation project("jetpad-integration") // Doesn't have a .pom, so not slow to load
@@ -168,7 +166,7 @@
 
 loadConfigurationQuicklyInto(configurations.cacheableApi, configurations.api)
 loadConfigurationQuicklyInto(configurations.cacheableImplementation, configurations.implementation)
-loadConfigurationQuicklyInto(configurations.cacheableRuntime, configurations.runtime)
+loadConfigurationQuicklyInto(configurations.cacheableRuntimeOnly, configurations.runtimeOnly)
 
 project.tasks.withType(Jar) { task ->
     task.reproducibleFileOrder = true
diff --git a/buildSrc/build_dependencies.gradle b/buildSrc/build_dependencies.gradle
index 5f9d6e6..28d058b 100644
--- a/buildSrc/build_dependencies.gradle
+++ b/buildSrc/build_dependencies.gradle
@@ -22,7 +22,7 @@
 build_versions.agp = build_versions.studio["agp"]
 build_versions.lint = build_versions.studio["lint"]
 
-build_versions.kotlin = "1.4.20"
+build_versions.kotlin = "1.4.21"
 build_versions.kotlin_coroutines = "1.4.1"
 
 build_versions.hilt = "2.29.1-alpha"
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index 5fa294c..a4dd2eb 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -33,6 +33,8 @@
 import androidx.build.jacoco.Jacoco
 import androidx.build.license.configureExternalDependencyLicenseCheck
 import androidx.build.studio.StudioTask
+import androidx.build.testConfiguration.addAppApkToTestConfigGeneration
+import androidx.build.testConfiguration.configureTestConfigGeneration
 import com.android.build.api.extension.LibraryAndroidComponentsExtension
 import com.android.build.gradle.AppExtension
 import com.android.build.gradle.AppPlugin
@@ -208,7 +210,7 @@
     ) {
         project.tasks.withType(KotlinCompile::class.java).configureEach { task ->
             task.kotlinOptions.jvmTarget = "1.8"
-            project.configureCompilationWarnings(task)
+            project.configureJavaCompilationWarnings(task)
             if (project.hasProperty(EXPERIMENTAL_KOTLIN_BACKEND_ENABLED)) {
                 task.kotlinOptions.freeCompilerArgs += listOf("-Xuse-ir=true")
             }
@@ -287,6 +289,7 @@
         project.configureSourceJarForAndroid(libraryExtension)
         project.configureVersionFileWriter(libraryExtension, androidXExtension)
         project.addCreateLibraryBuildInfoFileTask(androidXExtension)
+        project.configureJavaCompilationWarnings(androidXExtension)
 
         val verifyDependencyVersionsTask = project.createVerifyDependencyVersionsTask()
         val checkReleaseReadyTasks = mutableListOf<TaskProvider<out Task>>()
@@ -311,10 +314,6 @@
             verifyDependencyVersionsTask?.configure { task ->
                 task.dependsOn(libraryVariant.javaCompileProvider)
             }
-
-            libraryVariant.javaCompileProvider.configure { task ->
-                project.configureCompilationWarnings(task)
-            }
         }
 
         // Standard lint, docs, resource API, and Metalava configuration for AndroidX projects.
@@ -338,9 +337,7 @@
             targetCompatibility = VERSION_1_8
         }
 
-        project.tasks.withType(JavaCompile::class.java) { task ->
-            project.configureCompilationWarnings(task)
-        }
+        project.configureJavaCompilationWarnings(extension)
 
         project.hideJavadocTask()
 
@@ -532,15 +529,6 @@
             if (androidXExtension.publish.shouldRelease()) {
                 project.extra.set("publish", true)
             }
-            if (!project.rootProject.hasProperty(USE_MAX_DEP_VERSIONS)) {
-                defaultPublishVariant { libraryVariant ->
-                    libraryVariant.javaCompileProvider.configure { javaCompile ->
-                        if (androidXExtension.failOnDeprecationWarnings) {
-                            javaCompile.options.compilerArgs.add("-Xlint:deprecation")
-                        }
-                    }
-                }
-            }
         }
     }
 
@@ -750,14 +738,23 @@
     }
 }
 
-private fun Project.configureCompilationWarnings(task: JavaCompile) {
-    if (hasProperty(ALL_WARNINGS_AS_ERRORS)) {
-        task.options.compilerArgs.add("-Werror")
-        task.options.compilerArgs.add("-Xlint:unchecked")
+private fun Project.configureJavaCompilationWarnings(androidXExtension: AndroidXExtension) {
+    afterEvaluate {
+        project.tasks.withType(JavaCompile::class.java).configureEach { task ->
+            if (hasProperty(ALL_WARNINGS_AS_ERRORS)) {
+                task.options.compilerArgs.add("-Werror")
+                task.options.compilerArgs.add("-Xlint:unchecked")
+                if (androidXExtension.failOnDeprecationWarnings &&
+                    !hasProperty(AndroidXPlugin.USE_MAX_DEP_VERSIONS)
+                ) {
+                    task.options.compilerArgs.add("-Xlint:deprecation")
+                }
+            }
+        }
     }
 }
 
-private fun Project.configureCompilationWarnings(task: KotlinCompile) {
+private fun Project.configureJavaCompilationWarnings(task: KotlinCompile) {
     if (hasProperty(ALL_WARNINGS_AS_ERRORS)) {
         task.kotlinOptions.allWarningsAsErrors = true
     }
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
index 9847fce..e9c4d7a 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
@@ -31,6 +31,7 @@
 import org.gradle.api.plugins.ExtraPropertiesExtension
 import org.gradle.api.plugins.JavaPlugin
 import org.gradle.api.tasks.bundling.Zip
+import org.gradle.api.tasks.bundling.ZipEntryCompression
 import org.gradle.kotlin.dsl.KotlinClosure1
 import org.gradle.kotlin.dsl.extra
 import java.io.File
@@ -143,6 +144,8 @@
             it.destinationDirectory.set(project.getDistributionDirectory())
             it.archiveFileName.set("androidTest.zip")
             it.from(project.getTestConfigDirectory())
+            // We're mostly zipping a bunch of .apk files that are already compressed
+            it.entryCompression = ZipEntryCompression.STORED
         }
         buildOnServerTask.dependsOn(zipTestConfigsWithApks)
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
index f27e132..600632c 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
@@ -21,7 +21,6 @@
 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
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition
@@ -32,7 +31,6 @@
 import org.gradle.kotlin.dsl.invoke
 import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper
 import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
-import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 const val composeSourceOption =
@@ -194,7 +192,10 @@
                         "src/commonMain/kotlin", "src/jvmMain/kotlin",
                         "src/androidMain/kotlin"
                     )
-                    res.srcDirs("src/androidMain/res")
+                    res.srcDirs(
+                        "src/commonMain/resources",
+                        "src/androidMain/res"
+                    )
 
                     // Keep Kotlin files in java source sets so the source set is not empty when
                     // running unit tests which would prevent the tests from running in CI.
@@ -225,11 +226,9 @@
          * resolved.
          */
         private fun Project.configureForMultiplatform() {
-            if (multiplatformExtension == null) {
-                throw IllegalStateException(
-                    "Unable to configureForMultiplatform() when " +
-                        "multiplatformExtension is null (multiplatform plugin not enabled?)"
-                )
+            val multiplatformExtension = checkNotNull(multiplatformExtension) {
+                "Unable to configureForMultiplatform() when " +
+                    "multiplatformExtension is null (multiplatform plugin not enabled?)"
             }
 
             /*
@@ -247,13 +246,20 @@
             TODO: Consider changing unitTest to androidLocalTest and androidAndroidTest to
             androidDeviceTest when https://github.com/JetBrains/kotlin/pull/2829 rolls in.
             */
-            multiplatformExtension!!.sourceSets {
+            multiplatformExtension.sourceSets.all {
                 // Allow all experimental APIs, since MPP projects are themselves experimental
-                (this as DomainObjectCollection<KotlinSourceSet>).all {
-                    it.languageSettings.apply {
-                        useExperimentalAnnotation("kotlin.Experimental")
-                        useExperimentalAnnotation("kotlin.ExperimentalMultiplatform")
-                    }
+                it.languageSettings.apply {
+                    useExperimentalAnnotation("kotlin.Experimental")
+                    useExperimentalAnnotation("kotlin.ExperimentalMultiplatform")
+                }
+            }
+
+            afterEvaluate {
+                if (multiplatformExtension.targets.findByName("jvm") != null) {
+                    tasks.named("jvmTestClasses").also(::addToBuildOnServer)
+                }
+                if (multiplatformExtension.targets.findByName("desktop") != null) {
+                    tasks.named("desktopTestClasses").also(::addToBuildOnServer)
                 }
             }
         }
diff --git a/buildSrc/src/main/kotlin/androidx/build/GenerateMediaTestConfigurationTask.kt b/buildSrc/src/main/kotlin/androidx/build/GenerateMediaTestConfigurationTask.kt
deleted file mode 100644
index 3bfbd2f..0000000
--- a/buildSrc/src/main/kotlin/androidx/build/GenerateMediaTestConfigurationTask.kt
+++ /dev/null
@@ -1,219 +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.build
-
-import com.android.build.api.variant.BuiltArtifacts
-import com.android.build.api.variant.BuiltArtifactsLoader
-import org.gradle.api.DefaultTask
-import org.gradle.api.file.DirectoryProperty
-import org.gradle.api.file.RegularFileProperty
-import org.gradle.api.provider.Property
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Internal
-import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.TaskAction
-import java.io.File
-
-const val MEDIA_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
-        <!-- Copyright (C) 2019 The Android Open Source Project
-        Licensed under the Apache License, Version 2.0 (the "License")
-        you may not use this file except in compliance with the License.
-        You may obtain a copy of the License at
-        http://www.apache.org/licenses/LICENSE-2.0
-
-        Unless required by applicable law or agreed to in writing, software
-        distributed under the License is distributed on an "AS IS" BASIS,
-        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-        See the License for the specific language governing permissions and
-        limitations under the License.
-        -->
-        <configuration description="Runs tests for the module">
-        <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MinApiLevelModuleController">
-            <option name="min-api-level" value="MIN_SDK" />
-        </object>
-        <option name="test-suite-tag" value="androidx_unit_tests_suite" />
-        <option name="config-descriptor:metadata" key="applicationId"
-            value="CLIENT_APPLICATION_ID;SERVICE_APPLICATION_ID" />
-        <option name="wifi:disable" value="true" />
-        <include name="google/unbundled/common/setup" />
-        <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CLIENT_FILE_NAME" />
-        <option name="test-file-name" value="SERVICE_FILE_NAME" />
-        </target_preparer>
-        <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="runner" value="TEST_RUNNER"/>
-        <option name="package" value="CLIENT_APPLICATION_ID" />
-        INSTRUMENTATION_ARGS
-        </test>
-        <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="runner" value="TEST_RUNNER"/>
-        <option name="package" value="SERVICE_APPLICATION_ID" />
-        INSTRUMENTATION_ARGS
-        </test>
-        </configuration>"""
-
-const val CLIENT_PREVIOUS = """
-    <option name="instrumentation-arg" key="client_version" value="previous" />
-"""
-const val CLIENT_TOT = """
-    <option name="instrumentation-arg" key="client_version" value="tot" />
-"""
-const val SERVICE_PREVIOUS = """
-    <option name="instrumentation-arg" key="service_version" value="previous" />
-"""
-const val SERVICE_TOT = """
-    <option name="instrumentation-arg" key="service_version" value="tot" />
-"""
-
-/**
- * Writes three configuration files to test combinations of media client & service in
- * <a href=https://source.android.com/devices/tech/test_infra/tradefed/testing/through-suite/android-test-structure>AndroidTest.xml</a>
- * format that gets zipped alongside the APKs to be tested. The combinations are of previous and
- * tip-of-tree versions client and service. We want to test every possible pairing that includes
- * tip-of-tree.
- *
- * This config gets ingested by Tradefed.
- */
-abstract class GenerateMediaTestConfigurationTask : DefaultTask() {
-
-    @get:InputFiles
-    abstract val clientToTFolder: DirectoryProperty
-
-    @get:Internal
-    abstract val clientToTLoader: Property<BuiltArtifactsLoader>
-
-    @get:InputFiles
-    abstract val clientPreviousFolder: DirectoryProperty
-
-    @get:Internal
-    abstract val clientPreviousLoader: Property<BuiltArtifactsLoader>
-
-    @get:InputFiles
-    abstract val serviceToTFolder: DirectoryProperty
-
-    @get:Internal
-    abstract val serviceToTLoader: Property<BuiltArtifactsLoader>
-
-    @get:InputFiles
-    abstract val servicePreviousFolder: DirectoryProperty
-
-    @get:Internal
-    abstract val servicePreviousLoader: Property<BuiltArtifactsLoader>
-
-    @get:Input
-    abstract val clientToTPath: Property<String>
-
-    @get:Input
-    abstract val clientPreviousPath: Property<String>
-
-    @get:Input
-    abstract val serviceToTPath: Property<String>
-
-    @get:Input
-    abstract val servicePreviousPath: Property<String>
-
-    @get:Input
-    abstract val minSdk: Property<Int>
-
-    @get:Input
-    abstract val testRunner: Property<String>
-
-    @get:OutputFile
-    abstract val clientPreviousServiceToT: RegularFileProperty
-
-    @get:OutputFile
-    abstract val clientToTServicePrevious: RegularFileProperty
-
-    @get:OutputFile
-    abstract val clientToTServiceToT: RegularFileProperty
-
-    @TaskAction
-    fun generateAndroidTestZip() {
-        val clientToTApk = resolveApk(clientToTFolder, clientToTLoader)
-        val clientPreviousApk = resolveApk(clientPreviousFolder, clientPreviousLoader)
-        val serviceToTApk = resolveApk(serviceToTFolder, serviceToTLoader)
-        val servicePreviousApk = resolveApk(
-            servicePreviousFolder, servicePreviousLoader
-        )
-        writeConfigFileContent(
-            clientToTApk, serviceToTApk, clientToTPath.get(),
-            serviceToTPath.get(), clientToTServiceToT
-        )
-        writeConfigFileContent(
-            clientToTApk, servicePreviousApk, clientToTPath.get(),
-            servicePreviousPath.get(), clientToTServicePrevious
-        )
-        writeConfigFileContent(
-            clientPreviousApk, serviceToTApk, clientPreviousPath.get(),
-            serviceToTPath.get(), clientPreviousServiceToT
-        )
-    }
-
-    private fun resolveApk(
-        apkFolder: DirectoryProperty,
-        apkLoader: Property<BuiltArtifactsLoader>
-    ): BuiltArtifacts {
-        return apkLoader.get().load(apkFolder.get())
-            ?: throw RuntimeException("Cannot load APK for $name")
-    }
-
-    private fun resolveName(apk: BuiltArtifacts, path: String): String {
-        return apk.elements.single().outputFile.substringAfterLast("/")
-            .renameApkForTesting(path, false)
-    }
-
-    private fun writeConfigFileContent(
-        clientApk: BuiltArtifacts,
-        serviceApk: BuiltArtifacts,
-        clientPath: String,
-        servicePath: String,
-        outputFile: RegularFileProperty
-    ) {
-        val instrumentationArgs =
-            if (clientPath.contains("previous")) {
-                if (servicePath.contains("previous")) {
-                    CLIENT_PREVIOUS + SERVICE_PREVIOUS
-                } else {
-                    CLIENT_PREVIOUS + SERVICE_TOT
-                }
-            } else if (servicePath.contains("previous")) {
-                CLIENT_TOT + SERVICE_PREVIOUS
-            } else {
-                CLIENT_TOT + SERVICE_TOT
-            }
-        var configContent: String = MEDIA_TEMPLATE
-        configContent = configContent
-            .replace("CLIENT_FILE_NAME", resolveName(clientApk, clientPath))
-            .replace("SERVICE_FILE_NAME", resolveName(serviceApk, servicePath))
-            .replace("CLIENT_APPLICATION_ID", clientApk.applicationId)
-            .replace("SERVICE_APPLICATION_ID", serviceApk.applicationId)
-            .replace("MIN_SDK", minSdk.get().toString())
-            .replace("TEST_RUNNER", testRunner.get())
-            .replace("INSTRUMENTATION_ARGS", instrumentationArgs)
-        val resolvedOutputFile: File = outputFile.asFile.get()
-        if (!resolvedOutputFile.exists()) {
-            if (!resolvedOutputFile.createNewFile()) {
-                throw RuntimeException(
-                    "Failed to create test configuration file: $outputFile"
-                )
-            }
-        }
-        resolvedOutputFile.writeText(configContent)
-    }
-}
diff --git a/buildSrc/src/main/kotlin/androidx/build/GenerateTestConfigurationTask.kt b/buildSrc/src/main/kotlin/androidx/build/GenerateTestConfigurationTask.kt
deleted file mode 100644
index e958cd3..0000000
--- a/buildSrc/src/main/kotlin/androidx/build/GenerateTestConfigurationTask.kt
+++ /dev/null
@@ -1,210 +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.build
-
-import androidx.build.dependencyTracker.ProjectSubset
-import com.android.build.api.variant.BuiltArtifactsLoader
-import org.gradle.api.DefaultTask
-import org.gradle.api.file.DirectoryProperty
-import org.gradle.api.file.RegularFileProperty
-import org.gradle.api.provider.Property
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Internal
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.TaskAction
-import java.io.File
-
-const val TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
-        <!-- Copyright (C) 2019 The Android Open Source Project
-        Licensed under the Apache License, Version 2.0 (the "License")
-        you may not use this file except in compliance with the License.
-        You may obtain a copy of the License at
-        http://www.apache.org/licenses/LICENSE-2.0
-
-        Unless required by applicable law or agreed to in writing, software
-        distributed under the License is distributed on an "AS IS" BASIS,
-        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-        See the License for the specific language governing permissions and
-        limitations under the License.
-        -->
-        <configuration description="Runs tests for the module">
-        <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MinApiLevelModuleController">
-            <option name="min-api-level" value="MIN_SDK" />
-        </object>
-        <option name="test-suite-tag" value="TEST_SUITE_TAG" />
-        <option name="config-descriptor:metadata" key="applicationId" value="APPLICATION_ID" />
-        <option name="wifi:disable" value="true" />
-        <include name="google/unbundled/common/setup" />
-        <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="TEST_FILE_NAME" />
-        <option name="test-file-name" value="APP_FILE_NAME" />
-        </target_preparer>
-        <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="runner" value="TEST_RUNNER"/>
-        <option name="package" value="APPLICATION_ID" />
-        </test>
-        </configuration>"""
-
-const val SELF_INSTRUMENTING_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
-        <!-- Copyright (C) 2019 The Android Open Source Project
-        Licensed under the Apache License, Version 2.0 (the "License")
-        you may not use this file except in compliance with the License.
-        You may obtain a copy of the License at
-        http://www.apache.org/licenses/LICENSE-2.0
-
-        Unless required by applicable law or agreed to in writing, software
-        distributed under the License is distributed on an "AS IS" BASIS,
-        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-        See the License for the specific language governing permissions and
-        limitations under the License.
-        -->
-        <configuration description="Runs tests for the module">
-        <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MinApiLevelModuleController">
-            <option name="min-api-level" value="MIN_SDK" />
-        </object>
-        <option name="test-suite-tag" value="TEST_SUITE_TAG" />
-        <option name="config-descriptor:metadata" key="applicationId" value="APPLICATION_ID" />
-        <option name="wifi:disable" value="true" />
-        <include name="google/unbundled/common/setup" />
-        <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="TEST_FILE_NAME" />
-        </target_preparer>
-        TEST_BLOCK
-        </configuration>"""
-
-const val FULL_TEST = """
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="runner" value="TEST_RUNNER"/>
-        <option name="package" value="APPLICATION_ID" />
-    </test>
-"""
-
-const val DEPENDENT_TESTS = """
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="runner" value="TEST_RUNNER"/>
-        <option name="package" value="APPLICATION_ID" />
-        <option name="size" value="small" />
-        <option name="test-timeout" value="300" />
-    </test>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="runner" value="TEST_RUNNER"/>
-        <option name="package" value="APPLICATION_ID" />
-        <option name="size" value="medium" />
-        <option name="test-timeout" value="1500" />
-    </test>
-"""
-
-/**
- * Writes a configuration file in
- * <a href=https://source.android.com/devices/tech/test_infra/tradefed/testing/through-suite/android-test-structure>AndroidTest.xml</a>
- * format that gets zipped alongside the APKs to be tested.
- * This config gets ingested by Tradefed.
- */
-abstract class GenerateTestConfigurationTask : DefaultTask() {
-
-    @get:InputFiles
-    @get:Optional
-    abstract val appFolder: DirectoryProperty
-
-    @get:Internal
-    abstract val appLoader: Property<BuiltArtifactsLoader>
-
-    @get:InputFiles
-    abstract val testFolder: DirectoryProperty
-
-    @get:Internal
-    abstract val testLoader: Property<BuiltArtifactsLoader>
-
-    @get:Input
-    abstract val minSdk: Property<Int>
-
-    @get:Input
-    abstract val hasBenchmarkPlugin: Property<Boolean>
-
-    @get:Input
-    abstract val testRunner: Property<String>
-
-    @get:Input
-    abstract val projectPath: Property<String>
-
-    @get:Input
-    abstract val affectedModuleDetectorSubset: Property<ProjectSubset>
-
-    @get:OutputFile
-    abstract val outputXml: RegularFileProperty
-
-    @TaskAction
-    fun generateAndroidTestZip() {
-        writeConfigFileContent()
-    }
-
-    private fun writeConfigFileContent() {
-        /*
-        Testing an Android Application project involves 2 APKS: an application to be instrumented,
-        and a test APK. Testing an Android Library project involves only 1 APK, since the library
-        is bundled inside the test APK, meaning it is self instrumenting. We add extra data to
-        configurations testing Android Application projects, so that both APKs get installed.
-         */
-        var configContent: String = if (appLoader.isPresent) {
-            val appApk = appLoader.get().load(appFolder.get())
-                ?: throw RuntimeException("Cannot load application APK for $name")
-            val appName = appApk.elements.single().outputFile.substringAfterLast("/")
-                .renameApkForTesting(projectPath.get(), hasBenchmarkPlugin.get())
-            TEMPLATE.replace("APP_FILE_NAME", appName)
-        } else {
-            SELF_INSTRUMENTING_TEMPLATE
-        }
-        configContent = when (affectedModuleDetectorSubset.get()) {
-            ProjectSubset.CHANGED_PROJECTS, ProjectSubset.ALL_AFFECTED_PROJECTS -> {
-                configContent.replace("TEST_BLOCK", FULL_TEST)
-            }
-            ProjectSubset.DEPENDENT_PROJECTS -> {
-                configContent.replace("TEST_BLOCK", DEPENDENT_TESTS)
-            }
-            else -> {
-                throw IllegalStateException(
-                    "$name should not be running if the AffectedModuleDetector is returning " +
-                        "${affectedModuleDetectorSubset.get()} for this project."
-                )
-            }
-        }
-        val tag = if (hasBenchmarkPlugin.get()) "MetricTests" else "androidx_unit_tests"
-        val testApk = testLoader.get().load(testFolder.get())
-            ?: throw RuntimeException("Cannot load test APK for $name")
-        val testName = testApk.elements.single().outputFile
-            .substringAfterLast("/")
-            .renameApkForTesting(projectPath.get(), hasBenchmarkPlugin.get())
-        configContent = configContent.replace("TEST_FILE_NAME", testName)
-            .replace("APPLICATION_ID", testApk.applicationId)
-            .replace("MIN_SDK", minSdk.get().toString())
-            .replace("TEST_SUITE_TAG", tag)
-            .replace("TEST_RUNNER", testRunner.get())
-        val resolvedOutputFile: File = outputXml.asFile.get()
-        if (!resolvedOutputFile.exists()) {
-            if (!resolvedOutputFile.createNewFile()) {
-                throw RuntimeException(
-                    "Failed to create test configuration file: $outputXml"
-                )
-            }
-        }
-        resolvedOutputFile.writeText(configContent)
-    }
-}
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index 9719f00..5e24cda 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -76,6 +76,7 @@
     val RECOMMENDATION = LibraryGroup("androidx.recommendation", LibraryVersions.RECOMMENDATION)
     val RECYCLERVIEW = LibraryGroup("androidx.recyclerview", null)
     val REMOTECALLBACK = LibraryGroup("androidx.remotecallback", LibraryVersions.REMOTECALLBACK)
+    val RESOURCEINSPECTION = LibraryGroup("androidx.resourceinspection", LibraryVersions.RESOURCEINSPECTION)
     val ROOM = LibraryGroup("androidx.room", LibraryVersions.ROOM)
     val STARTUP = LibraryGroup("androidx.startup", LibraryVersions.STARTUP)
     val SAVEDSTATE = LibraryGroup("androidx.savedstate", LibraryVersions.SAVEDSTATE)
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 8886f28..8782501 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -22,9 +22,9 @@
 object LibraryVersions {
     val ACTIVITY = Version("1.2.0-rc01")
     val ADS_IDENTIFIER = Version("1.0.0-alpha04")
-    val ANNOTATION = Version("1.2.0-alpha02")
+    val ANNOTATION = Version("1.2.0-beta01")
     val ANNOTATION_EXPERIMENTAL = Version("1.1.0-alpha02")
-    val APPCOMPAT = Version("1.3.0-alpha03")
+    val APPCOMPAT = Version("1.3.0-beta01")
     val APPSEARCH = Version("1.0.0-alpha01")
     val ARCH_CORE = Version("2.2.0-alpha01")
     val ARCH_CORE_TESTING = ARCH_CORE
@@ -44,9 +44,9 @@
     val CAR_APP = Version("1.0.0-alpha01")
     val COLLECTION = Version("1.2.0-alpha01")
     val CONTENTPAGER = Version("1.1.0-alpha01")
-    val COMPOSE = Version(System.getenv("COMPOSE_CUSTOM_VERSION") ?: "1.0.0-alpha09")
+    val COMPOSE = Version(System.getenv("COMPOSE_CUSTOM_VERSION") ?: "1.0.0-alpha10")
     val COORDINATORLAYOUT = Version("1.2.0-alpha01")
-    val CORE = Version("1.5.0-alpha06")
+    val CORE = Version("1.5.0-beta01")
     val CORE_ANIMATION = Version("1.0.0-alpha03")
     val CORE_ANIMATION_TESTING = Version("1.0.0-alpha03")
     val CORE_APPDIGEST = Version("1.0.0-alpha01")
@@ -71,8 +71,9 @@
     val IPC = Version("1.0.0-alpha01")
     val JETIFIER = Version("1.0.0-beta10")
     val LEANBACK = Version("1.1.0-beta01")
-    val LEANBACK_PAGING = Version("1.1.0-alpha06")
+    val LEANBACK_PAGING = Version("1.1.0-alpha07")
     val LEANBACK_PREFERENCE = Version("1.1.0-beta01")
+    val LEANBACK_TAB = Version("1.1.0-beta01")
     val LEGACY = Version("1.1.0-alpha01")
     val LOCALBROADCASTMANAGER = Version("1.1.0-alpha02")
     val LIFECYCLE = Version("2.3.0-rc01")
@@ -82,9 +83,9 @@
     val MEDIA2 = Version("1.2.0-alpha01")
     val MEDIAROUTER = Version("1.3.0-alpha01")
     val NAVIGATION = Version("2.4.0-alpha01")
-    val NAVIGATION_COMPOSE = Version("1.0.0-alpha04")
-    val PAGING = Version("3.0.0-alpha10")
-    val PAGING_COMPOSE = Version("1.0.0-alpha04")
+    val NAVIGATION_COMPOSE = Version("1.0.0-alpha05")
+    val PAGING = Version("3.0.0-alpha12")
+    val PAGING_COMPOSE = Version("1.0.0-alpha05")
     val PALETTE = Version("1.1.0-alpha01")
     val PRINT = Version("1.1.0-beta01")
     val PERCENTLAYOUT = Version("1.1.0-alpha01")
@@ -93,6 +94,7 @@
     val RECYCLERVIEW = Version("1.2.0-beta01")
     val RECYCLERVIEW_SELECTION = Version("2.0.0-alpha01")
     val REMOTECALLBACK = Version("1.0.0-alpha02")
+    val RESOURCEINSPECTION = Version("1.0.0-alpha01")
     val ROOM = Version("2.3.0-alpha04")
     val SAVEDSTATE = Version("1.1.0-rc01")
     val SECURITY = Version("1.1.0-alpha03")
@@ -100,7 +102,7 @@
     val SECURITY_BIOMETRIC = Version("1.0.0-alpha01")
     val SECURITY_IDENTITY_CREDENTIAL = Version("1.0.0-alpha01")
     val SERIALIZATION = Version("1.0.0-alpha01")
-    val SHARETARGET = Version("1.1.0-rc01")
+    val SHARETARGET = Version("1.2.0-alpha01")
     val SLICE = Version("1.1.0-alpha02")
     val SLICE_BENCHMARK = Version("1.1.0-alpha02")
     val SLICE_BUILDERS_KTX = Version("1.0.0-alpha08")
@@ -113,7 +115,7 @@
     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 TRACING = Version("1.1.0-alpha01")
     val TRANSITION = Version("1.4.0-rc01")
     val TVPROVIDER = Version("1.1.0-alpha02")
     val VECTORDRAWABLE = Version("1.2.0-alpha03")
@@ -122,15 +124,16 @@
     val VERSIONED_PARCELABLE = Version("1.2.0-alpha01")
     val VIEWPAGER = Version("1.1.0-alpha01")
     val VIEWPAGER2 = Version("1.1.0-alpha02")
-    val WEAR = Version("1.2.0-alpha03")
-    val WEAR_COMPLICATIONS = Version("1.0.0-alpha03")
-    val WEAR_INPUT = Version("1.0.0-rc01")
+    val WEAR = Version("1.2.0-alpha04")
+    val WEAR_COMPLICATIONS = Version("1.0.0-alpha04")
+    val WEAR_INPUT = Version("1.1.0-alpha01")
+    val WEAR_REMOTE_INTERACTIONS = Version("1.0.0-alpha01")
     val WEAR_TILES = Version("1.0.0-alpha01")
     val WEAR_TILES_DATA = WEAR_TILES
-    val WEAR_WATCHFACE = Version("1.0.0-alpha03")
-    val WEAR_WATCHFACE_CLIENT = Version("1.0.0-alpha03")
-    val WEAR_WATCHFACE_DATA = Version("1.0.0-alpha03")
-    val WEAR_WATCHFACE_STYLE = Version("1.0.0-alpha03")
+    val WEAR_WATCHFACE = Version("1.0.0-alpha04")
+    val WEAR_WATCHFACE_CLIENT = Version("1.0.0-alpha04")
+    val WEAR_WATCHFACE_DATA = Version("1.0.0-alpha04")
+    val WEAR_WATCHFACE_STYLE = Version("1.0.0-alpha04")
     val WEBKIT = Version("1.4.0-beta01")
     val WINDOW = Version("1.0.0-alpha02")
     val WINDOW_EXTENSIONS = Version("1.0.0-alpha01")
diff --git a/buildSrc/src/main/kotlin/androidx/build/LintConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/LintConfiguration.kt
index e764c62..9c6b2c0 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LintConfiguration.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LintConfiguration.kt
@@ -110,10 +110,15 @@
             // concerned with drawables potentially being a little bit blurry
             disable("IconMissingDensityFolder")
 
+            // Disable a check that's only triggered by translation updates which are
+            // outside of library owners' control, b/174655193
+            disable("UnusedQuantity")
+
             // Disable until it works for our projects, b/171986505
             disable("JavaPluginLanguageLevel")
 
-            if (extension.type.compilationTarget != CompilationTarget.HOST) {
+            // Provide stricter enforcement for project types intended to run on a device.
+            if (extension.type.compilationTarget == CompilationTarget.DEVICE) {
                 fatal("Assert")
                 fatal("NewApi")
                 fatal("ObsoleteSdkInt")
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index d8ba393..1e2c5ac 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -42,6 +42,7 @@
 const val DAGGER = "com.google.dagger:dagger:2.29.1"
 const val DAGGER_COMPILER = "com.google.dagger:dagger-compiler:2.29.1"
 const val DEXMAKER_MOCKITO = "com.linkedin.dexmaker:dexmaker-mockito:2.25.0"
+const val DEXMAKER_MOCKITO_INLINE = "com.linkedin.dexmaker:dexmaker-mockito-inline:2.25.0"
 const val ESPRESSO_CONTRIB = "androidx.test.espresso:espresso-contrib:3.3.0"
 const val ESPRESSO_CORE = "androidx.test.espresso:espresso-core:3.3.0"
 const val ESPRESSO_INTENTS = "androidx.test.espresso:espresso-intents:3.3.0"
@@ -77,10 +78,10 @@
     "com.squareup:kotlinpoet-classinspector-elements:1.4.0"
 const val KOTLIN_COMPILE_TESTING = "com.github.tschuchortdev:kotlin-compile-testing:1.3.1"
 const val KOTLIN_COMPILE_TESTING_KSP = "com.github.tschuchortdev:kotlin-compile-testing-ksp:1.3.1"
-const val KSP_VERSION = "1.4.10-dev-experimental-20201120"
+const val KSP_VERSION = "1.4.20-dev-experimental-20201204"
 const val KOTLIN_KSP_API = "com.google.devtools.ksp:symbol-processing-api:$KSP_VERSION"
 const val KOTLIN_KSP = "com.google.devtools.ksp:symbol-processing:$KSP_VERSION"
-const val KOTLIN_GRADLE_PLUGIN = "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20"
+const val KOTLIN_GRADLE_PLUGIN = "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.21"
 
 const val KOTLIN_METADATA = "me.eugeniomarletti.kotlin.metadata:kotlin-metadata:1.4.0"
 const val KOTLIN_METADATA_JVM = "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0"
@@ -154,6 +155,12 @@
 val KOTLIN_TEST_JUNIT get() = "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion"
 val KOTLIN_TEST_JS get() = "org.jetbrains.kotlin:kotlin-test-js:$kotlinVersion"
 val KOTLIN_REFLECT get() = "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
+val KOTLIN_COMPILER_EMBEDDABLE
+    get() = "org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlinVersion"
+val KOTLIN_COMPILER_DAEMON_EMBEDDABLE
+    get() = "org.jetbrains.kotlin:kotlin-daemon-embeddable:$kotlinVersion"
+val KOTLIN_ANNOTATION_PROCESSING_EMBEDDABLE
+    get() = "org.jetbrains.kotlin:kotlin-annotation-processing-embeddable:$kotlinVersion"
 
 internal lateinit var kotlinCoroutinesVersion: String
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index beaed73..199694d 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -470,12 +470,19 @@
         private val COBUILT_TEST_PATHS = setOf(
             // Install media tests together per b/128577735
             setOf(
+                // Making a change in :media:version-compat-tests makes
+                // mediaGenerateTestConfiguration run (an unfortunate but low priority bug). To
+                // prevent failures from missing apks, we make sure to build the
+                // version-compat-tests projects in that case. Same with media2-session below.
+                ":media:version-compat-tests",
                 ":media:version-compat-tests:client",
                 ":media:version-compat-tests:service",
                 ":media:version-compat-tests:client-previous",
                 ":media:version-compat-tests:service-previous"
             ),
             setOf(
+                ":media2:media2-session",
+                ":media2:media2-session:version-compat-tests",
                 ":media2:media2-session:version-compat-tests:client",
                 ":media2:media2-session:version-compat-tests:service",
                 ":media2:media2-session:version-compat-tests:client-previous",
diff --git a/buildSrc/src/main/kotlin/androidx/build/doclava/Doclava.kt b/buildSrc/src/main/kotlin/androidx/build/doclava/Doclava.kt
index 06fb4d5..373c665 100644
--- a/buildSrc/src/main/kotlin/androidx/build/doclava/Doclava.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/doclava/Doclava.kt
@@ -53,15 +53,12 @@
                 project.zipTree(androidSrcJarFile(project))
                     .matching(PatternSet().include("**/*.java"))
             )
-            exclude("**/overview.html") // TODO https://issuetracker.google.com/issues/116699307
             apiFile = File(destination, "release/sdk_current.txt")
             generateDocs = false
-            coreJavadocOptions {
+            extraArgumentsBuilder.apply({
                 addStringOption("stubpackages", "android.*")
-            }
-            coreJavadocOptions {
                 addStringOption("-release", "8")
-            }
+            })
         }
     }
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/doclava/DoclavaTask.kt b/buildSrc/src/main/kotlin/androidx/build/doclava/DoclavaTask.kt
index 57ab23f..205bd58 100644
--- a/buildSrc/src/main/kotlin/androidx/build/doclava/DoclavaTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/doclava/DoclavaTask.kt
@@ -16,15 +16,22 @@
 
 package androidx.build.doclava
 
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.FileCollection
+import org.gradle.api.provider.ListProperty
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
 import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.javadoc.Javadoc
-import org.gradle.external.javadoc.CoreJavadocOptions
-import org.gradle.external.javadoc.StandardJavadocDocletOptions
+import org.gradle.api.tasks.TaskAction
+import org.gradle.process.ExecOperations
+import org.gradle.workers.WorkAction
+import org.gradle.workers.WorkParameters
+import org.gradle.workers.WorkerExecutor
 import java.io.File
+import javax.inject.Inject
 
 // external/doclava/src/com/google/doclava/Errors.java
 val DEFAULT_DOCLAVA_CONFIG = ChecksConfig(
@@ -40,14 +47,9 @@
     )
 )
 
-private fun <E> CoreJavadocOptions.addMultilineMultiValueOption(
-    name: String,
-    values: Collection<E>
-) {
-    addMultilineMultiValueOption(name).value = values.map { listOf(it.toString()) }
-}
-
-open class DoclavaTask : Javadoc() {
+abstract class DoclavaTask @Inject constructor(
+    private val workerExecutor: WorkerExecutor
+) : DefaultTask() {
 
     // All lowercase name to match MinimalJavadocOptions#docletpath
     private var docletpath: List<File> = emptyList()
@@ -86,20 +88,12 @@
 
     /**
      * If non-null, the location of where to place the generated removed api file.
-     * If this is non-null, then {@link #apiFile} must be non-null as well.
      */
     @Optional
     @OutputFile
     var removedApiFile: File? = null
 
     /**
-     * If non-null, the location of the generated keep list.
-     */
-    @Optional
-    @OutputFile
-    var keepListFile: File? = null
-
-    /**
      * If non-null, the location to put the generated stub sources.
      */
     @Optional
@@ -107,20 +101,13 @@
     var stubsDir: File? = null
 
     init {
-        setFailOnError(true)
-        options.doclet = "com.google.doclava.Doclava"
-        options.encoding("UTF-8")
-        options.quiet()
-        // doclava doesn't understand '-doctitle'
-        title = null
-        maxMemory = "1280m"
-        // If none of generateDocs, apiFile, keepListFile, or stubJarsDir are true, then there is
+        // If none of generateDocs, apiFile, or stubJarsDir are true, then there is
         // no work to do.
-        onlyIf({ generateDocs || apiFile != null || keepListFile != null || stubsDir != null })
+        onlyIf({ generateDocs || apiFile != null || stubsDir != null })
     }
 
     /**
-     * The doclet path which has the {@code com.gogole.doclava.Doclava} class.
+     * The doclet path which has the {@code com.google.doclava.Doclava} class.
      * This option will override any doclet path set in this instance's
      * {@link #options JavadocOptions}.
      * @see MinimalJavadocOptions#getDocletpath()
@@ -138,60 +125,164 @@
      */
     fun setDocletpath(docletpath: Collection<File>) {
         this.docletpath = docletpath.toList()
-        // Go ahead and keep the docletpath in our JavadocOptions object in sync.
-        options.docletpath = docletpath.toList()
+    }
+
+    @OutputDirectory
+    var destinationDir: File? = null
+
+    @InputFiles
+    var classpath: FileCollection? = null
+
+    @InputFiles
+    val sources = mutableListOf<FileCollection>()
+
+    fun source(files: FileCollection) {
+        sources.add(files)
     }
 
     /**
-     * "Configures" this DoclavaTask with parameters that might not be at their final values
-     * until this task is run.
+     * Builder containing extra arguments
      */
-    private fun configureDoclava() = (options as StandardJavadocDocletOptions).apply {
+    @Internal
+    val extraArgumentsBuilder = DoclavaArgumentBuilder()
 
-        docletpath = this@DoclavaTask.docletpath
+    @Input
+    val extraArguments = extraArgumentsBuilder.build()
+
+    private fun computeArguments(): List<String> {
+        val args = DoclavaArgumentBuilder()
+
+        // classpath
+        val classpathString = classpath!!.files.map({ f -> f.toString() }).joinToString(":")
+        args.addStringOption("cp", classpathString)
+        args.addStringOption("doclet", "com.google.doclava.Doclava")
+        args.addStringOption("docletpath", classpathString)
+
+        args.addOption("quiet")
+        args.addStringOption("encoding", "UTF-8")
 
         // configure doclava error/warning/hide levels
-        addMultilineMultiValueOption("hide", checksConfig.hidden)
-        addMultilineMultiValueOption("warning", checksConfig.warnings)
-        addMultilineMultiValueOption("error", checksConfig.errors)
+        args.addRepeatableOption("hide", checksConfig.hidden)
+        args.addRepeatableOption("warning", checksConfig.warnings)
+        args.addRepeatableOption("error", checksConfig.errors)
 
         if (hiddenPackages != null) {
-            addMultilineMultiValueOption("hidePackage", hiddenPackages!!)
+            args.addRepeatableOption("hidePackage", hiddenPackages!!)
         }
 
         if (!generateDocs) {
-            addBooleanOption("nodocs", true)
+            args.addOption("nodocs")
         }
 
         // If requested, generate the API files.
         if (apiFile != null) {
-            addFileOption("api", apiFile)
-            addFileOption("removedApi", removedApiFile)
+            args.addFileOption("api", apiFile!!)
+            if (removedApiFile != null) {
+                args.addFileOption("removedApi", removedApiFile!!)
+            }
         }
 
-        // If requested, generate the keep list.
-        addFileOption("proguard", keepListFile)
-
         // If requested, generate stubs.
         if (stubsDir != null) {
-            addFileOption("stubs", stubsDir)
+            args.addFileOption("stubs", stubsDir!!)
             val stubs = stubPackages
             if (stubs != null) {
-                addStringOption("stubpackages", stubs.joinToString(":"))
+                args.addStringOption("stubpackages", stubs.joinToString(":"))
             }
         }
         // Always treat this as an Android docs task.
-        addBooleanOption("android", true)
+        args.addOption("android")
 
-        // Doclava does not understand -notimestamp option that is default since Gradle 6.0
-        isNoTimestamp = false
+        // destination directory
+        args.addFileOption("d", destinationDir!!)
+
+        // source files
+        for (source in sources) {
+            for (file in source) {
+                val arg = file.toString()
+                // Doclava does not know how to parse Kotlin files
+                if (!arg.endsWith(".kt")) {
+                    args.add(arg)
+                }
+            }
+        }
+
+        return args.build() + extraArgumentsBuilder.build()
     }
 
-    fun coreJavadocOptions(configure: CoreJavadocOptions.() -> Unit) =
-        (options as CoreJavadocOptions).configure()
+    @TaskAction
+    fun generate() {
+        val args = computeArguments()
+        runDoclavaWithArgs(docletpath, args, workerExecutor)
+    }
+}
 
-    override fun generate() {
-        configureDoclava()
-        super.generate()
+class DoclavaArgumentBuilder {
+    fun add(value: String) {
+        args.add(value)
+    }
+
+    fun addOption(name: String) {
+        args.add("-" + name)
+    }
+
+    fun addStringOption(name: String, value: String) {
+        addOption(name)
+        args.add(value)
+    }
+
+    fun addBooleanOption(name: String, value: Boolean) {
+        addStringOption(name, value.toString())
+    }
+
+    fun addFileOption(name: String, value: File) {
+        addStringOption(name, value.toString())
+    }
+
+    fun addRepeatableOption(name: String, values: Collection<*>) {
+        for (value in values) {
+            addStringOption(name, value.toString())
+        }
+    }
+
+    fun addStringOption(name: String, values: Collection<String>) {
+        args.add("-" + name)
+        for (value in values) {
+            args.add(value)
+        }
+    }
+
+    fun build(): List<String> {
+        return args
+    }
+
+    private val args = mutableListOf<String>()
+}
+
+interface DoclavaParams : WorkParameters {
+    fun getClasspath(): ListProperty<File>
+    fun getArgs(): ListProperty<String>
+}
+
+fun runDoclavaWithArgs(classpath: List<File>, args: List<String>, workerExecutor: WorkerExecutor) {
+    val workQueue = workerExecutor.noIsolation()
+    workQueue.submit(DoclavaWorkAction::class.java) { parameters ->
+        parameters.getArgs().set(args)
+        parameters.getClasspath().set(classpath)
+    }
+}
+
+abstract class DoclavaWorkAction @Inject constructor (
+    private val execOperations: ExecOperations
+) : WorkAction<DoclavaParams> {
+    override fun execute() {
+        val args = getParameters().getArgs().get()
+        val classpath = getParameters().getClasspath().get()
+
+        execOperations.javaexec {
+            it.classpath(classpath)
+            it.main = "com.google.doclava.Doclava"
+            it.args = args
+        }
     }
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt
index 0607150..01510147 100644
--- a/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt
@@ -43,6 +43,7 @@
 import org.gradle.api.attributes.Usage
 import org.gradle.api.file.FileCollection
 import org.gradle.api.model.ObjectFactory
+import org.gradle.api.plugins.ExtraPropertiesExtension
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.tasks.Sync
 import org.gradle.api.tasks.TaskProvider
@@ -209,6 +210,24 @@
         val docsRuntimeClasspath = project.configurations.create("docs-runtime-classpath") {
             it.setResolveClasspathForUsage(Usage.JAVA_RUNTIME)
         }
+        docsCompileClasspath.resolutionStrategy {
+            val buildVersions = (project.rootProject.property("ext") as ExtraPropertiesExtension)
+                .let { it.get("build_versions") as Map<*, *> }
+            it.eachDependency { details ->
+                if (details.requested.group == "org.jetbrains.kotlin") {
+                    details.useVersion(buildVersions["kotlin"] as String)
+                }
+            }
+        }
+        docsRuntimeClasspath.resolutionStrategy {
+            val buildVersions = (project.rootProject.property("ext") as ExtraPropertiesExtension)
+                .let { it.get("build_versions") as Map<*, *> }
+            it.eachDependency { details ->
+                if (details.requested.group == "org.jetbrains.kotlin") {
+                    details.useVersion(buildVersions["kotlin"] as String)
+                }
+            }
+        }
         dependencyClasspath = docsCompileClasspath.incoming.artifactView {
             it.attributes.attribute(
                 Attribute.of("artifactType", String::class.java),
@@ -326,8 +345,6 @@
             it.apply {
                 dependsOn(unzipDocsTask)
                 dependsOn(generateSdkApiTask)
-                // Doclava does not know how to parse Kotlin files.
-                exclude("**/*.kt")
                 group = JavaBasePlugin.DOCUMENTATION_GROUP
                 description = "Generates Java documentation in the style of d.android.com. To " +
                     "generate offline docs use \'-PofflineDocs=true\' parameter.  Places the " +
@@ -337,7 +354,7 @@
                 destinationDir = destDir
                 classpath = androidJarFile(project) + dependencyClasspath
                 checksConfig = GENERATE_DOCS_CONFIG
-                coreJavadocOptions {
+                extraArgumentsBuilder.apply({
                     addStringOption(
                         "templatedir",
                         "${project.getCheckoutRoot()}/external/doclava/res/assets/templates-sdk"
@@ -347,27 +364,30 @@
                         "samplesdir",
                         "${project.rootDir}/samples"
                     )
-                    addMultilineMultiValueOption("federate").value = listOf(
+                    addStringOption(
+                        "federate",
                         listOf("Android", "https://developer.android.com")
                     )
-                    addMultilineMultiValueOption("federationapi").value = listOf(
-                        listOf("Android", generateSdkApiTask.get().apiFile?.absolutePath)
+                    addStringOption(
+                        "federationapi",
+                        listOf(
+                            "Android",
+                            generateSdkApiTask.get().apiFile?.absolutePath.toString()
+                        )
                     )
-                    addMultilineMultiValueOption("hdf").value = listOf(
-                        listOf("android.whichdoc", "online"),
-                        listOf("android.hasSamples", "true"),
-                        listOf("dac", "true")
-                    )
+                    addStringOption("hdf", listOf("android.whichdoc", "online"))
+                    addStringOption("hdf", listOf("android.hasSamples", "true"))
+                    addStringOption("hdf", listOf("dac", "true"))
 
                     // Specific to reference docs.
                     if (!offline) {
                         addStringOption("toroot", "/")
-                        addBooleanOption("devsite", true)
-                        addBooleanOption("yamlV2", true)
+                        addOption("devsite")
+                        addOption("yamlV2")
                         addStringOption("dac_libraryroot", dacOptions.libraryroot)
                         addStringOption("dac_dataname", dacOptions.dataname)
                     }
-                }
+                })
                 it.source(project.fileTree(unzippedDocsSources))
             }
         }
@@ -469,4 +489,4 @@
     "androidx.work.impl.utils",
     "androidx.work.impl.utils.futures",
     "androidx.work.impl.utils.taskexecutor"
-)
\ No newline at end of file
+)
diff --git a/buildSrc/src/main/kotlin/androidx/build/jacoco/Jacoco.kt b/buildSrc/src/main/kotlin/androidx/build/jacoco/Jacoco.kt
index 47148e3..2da678f 100644
--- a/buildSrc/src/main/kotlin/androidx/build/jacoco/Jacoco.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/jacoco/Jacoco.kt
@@ -67,7 +67,8 @@
                     it.from(v.testedVariant.javaCompileProvider.get().destinationDir)
                     it.exclude("**/R.class", "**/R\$*.class", "**/BuildConfig.class")
                     it.destinationDirectory.set(project.buildDir)
-                    it.archiveFileName.set("${project.name}-${v.baseName}-allclasses.jar")
+                    val sanitizedPath = project.path.removePrefix(":").replace(':', '_')
+                    it.archiveFileName.set("$sanitizedPath-${v.baseName}-allclasses.jar")
                 }
                 project.rootProject.tasks.named(
                     "packageAllClassFilesForCoverageReport",
diff --git a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/AndroidTestXmlBuilder.kt b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/AndroidTestXmlBuilder.kt
new file mode 100644
index 0000000..a55e365
--- /dev/null
+++ b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/AndroidTestXmlBuilder.kt
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.build.testConfiguration
+
+class ConfigBuilder {
+    var appApkName: String? = null
+    lateinit var applicationId: String
+    var isBenchmark: Boolean = false
+    var isPostsubmit: Boolean = true
+    lateinit var minSdk: String
+    var tag: String = "androidx_unit_tests"
+    lateinit var testApkName: String
+    lateinit var testRunner: String
+
+    fun appApkName(appApkName: String) = apply { this.appApkName = appApkName }
+    fun applicationId(applicationId: String) = apply { this.applicationId = applicationId }
+    fun isBenchmark(isBenchmark: Boolean) = apply { this.isBenchmark = isBenchmark }
+    fun isPostsubmit(isPostsubmit: Boolean) = apply { this.isPostsubmit = isPostsubmit }
+    fun minSdk(minSdk: String) = apply { this.minSdk = minSdk }
+    fun tag(tag: String) = apply { this.tag = tag }
+    fun testApkName(testApkName: String) = apply { this.testApkName = testApkName }
+    fun testRunner(testRunner: String) = apply { this.testRunner = testRunner }
+
+    fun build(): String {
+        val sb = StringBuilder()
+        sb.append(XML_HEADER_AND_LICENSE)
+            .append(CONFIGURATION_OPEN)
+            .append(MIN_API_LEVEL_CONTROLLER_OBJECT.replace("MIN_SDK", minSdk))
+            .append(TEST_SUITE_TAG_OPTION.replace("TEST_SUITE_TAG", tag))
+            .append(MODULE_METADATA_TAG_OPTION.replace("APPLICATION_ID", applicationId))
+            .append(WIFI_DISABLE_OPTION)
+        if (isBenchmark) {
+            if (isPostsubmit) {
+                sb.append(BENCHMARK_POSTSUBMIT_OPTIONS)
+            } else {
+                sb.append(BENCHMARK_PRESUBMIT_OPTION)
+            }
+        }
+        sb.append(SETUP_INCLUDE)
+            .append(TARGET_PREPARER_OPEN)
+            .append(APK_INSTALL_OPTION.replace("APK_NAME", testApkName))
+        if (!appApkName.isNullOrEmpty())
+            sb.append(APK_INSTALL_OPTION.replace("APK_NAME", appApkName!!))
+        sb.append(TARGET_PREPARER_CLOSE)
+            .append(TEST_BLOCK_OPEN)
+            .append(RUNNER_OPTION.replace("TEST_RUNNER", testRunner))
+            .append(PACKAGE_OPTION.replace("APPLICATION_ID", applicationId))
+        if (isPostsubmit)
+            sb.append(TEST_BLOCK_CLOSE)
+        else {
+            sb.append(SMALL_TEST_OPTIONS)
+                .append(TEST_BLOCK_CLOSE)
+                .append(TEST_BLOCK_OPEN)
+                .append(RUNNER_OPTION.replace("TEST_RUNNER", testRunner))
+                .append(PACKAGE_OPTION.replace("APPLICATION_ID", applicationId))
+                .append(MEDIUM_TEST_OPTIONS)
+                .append(TEST_BLOCK_CLOSE)
+        }
+        sb.append(CONFIGURATION_CLOSE)
+        return sb.toString()
+    }
+}
+
+class MediaConfigBuilder {
+    lateinit var clientApkName: String
+    lateinit var clientApplicationId: String
+    var isClientPrevious: Boolean = true
+    var isPostsubmit: Boolean = true
+    var isServicePrevious: Boolean = true
+    lateinit var minSdk: String
+    lateinit var serviceApkName: String
+    lateinit var serviceApplicationId: String
+    var tag: String = "androidx_unit_tests"
+    lateinit var testRunner: String
+
+    fun clientApkName(clientApkName: String) = apply { this.clientApkName = clientApkName }
+    fun clientApplicationId(clientApplicationId: String) =
+        apply { this.clientApplicationId = clientApplicationId }
+    fun isPostsubmit(isPostsubmit: Boolean) = apply { this.isPostsubmit = isPostsubmit }
+    fun isClientPrevious(isClientPrevious: Boolean) = apply {
+        this.isClientPrevious = isClientPrevious
+    }
+    fun isServicePrevious(isServicePrevious: Boolean) = apply {
+        this.isServicePrevious = isServicePrevious
+    }
+    fun minSdk(minSdk: String) = apply { this.minSdk = minSdk }
+    fun serviceApkName(serviceApkName: String) = apply { this.serviceApkName = serviceApkName }
+    fun serviceApplicationId(serviceApplicationId: String) =
+        apply { this.serviceApplicationId = serviceApplicationId }
+    fun tag(tag: String) = apply { this.tag = tag }
+    fun testRunner(testRunner: String) = apply { this.testRunner = testRunner }
+
+    private fun mediaInstrumentationArgs(): String {
+        return if (isClientPrevious) {
+            if (isServicePrevious) {
+                CLIENT_PREVIOUS + SERVICE_PREVIOUS
+            } else {
+                CLIENT_PREVIOUS + SERVICE_TOT
+            }
+        } else {
+            if (isServicePrevious) {
+                CLIENT_TOT + SERVICE_PREVIOUS
+            } else {
+                CLIENT_TOT + SERVICE_TOT
+            }
+        }
+    }
+
+    fun build(): String {
+        val sb = StringBuilder()
+        sb.append(XML_HEADER_AND_LICENSE)
+            .append(CONFIGURATION_OPEN)
+            .append(MIN_API_LEVEL_CONTROLLER_OBJECT.replace("MIN_SDK", minSdk))
+            .append(TEST_SUITE_TAG_OPTION.replace("TEST_SUITE_TAG", tag))
+            .append(TEST_SUITE_TAG_OPTION.replace("TEST_SUITE_TAG", "media_compat"))
+            .append(
+                MODULE_METADATA_TAG_OPTION.replace(
+                    "APPLICATION_ID", "$clientApplicationId;$serviceApplicationId"
+                )
+            )
+            .append(WIFI_DISABLE_OPTION)
+            .append(SETUP_INCLUDE)
+            .append(TARGET_PREPARER_OPEN)
+            .append(APK_INSTALL_OPTION.replace("APK_NAME", clientApkName))
+            .append(APK_INSTALL_OPTION.replace("APK_NAME", serviceApkName))
+        sb.append(TARGET_PREPARER_CLOSE)
+            .append(TEST_BLOCK_OPEN)
+            .append(RUNNER_OPTION.replace("TEST_RUNNER", testRunner))
+            .append(PACKAGE_OPTION.replace("APPLICATION_ID", clientApplicationId))
+            .append(mediaInstrumentationArgs())
+        if (isPostsubmit)
+            sb.append(TEST_BLOCK_CLOSE)
+                .append(TEST_BLOCK_OPEN)
+                .append(RUNNER_OPTION.replace("TEST_RUNNER", testRunner))
+                .append(PACKAGE_OPTION.replace("APPLICATION_ID", serviceApplicationId))
+                .append(mediaInstrumentationArgs())
+                .append(TEST_BLOCK_CLOSE)
+        else {
+            // add the small and medium test runners for both client and service apps
+            sb.append(SMALL_TEST_OPTIONS)
+                .append(TEST_BLOCK_CLOSE)
+                .append(TEST_BLOCK_OPEN)
+                .append(RUNNER_OPTION.replace("TEST_RUNNER", testRunner))
+                .append(PACKAGE_OPTION.replace("APPLICATION_ID", clientApplicationId))
+                .append(mediaInstrumentationArgs())
+                .append(MEDIUM_TEST_OPTIONS)
+                .append(TEST_BLOCK_CLOSE)
+                .append(TEST_BLOCK_OPEN)
+                .append(RUNNER_OPTION.replace("TEST_RUNNER", testRunner))
+                .append(PACKAGE_OPTION.replace("APPLICATION_ID", serviceApplicationId))
+                .append(mediaInstrumentationArgs())
+                .append(SMALL_TEST_OPTIONS)
+                .append(TEST_BLOCK_CLOSE)
+                .append(TEST_BLOCK_OPEN)
+                .append(RUNNER_OPTION.replace("TEST_RUNNER", testRunner))
+                .append(PACKAGE_OPTION.replace("APPLICATION_ID", serviceApplicationId))
+                .append(mediaInstrumentationArgs())
+                .append(MEDIUM_TEST_OPTIONS)
+                .append(TEST_BLOCK_CLOSE)
+        }
+        sb.append(CONFIGURATION_CLOSE)
+        return sb.toString()
+    }
+}
+
+/**
+ * These constants are the building blocks of the xml configs, but
+ * they aren't very readable as separate chunks. Look to
+ * the golden examples at the bottom of
+ * {@link androidx.build.testConfiguration.XmlTestConfigVerificationTest}
+ * for examples of what the full xml will look like.
+ */
+
+private val XML_HEADER_AND_LICENSE = """
+    <?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.-->
+
+""".trimIndent()
+
+private val CONFIGURATION_OPEN = """
+    <configuration description="Runs tests for the module">
+
+""".trimIndent()
+
+private val CONFIGURATION_CLOSE = """
+    </configuration>
+""".trimIndent()
+
+private val MIN_API_LEVEL_CONTROLLER_OBJECT = """
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MinApiLevelModuleController">
+    <option name="min-api-level" value="MIN_SDK" />
+    </object>
+
+""".trimIndent()
+
+private val TEST_SUITE_TAG_OPTION = """
+    <option name="test-suite-tag" value="TEST_SUITE_TAG" />
+
+""".trimIndent()
+
+private val MODULE_METADATA_TAG_OPTION = """
+    <option name="config-descriptor:metadata" key="applicationId" value="APPLICATION_ID" />
+
+""".trimIndent()
+
+private val WIFI_DISABLE_OPTION = """
+    <option name="wifi:disable" value="true" />
+
+""".trimIndent()
+
+private val SETUP_INCLUDE = """
+    <include name="google/unbundled/common/setup" />
+
+""".trimIndent()
+
+private val TARGET_PREPARER_OPEN = """
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+    <option name="cleanup-apks" value="true" />
+
+""".trimIndent()
+
+private val TARGET_PREPARER_CLOSE = """
+    </target_preparer>
+
+""".trimIndent()
+
+private val APK_INSTALL_OPTION = """
+    <option name="test-file-name" value="APK_NAME" />
+
+""".trimIndent()
+
+private val TEST_BLOCK_OPEN = """
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+
+""".trimIndent()
+
+private val TEST_BLOCK_CLOSE = """
+    </test>
+
+""".trimIndent()
+
+private val RUNNER_OPTION = """
+    <option name="runner" value="TEST_RUNNER"/>
+
+""".trimIndent()
+
+private val PACKAGE_OPTION = """
+    <option name="package" value="APPLICATION_ID" />
+
+""".trimIndent()
+
+private val BENCHMARK_PRESUBMIT_OPTION = """
+    <option name="instrumentation-arg" key="androidx.benchmark.dryRunMode.enable" value="true" />
+
+""".trimIndent()
+
+private val BENCHMARK_POSTSUBMIT_OPTIONS = """
+    <option name="instrumentation-arg" key="androidx.benchmark.output.enable" value="true" />
+    <option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.InstrumentationResultsRunListener" />
+
+""".trimIndent()
+
+private val SMALL_TEST_OPTIONS = """
+    <option name="size" value="small" />
+    <option name="test-timeout" value="300" />
+
+""".trimIndent()
+
+private val MEDIUM_TEST_OPTIONS = """
+    <option name="size" value="medium" />
+    <option name="test-timeout" value="1500" />
+
+""".trimIndent()
+
+private val CLIENT_PREVIOUS = """
+    <option name="instrumentation-arg" key="client_version" value="previous" />
+
+""".trimIndent()
+
+private val CLIENT_TOT = """
+    <option name="instrumentation-arg" key="client_version" value="tot" />
+
+""".trimIndent()
+
+private val SERVICE_PREVIOUS = """
+    <option name="instrumentation-arg" key="service_version" value="previous" />
+
+""".trimIndent()
+
+private val SERVICE_TOT = """
+    <option name="instrumentation-arg" key="service_version" value="tot" />
+
+""".trimIndent()
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateMediaTestConfigurationTask.kt b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateMediaTestConfigurationTask.kt
new file mode 100644
index 0000000..8ef93bcb
--- /dev/null
+++ b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateMediaTestConfigurationTask.kt
@@ -0,0 +1,177 @@
+/*
+ * 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.testConfiguration
+
+import androidx.build.dependencyTracker.ProjectSubset
+import androidx.build.renameApkForTesting
+import com.android.build.api.variant.BuiltArtifacts
+import com.android.build.api.variant.BuiltArtifactsLoader
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import java.io.File
+
+/**
+ * Writes three configuration files to test combinations of media client & service in
+ * <a href=https://source.android.com/devices/tech/test_infra/tradefed/testing/through-suite/android-test-structure>AndroidTest.xml</a>
+ * format that gets zipped alongside the APKs to be tested. The combinations are of previous and
+ * tip-of-tree versions client and service. We want to test every possible pairing that includes
+ * tip-of-tree.
+ *
+ * This config gets ingested by Tradefed.
+ */
+abstract class GenerateMediaTestConfigurationTask : DefaultTask() {
+
+    @get:InputFiles
+    abstract val clientToTFolder: DirectoryProperty
+
+    @get:Internal
+    abstract val clientToTLoader: Property<BuiltArtifactsLoader>
+
+    @get:InputFiles
+    abstract val clientPreviousFolder: DirectoryProperty
+
+    @get:Internal
+    abstract val clientPreviousLoader: Property<BuiltArtifactsLoader>
+
+    @get:InputFiles
+    abstract val serviceToTFolder: DirectoryProperty
+
+    @get:Internal
+    abstract val serviceToTLoader: Property<BuiltArtifactsLoader>
+
+    @get:InputFiles
+    abstract val servicePreviousFolder: DirectoryProperty
+
+    @get:Internal
+    abstract val servicePreviousLoader: Property<BuiltArtifactsLoader>
+
+    @get:Input
+    abstract val affectedModuleDetectorSubset: Property<ProjectSubset>
+
+    @get:Input
+    abstract val clientToTPath: Property<String>
+
+    @get:Input
+    abstract val clientPreviousPath: Property<String>
+
+    @get:Input
+    abstract val serviceToTPath: Property<String>
+
+    @get:Input
+    abstract val servicePreviousPath: Property<String>
+
+    @get:Input
+    abstract val minSdk: Property<Int>
+
+    @get:Input
+    abstract val testRunner: Property<String>
+
+    @get:OutputFile
+    abstract val clientPreviousServiceToT: RegularFileProperty
+
+    @get:OutputFile
+    abstract val clientToTServicePrevious: RegularFileProperty
+
+    @get:OutputFile
+    abstract val clientToTServiceToT: RegularFileProperty
+
+    @TaskAction
+    fun generateAndroidTestZip() {
+        val clientToTApk = resolveApk(clientToTFolder, clientToTLoader)
+        val clientPreviousApk = resolveApk(clientPreviousFolder, clientPreviousLoader)
+        val serviceToTApk = resolveApk(serviceToTFolder, serviceToTLoader)
+        val servicePreviousApk = resolveApk(
+            servicePreviousFolder, servicePreviousLoader
+        )
+        writeConfigFileContent(
+            clientToTApk, serviceToTApk, clientToTPath.get(),
+            serviceToTPath.get(), clientToTServiceToT, false, false
+        )
+        writeConfigFileContent(
+            clientToTApk, servicePreviousApk, clientToTPath.get(),
+            servicePreviousPath.get(), clientToTServicePrevious, false, true
+        )
+        writeConfigFileContent(
+            clientPreviousApk, serviceToTApk, clientPreviousPath.get(),
+            serviceToTPath.get(), clientPreviousServiceToT, true, false
+        )
+    }
+
+    private fun resolveApk(
+        apkFolder: DirectoryProperty,
+        apkLoader: Property<BuiltArtifactsLoader>
+    ): BuiltArtifacts {
+        return apkLoader.get().load(apkFolder.get())
+            ?: throw RuntimeException("Cannot load required APK for task: $name")
+    }
+
+    private fun resolveName(apk: BuiltArtifacts, path: String): String {
+        return apk.elements.single().outputFile.substringAfterLast("/")
+            .renameApkForTesting(path, false)
+    }
+
+    private fun writeConfigFileContent(
+        clientApk: BuiltArtifacts,
+        serviceApk: BuiltArtifacts,
+        clientPath: String,
+        servicePath: String,
+        outputFile: RegularFileProperty,
+        isClientPrevious: Boolean,
+        isServicePrevious: Boolean
+    ) {
+        val configBuilder = MediaConfigBuilder()
+        configBuilder.clientApkName(resolveName(clientApk, clientPath))
+            .clientApplicationId(clientApk.applicationId)
+            .serviceApkName(resolveName(serviceApk, servicePath))
+            .serviceApplicationId(serviceApk.applicationId)
+            .minSdk(minSdk.get().toString())
+            .testRunner(testRunner.get())
+            .isClientPrevious(isClientPrevious)
+            .isServicePrevious(isServicePrevious)
+        when (affectedModuleDetectorSubset.get()) {
+            ProjectSubset.CHANGED_PROJECTS, ProjectSubset.ALL_AFFECTED_PROJECTS -> {
+                configBuilder.isPostsubmit(true)
+            }
+            ProjectSubset.DEPENDENT_PROJECTS -> {
+                configBuilder.isPostsubmit(false)
+            }
+            else -> {
+                throw IllegalStateException(
+                    "$name should not be running if the AffectedModuleDetector is returning " +
+                        "${affectedModuleDetectorSubset.get()} for this project."
+                )
+            }
+        }
+
+        val resolvedOutputFile: File = outputFile.asFile.get()
+        if (!resolvedOutputFile.exists()) {
+            if (!resolvedOutputFile.createNewFile()) {
+                throw RuntimeException(
+                    "Failed to create test configuration file: $outputFile"
+                )
+            }
+        }
+        resolvedOutputFile.writeText(configBuilder.build())
+    }
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
new file mode 100644
index 0000000..a95e43c
--- /dev/null
+++ b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.testConfiguration
+
+import androidx.build.dependencyTracker.ProjectSubset
+import androidx.build.renameApkForTesting
+import com.android.build.api.variant.BuiltArtifactsLoader
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.Optional
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import java.io.File
+
+/**
+ * Writes a configuration file in
+ * <a href=https://source.android.com/devices/tech/test_infra/tradefed/testing/through-suite/android-test-structure>AndroidTest.xml</a>
+ * format that gets zipped alongside the APKs to be tested.
+ * This config gets ingested by Tradefed.
+ */
+abstract class GenerateTestConfigurationTask : DefaultTask() {
+
+    @get:InputFiles
+    @get:Optional
+    abstract val appFolder: DirectoryProperty
+
+    @get:Internal
+    abstract val appLoader: Property<BuiltArtifactsLoader>
+
+    @get:InputFiles
+    abstract val testFolder: DirectoryProperty
+
+    @get:Internal
+    abstract val testLoader: Property<BuiltArtifactsLoader>
+
+    @get:Input
+    abstract val minSdk: Property<Int>
+
+    @get:Input
+    abstract val hasBenchmarkPlugin: Property<Boolean>
+
+    @get:Input
+    abstract val testRunner: Property<String>
+
+    @get:Input
+    abstract val projectPath: Property<String>
+
+    @get:Input
+    abstract val affectedModuleDetectorSubset: Property<ProjectSubset>
+
+    @get:OutputFile
+    abstract val outputXml: RegularFileProperty
+
+    @TaskAction
+    fun generateAndroidTestZip() {
+        writeConfigFileContent()
+    }
+
+    private fun writeConfigFileContent() {
+        /*
+        Testing an Android Application project involves 2 APKS: an application to be instrumented,
+        and a test APK. Testing an Android Library project involves only 1 APK, since the library
+        is bundled inside the test APK, meaning it is self instrumenting. We add extra data to
+        configurations testing Android Application projects, so that both APKs get installed.
+         */
+        val configBuilder = ConfigBuilder()
+        if (appLoader.isPresent) {
+            val appApk = appLoader.get().load(appFolder.get())
+                ?: throw RuntimeException("Cannot load required APK for task: $name")
+            val appName = appApk.elements.single().outputFile.substringAfterLast("/")
+                .renameApkForTesting(projectPath.get(), hasBenchmarkPlugin.get())
+            configBuilder.appApkName(appName)
+        }
+        val isPostsubmit: Boolean = when (affectedModuleDetectorSubset.get()) {
+            ProjectSubset.CHANGED_PROJECTS, ProjectSubset.ALL_AFFECTED_PROJECTS -> {
+                true
+            }
+            ProjectSubset.DEPENDENT_PROJECTS -> {
+                false
+            }
+            else -> {
+                throw IllegalStateException(
+                    "$name should not be running if the AffectedModuleDetector is returning " +
+                        "${affectedModuleDetectorSubset.get()} for this project."
+                )
+            }
+        }
+        configBuilder.isPostsubmit(isPostsubmit)
+        if (hasBenchmarkPlugin.get()) {
+            configBuilder.isBenchmark(true)
+            if (isPostsubmit) {
+                configBuilder.tag("microbenchmarks")
+            }
+        } else if (projectPath.get().endsWith("macrobenchmark")) {
+            configBuilder.tag("macrobenchmarks")
+        }
+        val testApk = testLoader.get().load(testFolder.get())
+            ?: throw RuntimeException("Cannot load required APK for task: $name")
+        val testName = testApk.elements.single().outputFile
+            .substringAfterLast("/")
+            .renameApkForTesting(projectPath.get(), hasBenchmarkPlugin.get())
+        configBuilder.testApkName(testName)
+            .applicationId(testApk.applicationId)
+            .minSdk(minSdk.get().toString())
+            .testRunner(testRunner.get())
+
+        val resolvedOutputFile: File = outputXml.asFile.get()
+        if (!resolvedOutputFile.exists()) {
+            if (!resolvedOutputFile.createNewFile()) {
+                throw RuntimeException(
+                    "Failed to create test configuration file: $outputXml"
+                )
+            }
+        }
+        resolvedOutputFile.writeText(configBuilder.build())
+    }
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/TestSuiteConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
similarity index 92%
rename from buildSrc/src/main/kotlin/androidx/build/TestSuiteConfiguration.kt
rename to buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
index c676f20..764abcc 100644
--- a/buildSrc/src/main/kotlin/androidx/build/TestSuiteConfiguration.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
@@ -16,10 +16,15 @@
 
 @file:Suppress("UnstableApiUsage") // Incubating AGP APIs
 
-package androidx.build
+package androidx.build.testConfiguration
 
+import androidx.build.AndroidXPlugin
+import androidx.build.asFilenamePrefix
 import androidx.build.dependencyTracker.AffectedModuleDetector
+import androidx.build.getTestConfigDirectory
 import androidx.build.gradle.getByType
+import androidx.build.hasAndroidTestSourceCode
+import androidx.build.hasBenchmarkPlugin
 import com.android.build.api.artifact.ArtifactType
 import com.android.build.api.artifact.Artifacts
 import com.android.build.api.extension.AndroidComponentsExtension
@@ -85,8 +90,7 @@
         if (!project.parent!!.tasks.withType(GenerateMediaTestConfigurationTask::class.java)
             .names.contains(
                     "support-$mediaPrefix-test${
-                    AndroidXPlugin
-                        .GENERATE_TEST_CONFIGURATION_TASK
+                    AndroidXPlugin.GENERATE_TEST_CONFIGURATION_TASK
                     }"
                 )
         ) {
@@ -95,6 +99,11 @@
                 GenerateMediaTestConfigurationTask::class.java
             ) { task ->
                 AffectedModuleDetector.configureTaskGuard(task)
+                task.affectedModuleDetectorSubset.set(
+                    project.provider {
+                        AffectedModuleDetector.getProjectSubset(project)
+                    }
+                )
             }
             project.rootProject.tasks.findByName(AndroidXPlugin.ZIP_TEST_CONFIGS_WITH_APKS_TASK)!!
                 .dependsOn(task)
@@ -103,8 +112,7 @@
             return project.parent!!.tasks.withType(GenerateMediaTestConfigurationTask::class.java)
                 .named(
                     "support-$mediaPrefix-test${
-                    AndroidXPlugin
-                        .GENERATE_TEST_CONFIGURATION_TASK
+                    AndroidXPlugin.GENERATE_TEST_CONFIGURATION_TASK
                     }"
                 )
         }
@@ -203,4 +211,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/busytown/impl/build.sh b/busytown/impl/build.sh
index 67bb30c..09fac0b 100755
--- a/busytown/impl/build.sh
+++ b/busytown/impl/build.sh
@@ -6,14 +6,18 @@
 # find script
 SCRIPT_DIR="$(cd $(dirname $0) && pwd)"
 
-# resolve DIST_DIR
+# resolve directories
 if [ "$DIST_DIR" == "" ]; then
   DIST_DIR="$SCRIPT_DIR/../../../../out/dist"
 fi
 mkdir -p "$DIST_DIR"
-
-# cd to checkout root
 cd "$SCRIPT_DIR/../../../.."
+OUT_DIR="$PWD/out"
+mkdir -p "$OUT_DIR"
+
+# record the build start time
+BUILD_START_MARKER="$OUT_DIR/build.sh.start"
+touch $BUILD_START_MARKER
 
 # runs a given command and prints its result if it fails
 function run() {
@@ -37,9 +41,12 @@
 fi
 # --no-watch-fs disables file system watch, because it does not work on busytown
 # due to our builders using OS that is too old.
-run $PROJECTS_ARG OUT_DIR=out DIST_DIR=$DIST_DIR ANDROID_HOME=./prebuilts/fullsdk-linux \
+run $PROJECTS_ARG OUT_DIR=$OUT_DIR DIST_DIR=$DIST_DIR ANDROID_HOME=./prebuilts/fullsdk-linux \
     frameworks/support/gradlew -p frameworks/support \
     --stacktrace \
     -Pandroidx.summarizeStderr \
     --no-watch-fs \
     "$@"
+
+# check that no unexpected modifications were made to the source repository, such as new cache directories
+$SCRIPT_DIR/verify_no_caches_in_source_repo.sh $BUILD_START_MARKER
diff --git a/busytown/impl/verify_no_caches_in_source_repo.sh b/busytown/impl/verify_no_caches_in_source_repo.sh
new file mode 100755
index 0000000..7e5e028
--- /dev/null
+++ b/busytown/impl/verify_no_caches_in_source_repo.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+set -e
+
+function usage() {
+  echo "Confirms that no unexpected, generated files exist in the source repository"
+  echo
+  echo "Usage: $0 <timestamp file>"
+  echo
+  echo "<timestamp file>: any file newer than this one will be considered an error unless it is already exempted"
+  return 1
+}
+
+# parse arguments
+# a file whose timestamp is the oldest acceptable timestamp for source files
+COMPARE_TO_FILE="$1"
+if [ "$COMPARE_TO_FILE" == "" ]; then
+  usage
+fi
+
+# get script path
+SCRIPT_DIR="$(cd $(dirname $0) && pwd)"
+SOURCE_DIR="$(cd $SCRIPT_DIR/../.. && pwd)"
+
+# confirm that no files in the source repo were unexpectedly created (other than known exemptions)
+function checkForGeneratedFilesInSourceRepo() {
+
+  # Paths that are still expected to be generated and that we have to allow
+  # If you need add or remove an exemption here, update cleanBuild.sh too
+  EXEMPT_PATHS=".gradle appsearch/local-storage/.cxx buildSrc/.gradle local.properties"
+  # put "./" in front of each path to match the output from 'find'
+  EXEMPT_PATHS="$(echo " $EXEMPT_PATHS" | sed 's| | ./|g')"
+  # build a `find` argument for skipping descending into the exempt paths
+  EXEMPTIONS_ARGUMENT="$(echo $EXEMPT_PATHS | sed 's/ /\n/g' | sed 's|\(.*\)|-path \1 -prune -o|g' | xargs echo)"
+
+  # Search for files that were created or updated more recently than the build start.
+  # Unfortunately we can't also include directories because the `test` task seems to update
+  # the modification time in several projects
+  GENERATED_FILES="$(cd $SOURCE_DIR && find . $EXEMPTIONS_ARGUMENT -newer $COMPARE_TO_FILE -type f)"
+  UNEXPECTED_GENERATED_FILES=""
+  for f in $GENERATED_FILES; do
+    exempt=false
+    for exemption in $EXEMPT_PATHS; do
+      if [ "$f" == "$exemption" ]; then
+        exempt=true
+        break
+      fi
+      if [ "$f" == "$(dirname $exemption)" ]; then
+        # When the exempt directory gets created, its parent dir will be modified
+        # So, we ignore changes to the parent dir too (but not necessarily changes in sibling dirs)
+        exempt=true
+        break
+      fi
+    done
+    if [ "$exempt" == "false" ]; then
+      UNEXPECTED_GENERATED_FILES="$UNEXPECTED_GENERATED_FILES $f"
+    fi
+  done
+  if [ "$UNEXPECTED_GENERATED_FILES" != "" ]; then
+    echo >&2
+    echo "Unexpectedly found these files generated or modified by the build:
+
+${UNEXPECTED_GENERATED_FILES}
+
+Generated files should go in OUT_DIR instead because that is where developers expect to find them
+(to make it easier to diagnose build problems: inspect or delete these files)" >&2
+    exit 1
+  fi
+}
+checkForGeneratedFilesInSourceRepo
diff --git a/camera/README.md b/camera/README.md
index b758868..8ea1e6d 100644
--- a/camera/README.md
+++ b/camera/README.md
@@ -7,7 +7,7 @@
 [Release notes](https://developer.android.com/jetpack/androidx/releases/camera)
 
 [Browse
-source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/camera/)
+source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/camera/)
 
 [Reference
 documentation](https://developer.android.com/reference/androidx/classes.html)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
index cd16128..6c6d6da 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
@@ -20,6 +20,7 @@
 import android.hardware.camera2.CameraCharacteristics
 import android.view.Surface
 import androidx.camera.camera2.pipe.CameraPipe
+import androidx.camera.camera2.pipe.impl.Log
 import androidx.camera.camera2.pipe.integration.config.CameraConfig
 import androidx.camera.camera2.pipe.integration.config.CameraScope
 import androidx.camera.camera2.pipe.integration.impl.CameraCallbackMap
@@ -29,11 +30,14 @@
 import androidx.camera.core.ZoomState
 import androidx.camera.core.impl.CameraCaptureCallback
 import androidx.camera.core.impl.CameraInfoInternal
+import androidx.camera.core.impl.Quirks
 import androidx.camera.core.impl.utils.CameraOrientationUtil
 import androidx.lifecycle.LiveData
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
+internal val defaultQuirks = Quirks(emptyList())
+
 /**
  * Adapt the [CameraInfoInternal] interface to [CameraPipe].
  */
@@ -88,4 +92,9 @@
 
     override fun getImplementationType(): String = "CameraPipe"
     override fun toString(): String = "CameraInfoAdapter<$cameraConfig.cameraId>"
+
+    override fun getCameraQuirks(): Quirks {
+        Log.warn { "TODO: Quirks are not yet supported." }
+        return defaultQuirks
+    }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
index 3174c2c..d3dcff7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
@@ -28,13 +28,11 @@
 import androidx.camera.core.impl.CameraInternal
 import androidx.camera.core.impl.LiveDataObservable
 import androidx.camera.core.impl.Observable
-import androidx.camera.core.impl.Quirks
 import androidx.camera.core.impl.utils.futures.Futures
 import com.google.common.util.concurrent.ListenableFuture
 import kotlinx.atomicfu.atomic
 import javax.inject.Inject
 
-internal val defaultQuirks = Quirks(emptyList())
 internal val cameraAdapterIds = atomic(0)
 
 /**
@@ -58,11 +56,6 @@
         // TODO: Consider preloading the list of camera ids and metadata.
     }
 
-    override fun getCameraQuirks(): Quirks {
-        warn { "TODO: Quirks are not yet supported." }
-        return defaultQuirks
-    }
-
     // Load / unload methods
     override fun open() {
         debug { "$this#open" }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
index 1dddd47..c797ab9 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
@@ -95,7 +95,8 @@
     companion object Constants3A {
         // Constants related to controlling the time or frame budget a 3A operation should get.
         const val DEFAULT_FRAME_LIMIT = 60
-        const val DEFAULT_TIME_LIMIT_MS = 3000
+        const val DEFAULT_TIME_LIMIT_MS = 3_000
+        const val DEFAULT_TIME_LIMIT_NS = 3_000_000_000L
 
         // Constants related to metering regions.
         /** No metering region is specified. */
@@ -170,19 +171,19 @@
          *
          * @param frameLimit the maximum number of frames to wait before we give up waiting for
          * this operation to complete.
-         * @param timeLimitMs the maximum time limit in ms we wait before we give up waiting for
+         * @param timeLimitNs the maximum time limit in ms we wait before we give up waiting for
          * this operation to complete.
          *
          * @return [Result3A], which will contain the latest frame number at which the locks were
          * applied or the frame number at which the method returned early because either frame limit
          * or time limit was reached.
          */
-        fun lock3A(
+        suspend fun lock3A(
             aeLockBehavior: Lock3ABehavior? = null,
             afLockBehavior: Lock3ABehavior? = null,
             awbLockBehavior: Lock3ABehavior? = null,
             frameLimit: Int = DEFAULT_FRAME_LIMIT,
-            timeLimitMs: Int = DEFAULT_TIME_LIMIT_MS
+            timeLimitNs: Long = DEFAULT_TIME_LIMIT_NS
         ): Deferred<Result3A>
 
         /**
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Lock3ABehavior.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Lock3ABehavior.kt
index 27406f7..5d594f7 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Lock3ABehavior.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Lock3ABehavior.kt
@@ -41,4 +41,36 @@
      * Initiate a new scan, and then lock the values once the scan is done.
      */
     AFTER_NEW_SCAN,
-}
\ No newline at end of file
+}
+
+fun Lock3ABehavior?.shouldUnlockAe() =
+    this == Lock3ABehavior.AFTER_NEW_SCAN
+
+fun Lock3ABehavior?.shouldUnlockAf() =
+    this == Lock3ABehavior.AFTER_NEW_SCAN
+
+fun Lock3ABehavior?.shouldUnlockAwb() =
+    this == Lock3ABehavior.AFTER_NEW_SCAN
+
+// For ae and awb if we set the lock = true in the capture request the camera device
+// locks them immediately. So when we want to wait for ae to converge we have to explicitly
+// wait for it to converge.
+fun Lock3ABehavior?.shouldWaitForAeToConverge() =
+    this != null && this != Lock3ABehavior.IMMEDIATE
+
+fun Lock3ABehavior?.shouldWaitForAwbToConverge() =
+    this != null && this != Lock3ABehavior.IMMEDIATE
+
+// TODO(sushilnath@): add the optimization to not wait for af to converge before sending the
+// trigger for modes other than CONTINUOUS_VIDEO. The paragraph below explains the reasoning.
+//
+// For af, if the mode is MACRO, AUTO or CONTINUOUS_PICTURE and we send a capture request to
+// start an af trigger then camera device starts a new scan(for AUTO mode) or waits for the
+// current scan to finish(for CONTINUOUS_PICTURE) and then locks the auto-focus, so if we want
+// to wait for af to converge before locking it, we don't have to explicitly wait for
+// convergence, we can send the trigger right away, but if the mode is CONTINUOUS_VIDEO then
+// sending a request to start a trigger locks the auto focus immediately, so if we want af to
+// converge first then we have to explicitly wait for it.
+// Ref: https://developer.android.com/reference/android/hardware/camera2/CaptureResult#CONTROL_AF_STATE
+fun Lock3ABehavior?.shouldWaitForAfToConverge() =
+    this != null && this != Lock3ABehavior.IMMEDIATE
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/CameraGraphSessionImpl.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/CameraGraphSessionImpl.kt
index 9d5c485..2b7aa32 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/CameraGraphSessionImpl.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/CameraGraphSessionImpl.kt
@@ -30,6 +30,7 @@
 import kotlinx.coroutines.Deferred
 
 internal val cameraGraphSessionIds = atomic(0)
+
 class CameraGraphSessionImpl(
     private val token: TokenLock.Token,
     private val graphProcessor: GraphProcessor,
@@ -84,14 +85,20 @@
         TODO("Implement setTorch")
     }
 
-    override fun lock3A(
+    override suspend fun lock3A(
         aeLockBehavior: Lock3ABehavior?,
         afLockBehavior: Lock3ABehavior?,
         awbLockBehavior: Lock3ABehavior?,
         frameLimit: Int,
-        timeLimitMs: Int
+        timeLimitNs: Long
     ): Deferred<Result3A> {
-        TODO("Implement lock3A")
+        // TODO(sushilnath): check if the device or the current mode supports lock for each of
+        // ae, af and awb respectively. If not supported return an exception or return early with
+        // the right status code.
+        return controller3A.lock3A(
+            aeLockBehavior, afLockBehavior, awbLockBehavior, frameLimit,
+            timeLimitNs
+        )
     }
 
     override fun lock3A(
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Controller3A.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Controller3A.kt
index df3b3c2..4de0475 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Controller3A.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Controller3A.kt
@@ -16,16 +16,28 @@
 
 package androidx.camera.camera2.pipe.impl
 
+import android.hardware.camera2.CaptureRequest.CONTROL_AF_TRIGGER_CANCEL
+import android.hardware.camera2.CaptureRequest.CONTROL_AF_TRIGGER_START
 import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureRequest.CONTROL_AF_TRIGGER
 import android.hardware.camera2.CaptureResult
 import android.hardware.camera2.params.MeteringRectangle
 import androidx.annotation.GuardedBy
 import androidx.camera.camera2.pipe.AeMode
 import androidx.camera.camera2.pipe.AfMode
 import androidx.camera.camera2.pipe.AwbMode
+import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraGraph.Constants3A.FRAME_NUMBER_INVALID
+import androidx.camera.camera2.pipe.Lock3ABehavior
 import androidx.camera.camera2.pipe.Result3A
 import androidx.camera.camera2.pipe.Status3A
+import androidx.camera.camera2.pipe.impl.Log.debug
+import androidx.camera.camera2.pipe.shouldUnlockAe
+import androidx.camera.camera2.pipe.shouldUnlockAf
+import androidx.camera.camera2.pipe.shouldUnlockAwb
+import androidx.camera.camera2.pipe.shouldWaitForAeToConverge
+import androidx.camera.camera2.pipe.shouldWaitForAfToConverge
+import androidx.camera.camera2.pipe.shouldWaitForAwbToConverge
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.cancel
@@ -38,6 +50,66 @@
     private val graphState3A: GraphState3A,
     private val graphListener3A: Listener3A
 ) {
+    companion object {
+        private val aeConvergedStateList = listOf(
+            CaptureResult.CONTROL_AE_STATE_CONVERGED,
+            CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED,
+            CaptureResult.CONTROL_AE_STATE_LOCKED
+        )
+
+        private val awbConvergedStateList = listOf(
+            CaptureResult.CONTROL_AWB_STATE_CONVERGED,
+            CaptureResult.CONTROL_AWB_STATE_LOCKED
+        )
+
+        private val afConvergedStateList = listOf(
+            CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED,
+            CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED,
+            CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED,
+            CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED
+        )
+
+        private val aeLockedStateList = listOf(CaptureResult.CONTROL_AE_STATE_LOCKED)
+
+        private val awbLockedStateList = listOf(CaptureResult.CONTROL_AWB_STATE_LOCKED)
+
+        private val afLockedStateList = listOf(
+            CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED,
+            CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED
+        )
+
+        val parameterForAfTriggerStart = mapOf<CaptureRequest.Key<*>, Any>(
+            CONTROL_AF_TRIGGER to CONTROL_AF_TRIGGER_START
+        )
+
+        val parameterForAfTriggerCancel = mapOf<CaptureRequest.Key<*>, Any>(
+            CONTROL_AF_TRIGGER to CONTROL_AF_TRIGGER_CANCEL
+        )
+
+        private val result3ASubmitFailed = Result3A(FRAME_NUMBER_INVALID, Status3A.SUBMIT_FAILED)
+
+        private val aeUnlockedStateList = listOf(
+            CaptureResult.CONTROL_AE_STATE_INACTIVE,
+            CaptureResult.CONTROL_AE_STATE_SEARCHING,
+            CaptureResult.CONTROL_AE_STATE_CONVERGED,
+            CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED
+        )
+
+        private val afUnlockedStateList = listOf(
+            CaptureResult.CONTROL_AF_STATE_INACTIVE,
+            CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN,
+            CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN,
+            CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED,
+            CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED
+        )
+
+        private val awbUnlockedStateList = listOf(
+            CaptureResult.CONTROL_AWB_STATE_INACTIVE,
+            CaptureResult.CONTROL_AWB_STATE_SEARCHING,
+            CaptureResult.CONTROL_AWB_STATE_CONVERGED
+        )
+    }
+
     // Keep track of the result associated with latest call to update3A. If update3A is called again
     // and the current result is not complete, we will cancel the current result.
     @GuardedBy("this")
@@ -59,7 +131,7 @@
         // Update the 3A state of the graph. This will make sure then when GraphProcessor builds
         // the next request it will apply the 3A parameters corresponding to the updated 3A state
         // to the request.
-        graphState3A.update(aeMode, afMode, awbMode, aeRegions, afRegions, awbRegions)
+        graphState3A.update(aeMode, afMode, awbMode, aeRegions, afRegions, awbRegions, null, null)
         // Try submitting a new repeating request with the 3A parameters corresponding to the new
         // 3A state and corresponding listeners.
         graphProcessor.invalidate()
@@ -110,13 +182,261 @@
 
         if (!graphProcessor.submit(extra3AParams)) {
             graphListener3A.removeListener(listener)
-            return CompletableDeferred(
-                Result3A(FRAME_NUMBER_INVALID, Status3A.SUBMIT_FAILED)
-            )
+            return CompletableDeferred(result3ASubmitFailed)
         }
         return listener.getDeferredResult()
     }
 
+    /**
+     * Given the desired lock behaviors for ae, af and awb, this method, (a) first unlocks them and
+     * wait for them to converge, and then (b) locks them.
+     *
+     * (a) In this step, as needed, we first send a single request with 'af trigger = cancel' to
+     * unlock af, and then a repeating request to unlock ae and awb. We suspend till we receive a
+     * response from the camera that each of the ae, af awb are converged.
+     * (b) In this step, as needed, we submit a repeating request to lock ae and awb, and then a
+     * single request to lock af by setting 'af trigger = start'. Once these requests are submitted
+     * we don't wait further and immediately return a Deferred<Result3A> which gets completed when
+     * the capture result with correct lock states for ae, af and awb is received.
+     *
+     * If we received an error when submitting any of the above requests or if waiting for the
+     * desired 3A state times out then we return early with the appropriate status code.
+     *
+     * Note: the frameLimit and timeLimitNs applies to each of the above steps (a) and (b) and not
+     * as a whole for the whole lock3A method. Thus, in the worst case this method including the
+     * completion of returned Deferred<Result3A> can take 2 * min(time equivalent of frameLimit,
+     * timeLimit) to complete
+     */
+    suspend fun lock3A(
+        aeLockBehavior: Lock3ABehavior? = null,
+        afLockBehavior: Lock3ABehavior? = null,
+        awbLockBehavior: Lock3ABehavior? = null,
+        frameLimit: Int = CameraGraph.DEFAULT_FRAME_LIMIT,
+        timeLimitMsNs: Long? = CameraGraph.DEFAULT_TIME_LIMIT_NS
+    ): Deferred<Result3A> {
+        // If we explicitly need to unlock af first before proceeding to lock it, we need to send
+        // a single request with TRIGGER = TRIGGER_CANCEL so that af can start a fresh scan.
+        if (afLockBehavior.shouldUnlockAf()) {
+            debug { "lock3A - sending a request to unlock af first." }
+            if (!graphProcessor.submit(parameterForAfTriggerCancel)) {
+                return CompletableDeferred(result3ASubmitFailed)
+            }
+        }
+
+        // As needed unlock ae, awb and wait for ae, af and awb to converge.
+        if (aeLockBehavior.shouldWaitForAeToConverge() ||
+            afLockBehavior.shouldWaitForAfToConverge() ||
+            awbLockBehavior.shouldWaitForAwbToConverge()
+        ) {
+            val converged3AExitConditions = createConverged3AExitConditions(
+                aeLockBehavior.shouldWaitForAeToConverge(),
+                afLockBehavior.shouldWaitForAfToConverge(),
+                awbLockBehavior.shouldWaitForAwbToConverge()
+            )
+            val listener = Result3AStateListenerImpl(
+                converged3AExitConditions,
+                frameLimit,
+                timeLimitMsNs
+            )
+            graphListener3A.addListener(listener)
+
+            // If we have to explicitly unlock ae, awb, then update the 3A state of the camera
+            // graph. This is because ae, awb lock values should stay as part of repeating
+            // request to the camera device. For af we need only one single request to trigger it,
+            // leaving it unset in the subsequent requests to the camera device will not affect the
+            // previously sent af trigger.
+            val aeLockValue = if (aeLockBehavior.shouldUnlockAe()) false else null
+            val awbLockValue = if (awbLockBehavior.shouldUnlockAwb()) false else null
+            if (aeLockValue != null || awbLockValue != null) {
+                debug { "lock3A - setting aeLock=$aeLockValue, awbLock=$awbLockValue" }
+                graphState3A.update(
+                    aeLock = aeLockValue,
+                    awbLock = awbLockValue
+                )
+            }
+            graphProcessor.invalidate()
+
+            debug {
+                "lock3A - waiting for" +
+                    (if (aeLockBehavior.shouldWaitForAeToConverge()) " ae" else "") +
+                    (if (afLockBehavior.shouldWaitForAfToConverge()) " af" else "") +
+                    (if (awbLockBehavior.shouldWaitForAwbToConverge()) " awb" else "") +
+                    " to converge before locking them."
+            }
+            val result = listener.getDeferredResult().await()
+            debug {
+                "lock3A - converged at frame number=${result.frameNumber.value}, status=${result
+                    .status}"
+            }
+            // Return immediately if we encounter an error when unlocking and waiting for
+            // convergence.
+            if (result.status != Status3A.OK) {
+                return CompletableDeferred(result)
+            }
+        }
+
+        return lock3ANow(aeLockBehavior, afLockBehavior, awbLockBehavior, frameLimit, timeLimitMsNs)
+    }
+
+    /**
+     * This method unlocks ae, af and awb, as specified by setting the corresponding parameter to
+     * true.
+     *
+     * There are two requests involved in this operation, (a) a single request with af trigger =
+     * cancel, to unlock af, and then (a) a repeating request to unlock ae, awb.
+     */
+    suspend fun unlock3A(
+        ae: Boolean? = null,
+        af: Boolean? = null,
+        awb: Boolean? = null
+    ): Deferred<Result3A> {
+        check(ae == true || af == true || awb == true) { "No parameter has value as true" }
+        // If we explicitly need to unlock af first before proceeding to lock it, we need to send
+        // a single request with TRIGGER = TRIGGER_CANCEL so that af can start a fresh scan.
+        if (af == true) {
+            debug { "unlock3A - sending a request to unlock af first." }
+            if (!graphProcessor.submit(parameterForAfTriggerCancel)) {
+                debug { "unlock3A - request to unlock af failed, returning early." }
+                return CompletableDeferred(result3ASubmitFailed)
+            }
+        }
+
+        // As needed unlock ae, awb and wait for ae, af and awb to converge.
+        val unlocked3AExitConditions = createUnLocked3AExitConditions(
+            ae == true,
+            af == true,
+            awb == true
+        )
+        val listener = Result3AStateListenerImpl(unlocked3AExitConditions)
+        graphListener3A.addListener(listener)
+
+        // Update the 3A state of the camera graph and invalidate the repeating request with the
+        // new state.
+        val aeLockValue = if (ae == true) false else null
+        val awbLockValue = if (awb == true) false else null
+        if (aeLockValue != null || awbLockValue != null) {
+            debug { "unlock3A - updating graph state, aeLock=$aeLockValue, awbLock=$awbLockValue" }
+            graphState3A.update(
+                aeLock = aeLockValue,
+                awbLock = awbLockValue
+            )
+        }
+        graphProcessor.invalidate()
+        return listener.getDeferredResult()
+    }
+
+    private suspend fun lock3ANow(
+        aeLockBehavior: Lock3ABehavior?,
+        afLockBehavior: Lock3ABehavior?,
+        awbLockBehavior: Lock3ABehavior?,
+        frameLimit: Int?,
+        timeLimitMsNs: Long?
+    ): Deferred<Result3A> {
+        val finalAeLockValue = if (aeLockBehavior == null) null else true
+        val finalAwbLockValue = if (awbLockBehavior == null) null else true
+        val locked3AExitConditions = createLocked3AExitConditions(
+            finalAeLockValue != null,
+            afLockBehavior != null,
+            finalAwbLockValue != null
+        )
+
+        var resultForLocked: Deferred<Result3A>? = null
+        if (locked3AExitConditions.isNotEmpty()) {
+            val listener = Result3AStateListenerImpl(
+                locked3AExitConditions,
+                frameLimit,
+                timeLimitMsNs
+            )
+            graphListener3A.addListener(listener)
+            graphState3A.update(aeLock = finalAeLockValue, awbLock = finalAwbLockValue)
+            debug {
+                "lock3A - submitting request with aeLock=$finalAeLockValue , " +
+                    "awbLock=$finalAwbLockValue"
+            }
+            graphProcessor.invalidate()
+            resultForLocked = listener.getDeferredResult()
+        }
+
+        if (afLockBehavior == null) {
+            return resultForLocked!!
+        }
+
+        debug { "lock3A - submitting a request to lock af." }
+        if (!graphProcessor.submit(parameterForAfTriggerStart)) {
+            // TODO(sushilnath@): Change the error code to a more specific code so it's clear
+            // that one of the request in sequence of requests failed and the caller should
+            // unlock 3A to bring the 3A system to an initial state and then try again if they
+            // want to. The other option is to reset or restore the 3A state here.
+            return CompletableDeferred(result3ASubmitFailed)
+        }
+        return resultForLocked!!
+    }
+
+    private fun createConverged3AExitConditions(
+        waitForAeToConverge: Boolean,
+        waitForAfToConverge: Boolean,
+        waitForAwbToConverge: Boolean
+    ): Map<CaptureResult.Key<*>, List<Any>> {
+        if (
+            !waitForAeToConverge && !waitForAfToConverge && !waitForAwbToConverge
+        ) {
+            return mapOf()
+        }
+        val exitConditionMapForConverged = mutableMapOf<CaptureResult.Key<*>, List<Any>>()
+        if (waitForAeToConverge) {
+            exitConditionMapForConverged[CaptureResult.CONTROL_AE_STATE] = aeConvergedStateList
+        }
+        if (waitForAwbToConverge) {
+            exitConditionMapForConverged[CaptureResult.CONTROL_AWB_STATE] = awbConvergedStateList
+        }
+        if (waitForAfToConverge) {
+            exitConditionMapForConverged[CaptureResult.CONTROL_AF_STATE] = afConvergedStateList
+        }
+        return exitConditionMapForConverged
+    }
+
+    private fun createLocked3AExitConditions(
+        waitForAeToLock: Boolean,
+        waitForAfToLock: Boolean,
+        waitForAwbToLock: Boolean
+    ): Map<CaptureResult.Key<*>, List<Any>> {
+        if (!waitForAeToLock && !waitForAfToLock && !waitForAwbToLock) {
+            return mapOf()
+        }
+        val exitConditionMapForLocked = mutableMapOf<CaptureResult.Key<*>, List<Any>>()
+        if (waitForAeToLock) {
+            exitConditionMapForLocked[CaptureResult.CONTROL_AE_STATE] = aeLockedStateList
+        }
+        if (waitForAfToLock) {
+            exitConditionMapForLocked[CaptureResult.CONTROL_AF_STATE] = afLockedStateList
+        }
+        if (waitForAwbToLock) {
+            exitConditionMapForLocked[CaptureResult.CONTROL_AWB_STATE] = awbLockedStateList
+        }
+        return exitConditionMapForLocked
+    }
+
+    private fun createUnLocked3AExitConditions(
+        ae: Boolean,
+        af: Boolean,
+        awb: Boolean
+    ): Map<CaptureResult.Key<*>, List<Any>> {
+        if (!ae && !af && !awb) {
+            return mapOf()
+        }
+        val exitConditionMapForUnLocked = mutableMapOf<CaptureResult.Key<*>, List<Any>>()
+        if (ae) {
+            exitConditionMapForUnLocked[CaptureResult.CONTROL_AE_STATE] = aeUnlockedStateList
+        }
+        if (af) {
+            exitConditionMapForUnLocked[CaptureResult.CONTROL_AF_STATE] = afUnlockedStateList
+        }
+        if (awb) {
+            exitConditionMapForUnLocked[CaptureResult.CONTROL_AWB_STATE] = awbUnlockedStateList
+        }
+        return exitConditionMapForUnLocked
+    }
+
     // We create a map for the 3A modes and the desired values and leave out the keys
     // corresponding to the metering regions. The reason being the camera framework can chose to
     // crop or modify the metering regions as per its constraints. So when we receive at least
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/GraphState3A.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/GraphState3A.kt
index b69476c..97664c0 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/GraphState3A.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/GraphState3A.kt
@@ -28,6 +28,12 @@
  *
  * This object is used to maintain the key-value pairs for the most recent 3A state that is used
  * when building the requests that are sent to a CameraCaptureSession.
+ *
+ * The state is comprised of the modes, metering regions for ae, af and awb, and locks for ae and
+ * awb. We don't track the lock for af since af lock is achieved by setting 'af trigger = start' in
+ * in a request and then omitting the af trigger field in the subsequent requests doesn't disturb
+ * the af state. However for ae and awb, the lock type is boolean and should be explicitly set to
+ * 'true' in the subsequent requests once we have locked ae/awb and want them to stay locked.
  */
 @CameraGraphScope
 class GraphState3A @Inject constructor() {
@@ -37,14 +43,18 @@
     private var aeRegions: List<MeteringRectangle>? = null
     private var afRegions: List<MeteringRectangle>? = null
     private var awbRegions: List<MeteringRectangle>? = null
+    private var aeLock: Boolean? = null
+    private var awbLock: Boolean? = null
 
     fun update(
-        aeMode: AeMode?,
-        afMode: AfMode?,
-        awbMode: AwbMode?,
-        aeRegions: List<MeteringRectangle>?,
-        afRegions: List<MeteringRectangle>?,
-        awbRegions: List<MeteringRectangle>?
+        aeMode: AeMode? = null,
+        afMode: AfMode? = null,
+        awbMode: AwbMode? = null,
+        aeRegions: List<MeteringRectangle>? = null,
+        afRegions: List<MeteringRectangle>? = null,
+        awbRegions: List<MeteringRectangle>? = null,
+        aeLock: Boolean? = null,
+        awbLock: Boolean? = null
     ) {
         synchronized(this) {
             aeMode?.let { this.aeMode = it }
@@ -53,6 +63,8 @@
             aeRegions?.let { this.aeRegions = it }
             afRegions?.let { this.afRegions = it }
             awbRegions?.let { this.awbRegions = it }
+            aeLock?.let { this.aeLock = it }
+            awbLock?.let { this.awbLock = it }
         }
     }
 
@@ -65,6 +77,8 @@
             aeRegions?.let { map.put(CaptureRequest.CONTROL_AE_REGIONS, it.toTypedArray()) }
             afRegions?.let { map.put(CaptureRequest.CONTROL_AF_REGIONS, it.toTypedArray()) }
             awbRegions?.let { map.put(CaptureRequest.CONTROL_AWB_REGIONS, it.toTypedArray()) }
+            aeLock?.let { map.put(CaptureRequest.CONTROL_AE_LOCK, it) }
+            awbLock?.let { map.put(CaptureRequest.CONTROL_AWB_LOCK, it) }
             return map
         }
     }
@@ -77,6 +91,8 @@
             aeRegions?.let { builder.set(CaptureRequest.CONTROL_AE_REGIONS, it.toTypedArray()) }
             afRegions?.let { builder.set(CaptureRequest.CONTROL_AF_REGIONS, it.toTypedArray()) }
             awbRegions?.let { builder.set(CaptureRequest.CONTROL_AWB_REGIONS, it.toTypedArray()) }
+            aeLock?.let { builder.set(CaptureRequest.CONTROL_AE_LOCK, it) }
+            awbLock?.let { builder.set(CaptureRequest.CONTROL_AWB_LOCK, it) }
         }
     }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3ALock3ATest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3ALock3ATest.kt
new file mode 100644
index 0000000..8d837e7
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3ALock3ATest.kt
@@ -0,0 +1,641 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.impl
+
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureResult
+import android.os.Build
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.Lock3ABehavior
+import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestNumber
+import androidx.camera.camera2.pipe.Status3A
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.testing.CameraPipeRobolectricTestRunner
+import androidx.camera.camera2.pipe.testing.FakeFrameMetadata
+import androidx.camera.camera2.pipe.testing.FakeGraphProcessor
+import androidx.camera.camera2.pipe.testing.FakeRequestMetadata
+import androidx.camera.camera2.pipe.testing.FakeRequestProcessor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+
+@RunWith(CameraPipeRobolectricTestRunner::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class Controller3ALock3ATest {
+    private val graphProcessor = FakeGraphProcessor()
+    private val graphState3A = GraphState3A()
+    private val requestProcessor = FakeRequestProcessor(graphState3A)
+    private val listener3A = Listener3A()
+    private val controller3A = Controller3A(graphProcessor, graphState3A, listener3A)
+
+    @Test
+    fun testAfImmediateAeImmediate(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val result = controller3A.lock3A(
+            afLockBehavior = Lock3ABehavior.IMMEDIATE,
+            aeLockBehavior = Lock3ABehavior.IMMEDIATE
+        )
+        assertThat(result.isCompleted).isFalse()
+
+        // Since requirement of to lock both AE and AF immediately, the requests to lock AE and AF
+        // are sent right away. The result of lock3A call will complete once AE and AF have reached
+        // their desired states. In this response i.e cameraResponse1, AF is still scanning so the
+        // result won't be complete.
+        val cameraResponse = GlobalScope.async {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AF_STATE to CaptureResult
+                            .CONTROL_AF_STATE_PASSIVE_SCAN,
+                        CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_LOCKED
+                    )
+                )
+            )
+        }
+
+        cameraResponse.await()
+        assertThat(result.isCompleted).isFalse()
+
+        // One we we are notified that the AE and AF are in locked state, the result of lock3A call
+        // will complete.
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AF_STATE to CaptureResult
+                            .CONTROL_AF_STATE_FOCUSED_LOCKED,
+                        CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_LOCKED
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        assertThat(result3A.status).isEqualTo(Status3A.OK)
+
+        // We not check if the correct sequence of requests were submitted by lock3A call. The
+        // request should be a repeating request to lock AE.
+        val request1 = requestProcessor.nextEvent().request
+        assertThat(request1!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+
+        // The second request should be a single request to lock AF.
+        val request2 = requestProcessor.nextEvent().request
+        assertThat(request2!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER]).isEqualTo(
+            CaptureRequest.CONTROL_AF_TRIGGER_START
+        )
+        assertThat(request2.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+    }
+
+    @Test
+    fun testAfImmediateAeAfterCurrentScan(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val lock3AAsyncTask = GlobalScope.async {
+            controller3A.lock3A(
+                afLockBehavior = Lock3ABehavior.IMMEDIATE,
+                aeLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN
+            )
+        }
+        assertThat(lock3AAsyncTask.isCompleted).isFalse()
+        // Launch a task to repeatedly invoke a given capture result.
+        GlobalScope.launch {
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(
+                        requestNumber = RequestNumber(1)
+                    )
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(101L),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(101L),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AF_STATE to
+                                CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN,
+                            CaptureResult.CONTROL_AE_STATE to
+                                CaptureResult.CONTROL_AE_STATE_CONVERGED
+                        )
+                    )
+                )
+                delay(Companion.FRAME_RATE_MS)
+            }
+        }
+
+        val result = lock3AAsyncTask.await()
+        // Result of lock3A call shouldn't be complete yet since the AE and AF are not locked yet.
+        assertThat(result.isCompleted).isFalse()
+
+        // Check the correctness of the requests submitted by lock3A.
+        // One repeating request was sent to monitor the state of AE to get converged.
+        requestProcessor.nextEvent().request
+        // Once AE is converged, another repeatingrequest is sent to lock AE.
+        val request1 = requestProcessor.nextEvent().request
+        assertThat(request1!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AF_STATE to CaptureResult
+                            .CONTROL_AF_STATE_FOCUSED_LOCKED,
+                        CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_LOCKED
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        assertThat(result3A.status).isEqualTo(Status3A.OK)
+
+        // A single request to lock AF must have been used as well.
+        val request2 = requestProcessor.nextEvent().request
+        assertThat(request2!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER]).isEqualTo(
+            CaptureRequest.CONTROL_AF_TRIGGER_START
+        )
+    }
+
+    @Test
+    fun testAfImmediateAeAfterNewScan(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val lock3AAsyncTask = GlobalScope.async {
+            controller3A.lock3A(
+                afLockBehavior = Lock3ABehavior.IMMEDIATE,
+                aeLockBehavior = Lock3ABehavior.AFTER_NEW_SCAN
+            )
+        }
+        assertThat(lock3AAsyncTask.isCompleted).isFalse()
+        GlobalScope.launch {
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(
+                        requestNumber = RequestNumber(1)
+                    )
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(101L),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(101L),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AF_STATE to
+                                CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN,
+                            CaptureResult.CONTROL_AE_STATE to
+                                CaptureResult.CONTROL_AE_STATE_CONVERGED
+                        )
+                    )
+                )
+                delay(FRAME_RATE_MS)
+            }
+        }
+
+        val result = lock3AAsyncTask.await()
+        assertThat(result.isCompleted).isFalse()
+
+        // For a new AE scan we first send a request to unlock AE just in case it was
+        // previously or internally locked.
+        val request1 = requestProcessor.nextEvent().request
+        assertThat(request1!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            false
+        )
+
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AF_STATE to CaptureResult
+                            .CONTROL_AF_STATE_FOCUSED_LOCKED,
+                        CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_LOCKED
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        assertThat(result3A.status).isEqualTo(Status3A.OK)
+
+        // There should be one more request to lock AE after new scan is done.
+        val request2 = requestProcessor.nextEvent().request
+        assertThat(request2!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+
+        // And one request to lock AF.
+        val request3 = requestProcessor.nextEvent().request
+        assertThat(request3!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER]).isEqualTo(
+            CaptureRequest.CONTROL_AF_TRIGGER_START
+        )
+        assertThat(request3.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+    }
+
+    @Test
+    fun testAfAfterCurrentScanAeImmediate(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val lock3AAsyncTask = GlobalScope.async {
+            controller3A.lock3A(
+                afLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
+                aeLockBehavior = Lock3ABehavior.IMMEDIATE
+            )
+        }
+        assertThat(lock3AAsyncTask.isCompleted).isFalse()
+        GlobalScope.launch {
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(
+                        requestNumber = RequestNumber(1)
+                    )
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(101L),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(101L),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AF_STATE to
+                                CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED,
+                            CaptureResult.CONTROL_AE_STATE to
+                                CaptureResult.CONTROL_AE_STATE_CONVERGED
+                        )
+                    )
+                )
+                delay(FRAME_RATE_MS)
+            }
+        }
+
+        val result = lock3AAsyncTask.await()
+        assertThat(result.isCompleted).isFalse()
+
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AF_STATE to CaptureResult
+                            .CONTROL_AF_STATE_FOCUSED_LOCKED,
+                        CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_LOCKED
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        assertThat(result3A.status).isEqualTo(Status3A.OK)
+
+        // There should be one request to monitor AF to finish it's scan.
+        requestProcessor.nextEvent()
+        // One request to lock AE
+        val request2 = requestProcessor.nextEvent().request
+        assertThat(request2!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+
+        // And one request to lock AF.
+        val request3 = requestProcessor.nextEvent().request
+        assertThat(request3!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER]).isEqualTo(
+            CaptureRequest.CONTROL_AF_TRIGGER_START
+        )
+        assertThat(request3.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+    }
+
+    @Test
+    fun testAfAfterNewScanScanAeImmediate(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val lock3AAsyncTask = GlobalScope.async {
+            controller3A.lock3A(
+                afLockBehavior = Lock3ABehavior.AFTER_NEW_SCAN,
+                aeLockBehavior = Lock3ABehavior.IMMEDIATE
+            )
+        }
+        assertThat(lock3AAsyncTask.isCompleted).isFalse()
+        GlobalScope.launch {
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(
+                        requestNumber = RequestNumber(1)
+                    )
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(101L),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(101L),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AF_STATE to
+                                CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED,
+                            CaptureResult.CONTROL_AE_STATE to
+                                CaptureResult.CONTROL_AE_STATE_CONVERGED
+                        )
+                    )
+                )
+                delay(FRAME_RATE_MS)
+            }
+        }
+
+        val result = lock3AAsyncTask.await()
+        assertThat(result.isCompleted).isFalse()
+
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AF_STATE to CaptureResult
+                            .CONTROL_AF_STATE_FOCUSED_LOCKED,
+                        CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_LOCKED
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        assertThat(result3A.status).isEqualTo(Status3A.OK)
+
+        // One request to cancel AF to start a new scan.
+        val request1 = requestProcessor.nextEvent().request
+        assertThat(request1!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER]).isEqualTo(
+            CaptureRequest.CONTROL_AF_TRIGGER_CANCEL
+        )
+        // There should be one request to monitor AF to finish it's scan.
+        requestProcessor.nextEvent()
+
+        // There should be one request to monitor lock AE.
+        val request2 = requestProcessor.nextEvent().request
+        assertThat(request2!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+
+        // And one request to lock AF.
+        val request3 = requestProcessor.nextEvent().request
+        assertThat(request3!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER]).isEqualTo(
+            CaptureRequest.CONTROL_AF_TRIGGER_START
+        )
+        assertThat(request3.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+    }
+
+    @Test
+    fun testAfAfterCurrentScanAeAfterCurrentScan(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val lock3AAsyncTask = GlobalScope.async {
+            controller3A.lock3A(
+                afLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
+                aeLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN
+            )
+        }
+        assertThat(lock3AAsyncTask.isCompleted).isFalse()
+        GlobalScope.launch {
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(
+                        requestNumber = RequestNumber(1)
+                    )
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(101L),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(101L),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AF_STATE to
+                                CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED,
+                            CaptureResult.CONTROL_AE_STATE to
+                                CaptureResult.CONTROL_AE_STATE_CONVERGED
+                        )
+                    )
+                )
+                delay(FRAME_RATE_MS)
+            }
+        }
+
+        val result = lock3AAsyncTask.await()
+        assertThat(result.isCompleted).isFalse()
+
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AF_STATE to CaptureResult
+                            .CONTROL_AF_STATE_FOCUSED_LOCKED,
+                        CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_LOCKED
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        assertThat(result3A.status).isEqualTo(Status3A.OK)
+
+        // There should be one request to monitor AF to finish it's scan.
+        requestProcessor.nextEvent()
+        // One request to lock AE
+        val request2 = requestProcessor.nextEvent().request
+        assertThat(request2!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+
+        // And one request to lock AF.
+        val request3 = requestProcessor.nextEvent().request
+        assertThat(request3!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER]).isEqualTo(
+            CaptureRequest.CONTROL_AF_TRIGGER_START
+        )
+        assertThat(request3.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+    }
+
+    @Test
+    fun testAfAfterNewScanScanAeAfterNewScan(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val lock3AAsyncTask = GlobalScope.async {
+            controller3A.lock3A(
+                afLockBehavior = Lock3ABehavior.AFTER_NEW_SCAN,
+                aeLockBehavior = Lock3ABehavior.AFTER_NEW_SCAN
+            )
+        }
+        assertThat(lock3AAsyncTask.isCompleted).isFalse()
+        GlobalScope.launch {
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(
+                        requestNumber = RequestNumber(1)
+                    )
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(101L),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(101L),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AF_STATE to
+                                CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED,
+                            CaptureResult.CONTROL_AE_STATE to
+                                CaptureResult.CONTROL_AE_STATE_CONVERGED
+                        )
+                    )
+                )
+                delay(FRAME_RATE_MS)
+            }
+        }
+
+        val result = lock3AAsyncTask.await()
+        assertThat(result.isCompleted).isFalse()
+
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AF_STATE to CaptureResult
+                            .CONTROL_AF_STATE_FOCUSED_LOCKED,
+                        CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_LOCKED
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        assertThat(result3A.status).isEqualTo(Status3A.OK)
+
+        // One request to cancel AF to start a new scan.
+        val request1 = requestProcessor.nextEvent().request
+        assertThat(request1!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER]).isEqualTo(
+            CaptureRequest.CONTROL_AF_TRIGGER_CANCEL
+        )
+        // There should be one request to unlock AE and monitor the current AF scan to finish.
+        val request2 = requestProcessor.nextEvent().request
+        assertThat(request2!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            false
+        )
+
+        // There should be one request to monitor lock AE.
+        val request3 = requestProcessor.nextEvent().request
+        assertThat(request3!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+
+        // And one request to lock AF.
+        val request4 = requestProcessor.nextEvent().request
+        assertThat(request4!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER]).isEqualTo(
+            CaptureRequest.CONTROL_AF_TRIGGER_START
+        )
+        assertThat(request4.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK]).isEqualTo(
+            true
+        )
+    }
+
+    private fun initGraphProcessor() {
+        graphProcessor.attach(requestProcessor)
+        graphProcessor.setRepeating(Request(streams = listOf(StreamId(1))))
+    }
+
+    companion object {
+        // The time duration in milliseconds between two frame results.
+        private const val FRAME_RATE_MS = 33L
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3ASubmit3ATest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3ASubmit3ATest.kt
index bf5ea3f..c87f5c0 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3ASubmit3ATest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3ASubmit3ATest.kt
@@ -45,9 +45,9 @@
 @RunWith(CameraPipeRobolectricTestRunner::class)
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class Controller3ASubmit3ATest {
-    private val requestProcessor = FakeRequestProcessor()
     private val graphProcessor = FakeGraphProcessor()
     private val graphState3A = GraphState3A()
+    private val requestProcessor = FakeRequestProcessor(graphState3A)
     private val listener3A = Listener3A()
     private val controller3A = Controller3A(graphProcessor, graphState3A, listener3A)
 
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3AUnlock3ATest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3AUnlock3ATest.kt
new file mode 100644
index 0000000..ad3d24c
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3AUnlock3ATest.kt
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.impl
+
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureResult
+import android.os.Build
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestNumber
+import androidx.camera.camera2.pipe.Status3A
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.testing.CameraPipeRobolectricTestRunner
+import androidx.camera.camera2.pipe.testing.FakeFrameMetadata
+import androidx.camera.camera2.pipe.testing.FakeGraphProcessor
+import androidx.camera.camera2.pipe.testing.FakeRequestMetadata
+import androidx.camera.camera2.pipe.testing.FakeRequestProcessor
+import com.google.common.truth.Truth
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+
+@RunWith(CameraPipeRobolectricTestRunner::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class Controller3AUnlock3ATest {
+    private val graphProcessor = FakeGraphProcessor()
+    private val graphState3A = GraphState3A()
+    private val requestProcessor = FakeRequestProcessor(graphState3A)
+    private val listener3A = Listener3A()
+    private val controller3A = Controller3A(graphProcessor, graphState3A, listener3A)
+
+    @Test
+    fun testUnlockAe(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val unLock3AAsyncTask = GlobalScope.async {
+            controller3A.unlock3A(ae = true)
+        }
+
+        // Launch a task to repeatedly invoke a given capture result.
+        GlobalScope.launch {
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(
+                        requestNumber = RequestNumber(1)
+                    )
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(101L),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(101L),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AE_STATE to
+                                CaptureResult.CONTROL_AE_STATE_LOCKED
+                        )
+                    )
+                )
+                delay(FRAME_RATE_MS)
+            }
+        }
+
+        val result = unLock3AAsyncTask.await()
+        // Result of unlock3A call shouldn't be complete yet since the AE is locked.
+        Truth.assertThat(result.isCompleted).isFalse()
+
+        // There should be one request to lock AE.
+        val request1 = requestProcessor.nextEvent().request
+        Truth.assertThat(request1!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK])
+            .isEqualTo(false)
+
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_SEARCHING
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        Truth.assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        Truth.assertThat(result3A.status).isEqualTo(Status3A.OK)
+    }
+
+    @Test
+    fun testUnlockAf(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val unLock3AAsyncTask = GlobalScope.async { controller3A.unlock3A(af = true) }
+
+        // Launch a task to repeatedly invoke a given capture result.
+        GlobalScope.launch {
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(
+                        requestNumber = RequestNumber(1)
+                    )
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(101L),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(101L),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AF_STATE to
+                                CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED
+                        )
+                    )
+                )
+                delay(FRAME_RATE_MS)
+            }
+        }
+
+        val result = unLock3AAsyncTask.await()
+        // Result of unlock3A call shouldn't be complete yet since the AF is locked.
+        Truth.assertThat(result.isCompleted).isFalse()
+
+        // There should be one request to unlock AF.
+        val request1 = requestProcessor.nextEvent().request
+        Truth.assertThat(request1!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER])
+            .isEqualTo(CaptureRequest.CONTROL_AF_TRIGGER_CANCEL)
+
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AF_STATE to CaptureResult.CONTROL_AF_STATE_INACTIVE
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        Truth.assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        Truth.assertThat(result3A.status).isEqualTo(Status3A.OK)
+    }
+
+    @Test
+    fun testUnlockAwb(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val unLock3AAsyncTask = GlobalScope.async {
+            controller3A.unlock3A(awb = true)
+        }
+
+        // Launch a task to repeatedly invoke a given capture result.
+        GlobalScope.launch {
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(
+                        requestNumber = RequestNumber(1)
+                    )
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(101L),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(101L),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AWB_STATE to
+                                CaptureResult.CONTROL_AWB_STATE_LOCKED
+                        )
+                    )
+                )
+                delay(FRAME_RATE_MS)
+            }
+        }
+
+        val result = unLock3AAsyncTask.await()
+        // Result of unlock3A call shouldn't be complete yet since the AWB is locked.
+        Truth.assertThat(result.isCompleted).isFalse()
+
+        // There should be one request to lock AWB.
+        val request1 = requestProcessor.nextEvent().request
+        Truth.assertThat(request1!!.extraRequestParameters[CaptureRequest.CONTROL_AWB_LOCK])
+            .isEqualTo(false)
+
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AWB_STATE to CaptureResult.CONTROL_AWB_STATE_SEARCHING
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        Truth.assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        Truth.assertThat(result3A.status).isEqualTo(Status3A.OK)
+    }
+
+    @Test
+    fun testUnlockAeAf(): Unit = runBlocking {
+        initGraphProcessor()
+
+        val unLock3AAsyncTask = GlobalScope.async { controller3A.unlock3A(ae = true, af = true) }
+
+        // Launch a task to repeatedly invoke a given capture result.
+        GlobalScope.launch {
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(
+                        requestNumber = RequestNumber(1)
+                    )
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(101L),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(101L),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_LOCKED,
+                            CaptureResult.CONTROL_AF_STATE to
+                                CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED
+                        )
+                    )
+                )
+                delay(FRAME_RATE_MS)
+            }
+        }
+
+        val result = unLock3AAsyncTask.await()
+        // Result of unlock3A call shouldn't be complete yet since the AF is locked.
+        Truth.assertThat(result.isCompleted).isFalse()
+
+        // There should be one request to unlock AF.
+        val request1 = requestProcessor.nextEvent().request
+        Truth.assertThat(request1!!.extraRequestParameters[CaptureRequest.CONTROL_AF_TRIGGER])
+            .isEqualTo(CaptureRequest.CONTROL_AF_TRIGGER_CANCEL)
+        // Then request to unlock AE.
+        val request2 = requestProcessor.nextEvent().request
+        Truth.assertThat(request2!!.extraRequestParameters[CaptureRequest.CONTROL_AE_LOCK])
+            .isEqualTo(false)
+
+        GlobalScope.launch {
+            listener3A.onRequestSequenceCreated(
+                FakeRequestMetadata(
+                    requestNumber = RequestNumber(1)
+                )
+            )
+            listener3A.onPartialCaptureResult(
+                FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                FrameNumber(101L),
+                FakeFrameMetadata(
+                    frameNumber = FrameNumber(101L),
+                    resultMetadata = mapOf(
+                        CaptureResult.CONTROL_AF_STATE to CaptureResult.CONTROL_AF_STATE_INACTIVE,
+                        CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_SEARCHING
+                    )
+                )
+            )
+        }
+
+        val result3A = result.await()
+        Truth.assertThat(result3A.frameNumber.value).isEqualTo(101L)
+        Truth.assertThat(result3A.status).isEqualTo(Status3A.OK)
+    }
+
+    private fun initGraphProcessor() {
+        graphProcessor.attach(requestProcessor)
+        graphProcessor.setRepeating(Request(streams = listOf(StreamId(1))))
+    }
+
+    companion object {
+        // The time duration in milliseconds between two frame results.
+        private const val FRAME_RATE_MS = 33L
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3AUpdate3ATest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3AUpdate3ATest.kt
index 833e885..48b00fa 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3AUpdate3ATest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Controller3AUpdate3ATest.kt
@@ -24,12 +24,15 @@
 import androidx.camera.camera2.pipe.AfMode
 import androidx.camera.camera2.pipe.AwbMode
 import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.Request
 import androidx.camera.camera2.pipe.RequestNumber
 import androidx.camera.camera2.pipe.Status3A
+import androidx.camera.camera2.pipe.StreamId
 import androidx.camera.camera2.pipe.testing.CameraPipeRobolectricTestRunner
 import androidx.camera.camera2.pipe.testing.FakeFrameMetadata
 import androidx.camera.camera2.pipe.testing.FakeGraphProcessor
 import androidx.camera.camera2.pipe.testing.FakeRequestMetadata
+import androidx.camera.camera2.pipe.testing.FakeRequestProcessor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,11 +48,14 @@
 class Controller3AUpdate3ATest {
     private val graphProcessor = FakeGraphProcessor()
     private val graphState3A = GraphState3A()
+    private val requestProcessor = FakeRequestProcessor(graphState3A)
     private val listener3A = Listener3A()
     private val controller3A = Controller3A(graphProcessor, graphState3A, listener3A)
 
     @Test
     fun testUpdate3AUpdatesState3A() {
+        initGraphProcessor()
+
         val result = controller3A.update3A(afMode = AfMode.OFF)
         assertThat(graphState3A.readState()[CaptureRequest.CONTROL_AF_MODE]).isEqualTo(
             CaptureRequest.CONTROL_AE_MODE_OFF
@@ -60,6 +66,8 @@
     @ExperimentalCoroutinesApi
     @Test
     fun testUpdate3ACancelsPreviousInProgressUpdate() {
+        initGraphProcessor()
+
         val result = controller3A.update3A(afMode = AfMode.OFF)
         // Invoking update3A before the previous one is complete will cancel the result of the
         // previous call.
@@ -69,6 +77,8 @@
 
     @Test
     fun testAfModeUpdate(): Unit = runBlocking {
+        initGraphProcessor()
+
         val result = controller3A.update3A(afMode = AfMode.OFF)
         GlobalScope.launch {
             listener3A.onRequestSequenceCreated(
@@ -94,6 +104,8 @@
 
     @Test
     fun testAeModeUpdate(): Unit = runBlocking {
+        initGraphProcessor()
+
         val result = controller3A.update3A(aeMode = AeMode.ON_ALWAYS_FLASH)
         GlobalScope.launch {
             listener3A.onRequestSequenceCreated(
@@ -120,6 +132,8 @@
 
     @Test
     fun testAwbModeUpdate(): Unit = runBlocking {
+        initGraphProcessor()
+
         val result = controller3A.update3A(awbMode = AwbMode.CLOUDY_DAYLIGHT)
         GlobalScope.launch {
             listener3A.onRequestSequenceCreated(
@@ -146,6 +160,8 @@
 
     @Test
     fun testAfRegionsUpdate(): Unit = runBlocking {
+        initGraphProcessor()
+
         val result = controller3A.update3A(afRegions = listOf(MeteringRectangle(1, 1, 100, 100, 2)))
         GlobalScope.launch {
             listener3A.onRequestSequenceCreated(
@@ -172,6 +188,8 @@
 
     @Test
     fun testAeRegionsUpdate(): Unit = runBlocking {
+        initGraphProcessor()
+
         val result = controller3A.update3A(aeRegions = listOf(MeteringRectangle(1, 1, 100, 100, 2)))
         GlobalScope.launch {
             listener3A.onRequestSequenceCreated(
@@ -198,6 +216,8 @@
 
     @Test
     fun testAwbRegionsUpdate(): Unit = runBlocking {
+        initGraphProcessor()
+
         val result = controller3A.update3A(
             awbRegions = listOf(
                 MeteringRectangle(1, 1, 100, 100, 2)
@@ -225,4 +245,9 @@
         assertThat(result3A.frameNumber.value).isEqualTo(101L)
         assertThat(result3A.status).isEqualTo(Status3A.OK)
     }
+
+    private fun initGraphProcessor() {
+        graphProcessor.attach(requestProcessor)
+        graphProcessor.setRepeating(Request(streams = listOf(StreamId(1))))
+    }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/GraphProcessorTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/GraphProcessorTest.kt
index abdc307..23c63d1 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/GraphProcessorTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/GraphProcessorTest.kt
@@ -36,8 +36,8 @@
 class GraphProcessorTest {
     private val globalListener = FakeRequestListener()
 
-    private val fakeProcessor1 = FakeRequestProcessor()
-    private val fakeProcessor2 = FakeRequestProcessor()
+    private val fakeProcessor1 = FakeRequestProcessor(GraphState3A())
+    private val fakeProcessor2 = FakeRequestProcessor(GraphState3A())
 
     private val requestListener1 = FakeRequestListener()
     private val request1 = Request(listOf(StreamId(0)), listeners = listOf(requestListener1))
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/SessionFactoryTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/SessionFactoryTest.kt
index 730a1dd..f8007e9 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/SessionFactoryTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/SessionFactoryTest.kt
@@ -105,7 +105,7 @@
             virtualSessionState = VirtualSessionState(
                 FakeGraphProcessor(),
                 sessionFactory,
-                FakeRequestProcessor(),
+                FakeRequestProcessor(GraphState3A()),
                 this
             )
         )
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeGraphProcessor.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeGraphProcessor.kt
index 139f0b0..5ab64bc 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeGraphProcessor.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeGraphProcessor.kt
@@ -93,5 +93,6 @@
     }
 
     override fun invalidate() {
+        processor!!.setRepeating(repeatingRequest!!, mapOf(), false)
     }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeRequestProcessor.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeRequestProcessor.kt
index f7e3bed..0f446d5 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeRequestProcessor.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeRequestProcessor.kt
@@ -20,6 +20,7 @@
 import android.view.Surface
 import androidx.camera.camera2.pipe.Request
 import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.impl.GraphState3A
 import androidx.camera.camera2.pipe.impl.RequestProcessor
 import androidx.camera.camera2.pipe.impl.TokenLock
 import androidx.camera.camera2.pipe.impl.TokenLockImpl
@@ -30,7 +31,8 @@
 /**
  * Fake implementation of a [RequestProcessor] for tests.
  */
-class FakeRequestProcessor : RequestProcessor, RequestProcessor.Factory {
+class FakeRequestProcessor(private val graphState3A: GraphState3A) :
+    RequestProcessor, RequestProcessor.Factory {
     private val eventChannel = Channel<Event>(Channel.UNLIMITED)
 
     val requestQueue: MutableList<FakeRequest> = mutableListOf()
@@ -71,7 +73,7 @@
         requireSurfacesForAllStreams: Boolean
     ): Boolean {
         val fakeRequest =
-            FakeRequest(listOf(request), extraRequestParameters, requireSurfacesForAllStreams)
+            createFakeRequest(listOf(request), extraRequestParameters, requireSurfacesForAllStreams)
 
         if (rejectRequests || closeInvoked) {
             check(eventChannel.offer(Event(request = fakeRequest, rejected = true)))
@@ -90,7 +92,7 @@
         requireSurfacesForAllStreams: Boolean
     ): Boolean {
         val fakeRequest =
-            FakeRequest(requests, extraRequestParameters, requireSurfacesForAllStreams)
+            createFakeRequest(requests, extraRequestParameters, requireSurfacesForAllStreams)
         if (rejectRequests || closeInvoked) {
             check(eventChannel.offer(Event(request = fakeRequest, rejected = true)))
             return false
@@ -108,7 +110,7 @@
         requireSurfacesForAllStreams: Boolean
     ): Boolean {
         val fakeRequest =
-            FakeRequest(listOf(request), extraRequestParameters, requireSurfacesForAllStreams)
+            createFakeRequest(listOf(request), extraRequestParameters, requireSurfacesForAllStreams)
         if (rejectRequests || closeInvoked) {
             check(eventChannel.offer(Event(request = fakeRequest, rejected = true)))
             return false
@@ -137,9 +139,20 @@
     /**
      * Get the next event from queue with an option to specify a timeout for tests.
      */
-    suspend fun nextEvent(timeMillis: Long = 100): Event = withTimeout(timeMillis) {
+    suspend fun nextEvent(timeMillis: Long = 500): Event = withTimeout(timeMillis) {
         eventChannel.receive()
     }
+
+    private fun createFakeRequest(
+        burst: List<Request>,
+        extraRequestParameters: Map<CaptureRequest.Key<*>, Any>,
+        requireStreams: Boolean
+    ): FakeRequest {
+        val parameterMap = mutableMapOf<CaptureRequest.Key<*>, Any>()
+        parameterMap.putAll(graphState3A.readState())
+        parameterMap.putAll(extraRequestParameters)
+        return FakeRequest(burst, parameterMap, requireStreams)
+    }
 }
 
 data class Event(
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 33822d5..7b7671f 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -64,6 +64,7 @@
     androidTestImplementation(project(":annotation:annotation-experimental"))
     androidTestImplementation(project(":internal-testutils-truth"))
     androidTestImplementation("org.jetbrains.kotlinx:atomicfu:0.13.1")
+    androidTestImplementation("androidx.exifinterface:exifinterface:1.0.0")
 }
 android {
     defaultConfig {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraDisconnectTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraDisconnectTest.java
index 50460c4..25ce33e 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraDisconnectTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraDisconnectTest.java
@@ -40,7 +40,6 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.After;
 import org.junit.Before;
@@ -60,12 +59,14 @@
     @Rule
     public TestRule mCameraRule = CameraUtil.grantCameraPermissionAndPreTest();
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<CameraXTestActivity> mCameraXTestActivityRule =
-            new ActivityTestRule<>(CameraXTestActivity.class, true, false);
+    public androidx.test.rule.ActivityTestRule<CameraXTestActivity> mCameraXTestActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(CameraXTestActivity.class, true, false);
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<Camera2TestActivity> mCamera2ActivityRule =
-            new ActivityTestRule<>(Camera2TestActivity.class, true, false);
+    public androidx.test.rule.ActivityTestRule<Camera2TestActivity> mCamera2ActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(Camera2TestActivity.class, true, false);
 
     private CameraXTestActivity mCameraXTestActivity;
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
index 85fac6e..2da8952 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
@@ -50,7 +50,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.interop.Camera2Interop;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.AspectRatio;
@@ -145,7 +145,7 @@
     }
 
     @Before
-    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
     public void setUp() throws ExecutionException, InterruptedException {
         createDefaultPictureFolderIfNotExist();
         mContext = ApplicationProvider.getApplicationContext();
@@ -610,7 +610,7 @@
     }
 
     @Test
-    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
     public void camera2InteropCaptureSessionCallbacks() {
         ImageCapture.Builder builder = new ImageCapture.Builder();
         CameraCaptureSession.CaptureCallback captureCallback =
@@ -825,6 +825,7 @@
                 ImageCapture.ERROR_INVALID_CAMERA);
     }
 
+    @SuppressWarnings("deprecation")
     private void createDefaultPictureFolderIfNotExist() {
         File pictureFolder = Environment.getExternalStoragePublicDirectory(
                 Environment.DIRECTORY_PICTURES);
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraCaptureResultTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraCaptureResultTest.java
index ef5af22..676a492 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraCaptureResultTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraCaptureResultTest.java
@@ -16,11 +16,16 @@
 
 package androidx.camera.camera2.internal;
 
+import static androidx.exifinterface.media.ExifInterface.FLAG_FLASH_FIRED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.when;
 
+import android.graphics.Rect;
+import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CaptureResult;
+import android.os.Build;
 
 import androidx.camera.core.impl.CameraCaptureMetaData.AeState;
 import androidx.camera.core.impl.CameraCaptureMetaData.AfMode;
@@ -28,6 +33,8 @@
 import androidx.camera.core.impl.CameraCaptureMetaData.AwbState;
 import androidx.camera.core.impl.CameraCaptureMetaData.FlashState;
 import androidx.camera.core.impl.TagBundle;
+import androidx.camera.core.impl.utils.ExifData;
+import androidx.exifinterface.media.ExifInterface;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -36,13 +43,15 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
+import java.util.concurrent.TimeUnit;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public final class Camera2CameraCaptureResultTest {
 
     private CaptureResult mCaptureResult;
     private Camera2CameraCaptureResult mCamera2CameraCaptureResult;
-    private TagBundle mTag = TagBundle.emptyBundle();
+    private final TagBundle mTag = TagBundle.emptyBundle();
 
     @Before
     public void setUp() {
@@ -275,4 +284,78 @@
                 .thenReturn(CaptureResult.FLASH_STATE_PARTIAL);
         assertThat(mCamera2CameraCaptureResult.getFlashState()).isEqualTo(FlashState.FIRED);
     }
+
+    @Test
+    public void canPopulateExif() {
+        // Arrange
+        when(mCaptureResult.get(CaptureResult.FLASH_STATE))
+                .thenReturn(CaptureResult.FLASH_STATE_FIRED);
+
+        Rect cropRegion = new Rect(0, 0, 640, 480);
+        when(mCaptureResult.get(CaptureResult.SCALER_CROP_REGION)).thenReturn(cropRegion);
+
+        when(mCaptureResult.get(CaptureResult.JPEG_ORIENTATION)).thenReturn(270);
+
+        long exposureTime = TimeUnit.SECONDS.toNanos(5);
+        when(mCaptureResult.get(CaptureResult.SENSOR_EXPOSURE_TIME)).thenReturn(exposureTime);
+
+        float aperture = 1.8f;
+        when(mCaptureResult.get(CaptureResult.LENS_APERTURE)).thenReturn(aperture);
+
+        int iso = 200;
+        int postRawSensitivityBoost = 100; // No boost for API < 24
+        when(mCaptureResult.get(CaptureResult.SENSOR_SENSITIVITY)).thenReturn(iso);
+        if (Build.VERSION.SDK_INT >= 24) {
+            // Add boost for API >= 24
+            postRawSensitivityBoost = 200;
+            when(mCaptureResult.get(CaptureResult.CONTROL_POST_RAW_SENSITIVITY_BOOST))
+                    .thenReturn(postRawSensitivityBoost);
+        }
+
+        float focalLength = 4200f;
+        when(mCaptureResult.get(CaptureResult.LENS_FOCAL_LENGTH)).thenReturn(focalLength);
+
+        when(mCaptureResult.get(CaptureResult.CONTROL_AWB_MODE))
+                .thenReturn(CameraMetadata.CONTROL_AWB_MODE_OFF);
+
+        // Act
+        ExifData.Builder exifBuilder = ExifData.builderForDevice();
+        mCamera2CameraCaptureResult.populateExifData(exifBuilder);
+        ExifData exifData = exifBuilder.build();
+
+        // Assert
+        assertThat(Short.parseShort(exifData.getAttribute(ExifInterface.TAG_FLASH)))
+                .isEqualTo(FLAG_FLASH_FIRED);
+
+        assertThat(exifData.getAttribute(ExifInterface.TAG_IMAGE_WIDTH))
+                .isEqualTo(String.valueOf(cropRegion.width()));
+
+        assertThat(exifData.getAttribute(ExifInterface.TAG_IMAGE_LENGTH))
+                .isEqualTo(String.valueOf(cropRegion.height()));
+
+        assertThat(exifData.getAttribute(ExifInterface.TAG_ORIENTATION))
+                .isEqualTo(String.valueOf(ExifInterface.ORIENTATION_ROTATE_270));
+
+        String exposureTimeString = exifData.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
+        assertThat(exposureTimeString).isNotNull();
+        assertThat(Float.parseFloat(exposureTimeString)).isWithin(0.1f)
+                .of(TimeUnit.NANOSECONDS.toSeconds(exposureTime));
+
+        assertThat(exifData.getAttribute(ExifInterface.TAG_F_NUMBER))
+                .isEqualTo(String.valueOf(aperture));
+
+        assertThat(
+                Short.parseShort(exifData.getAttribute(ExifInterface.TAG_PHOTOGRAPHIC_SENSITIVITY)))
+                .isEqualTo((short) (iso * (int) (postRawSensitivityBoost / 100f)));
+
+        String focalLengthString = exifData.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
+        assertThat(focalLengthString).isNotNull();
+        String[] fractionValues = focalLengthString.split("/");
+        long numerator = Long.parseLong(fractionValues[0]);
+        long denominator = Long.parseLong(fractionValues[1]);
+        assertThat(numerator / (float) denominator).isWithin(0.1f).of(focalLength);
+
+        assertThat(Short.parseShort(exifData.getAttribute(ExifInterface.TAG_WHITE_BALANCE)))
+                .isEqualTo(ExifInterface.WHITE_BALANCE_MANUAL);
+    }
 }
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplCameraReopenTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplCameraReopenTest.java
new file mode 100644
index 0000000..7dd2b91
--- /dev/null
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplCameraReopenTest.java
@@ -0,0 +1,322 @@
+/*
+ * 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.internal;
+
+import static androidx.camera.camera2.internal.Camera2CameraImpl.StateCallback.CameraReopenMonitor.REOPEN_LIMIT_MS;
+import static androidx.camera.camera2.internal.Camera2CameraImpl.StateCallback.REOPEN_DELAY_MS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeFalse;
+
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
+import androidx.camera.camera2.internal.compat.CameraManagerCompat;
+import androidx.camera.camera2.internal.compat.CameraManagerCompat.CameraManagerCompatImpl;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.CameraUnavailableException;
+import androidx.camera.core.Logger;
+import androidx.camera.core.impl.CameraInternal;
+import androidx.camera.core.impl.CameraStateRegistry;
+import androidx.camera.core.impl.Observable;
+import androidx.camera.core.impl.utils.MainThreadAsyncHandler;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.camera.testing.CameraUtil;
+import androidx.core.os.HandlerCompat;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/** Contains {@link Camera2CameraImpl} tests for reopening the camera with failures. */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public final class Camera2CameraImplCameraReopenTest {
+
+    @Rule
+    public TestRule mCameraRule = CameraUtil.grantCameraPermissionAndPreTest();
+
+    private static HandlerThread sCameraHandlerThread;
+    private static Handler sCameraHandler;
+    private static ExecutorService sCameraExecutor;
+    private Camera2CameraImpl mCamera2CameraImpl;
+
+    @BeforeClass
+    public static void classSetup() {
+        sCameraHandlerThread = new HandlerThread("cameraThread");
+        sCameraHandlerThread.start();
+        sCameraHandler = HandlerCompat.createAsync(sCameraHandlerThread.getLooper());
+        sCameraExecutor = CameraXExecutors.newHandlerExecutor(sCameraHandler);
+    }
+
+    @AfterClass
+    public static void classTeardown() {
+        sCameraHandlerThread.quitSafely();
+    }
+
+    @After
+    public void testTeardown() throws InterruptedException, ExecutionException {
+        // Release camera, otherwise the CameraDevice is not closed, which can cause problems that
+        // interfere with other tests.
+        if (mCamera2CameraImpl != null) {
+            mCamera2CameraImpl.release().get();
+            mCamera2CameraImpl = null;
+        }
+    }
+
+    @Test
+    public void openCameraAfterMultipleFailures_whenCameraReopenLimitNotReached() throws Exception {
+        // Set up the camera
+        final FailCameraOpenCameraManagerImpl cameraManagerImpl =
+                new FailCameraOpenCameraManagerImpl();
+        setUpCamera(cameraManagerImpl);
+
+        // Set up camera open attempt listener
+        final Semaphore cameraOpenSemaphore = new Semaphore(0);
+        cameraManagerImpl.setOnCameraOpenAttemptListener(cameraOpenSemaphore::release);
+
+        // Try opening the camera. This will fail and trigger reopening attempts
+        mCamera2CameraImpl.open();
+        assertThat(cameraOpenSemaphore.tryAcquire(1, 1_000, TimeUnit.MILLISECONDS)).isTrue();
+
+        // Wait for half the max reopen attempts to occur
+        assertThat(cameraOpenSemaphore.tryAcquire(getMaxCameraReopenAttempts() / 2, REOPEN_LIMIT_MS,
+                TimeUnit.MILLISECONDS)).isTrue();
+
+        // Allow camera opening to succeed
+        cameraManagerImpl.setShouldFailCameraOpen(false);
+
+        // Verify the camera opens
+        awaitCameraOpen();
+    }
+
+    @Test
+    public void doNotAttemptCameraReopen_whenCameraReopenLimitReached() throws Exception {
+        // Set up the camera
+        final FailCameraOpenCameraManagerImpl cameraManagerImpl =
+                new FailCameraOpenCameraManagerImpl();
+        setUpCamera(cameraManagerImpl);
+
+        // Set up camera open attempt listener
+        final Semaphore cameraOpenSemaphore = new Semaphore(0);
+        cameraManagerImpl.setOnCameraOpenAttemptListener(cameraOpenSemaphore::release);
+
+        // Try opening the camera. This will fail and trigger reopening attempts
+        mCamera2CameraImpl.open();
+        assertThat(cameraOpenSemaphore.tryAcquire(1, 1_000, TimeUnit.MILLISECONDS)).isTrue();
+
+        // Wait for approximately max reopen attempts to occur. The exact max number of reopen
+        // attempts may not occur, this is due to impreciseness of the camera reopen scheduling.
+        cameraOpenSemaphore.tryAcquire(getMaxCameraReopenAttempts(), REOPEN_LIMIT_MS,
+                TimeUnit.MILLISECONDS);
+        cameraOpenSemaphore.drainPermits();
+
+        // Verify 0 or at most 1 camera reopen occurred. There may be 1 additional reopen attempt
+        // if a last reopen attempt was scheduled right before the reopen attempt limit time was
+        // reached.
+        assertThat(cameraOpenSemaphore.tryAcquire(2, 2_000, TimeUnit.MILLISECONDS)).isFalse();
+    }
+
+    @Test
+    public void openCameraAfterExplicitRequest_whenCameraReopenLimitReached() throws Exception {
+        // Set up the camera
+        final FailCameraOpenCameraManagerImpl cameraManagerImpl =
+                new FailCameraOpenCameraManagerImpl();
+        setUpCamera(cameraManagerImpl);
+
+        // Set up camera open attempt listener
+        final Semaphore cameraOpenSemaphore = new Semaphore(0);
+        cameraManagerImpl.setOnCameraOpenAttemptListener(cameraOpenSemaphore::release);
+
+        // Try opening the camera. This will fail and trigger reopening attempts
+        mCamera2CameraImpl.open();
+        assertThat(cameraOpenSemaphore.tryAcquire(1, 1_000, TimeUnit.MILLISECONDS)).isTrue();
+
+        // Wait for approximately max reopen attempts to occur. The exact max number of reopen
+        // attempts may not occur, this is due to impreciseness of the camera reopen scheduling.
+        cameraOpenSemaphore.tryAcquire(getMaxCameraReopenAttempts(), REOPEN_LIMIT_MS,
+                TimeUnit.MILLISECONDS);
+
+        // Allow camera opening to succeed
+        cameraManagerImpl.setShouldFailCameraOpen(false);
+
+        // Try opening the camera. This should succeed
+        mCamera2CameraImpl.open();
+
+        // Verify the camera opens
+        awaitCameraOpen();
+    }
+
+    private void setUpCamera(@NonNull final FailCameraOpenCameraManagerImpl cameraManagerImpl)
+            throws CameraAccessExceptionCompat, CameraUnavailableException {
+        // Get camera id of camera to open
+        final String cameraId = CameraUtil.getCameraIdWithLensFacing(
+                CameraSelector.LENS_FACING_BACK);
+        assumeFalse("Device doesn't have a back facing camera", cameraId == null);
+
+        // Build camera manager wrapper
+        final CameraManagerCompat cameraManagerCompat = CameraManagerCompat.from(cameraManagerImpl);
+
+        // Build camera info
+        final Camera2CameraInfoImpl camera2CameraInfo = new Camera2CameraInfoImpl(cameraId,
+                cameraManagerCompat.getCameraCharacteristicsCompat(cameraId));
+
+        // Initialize camera instance
+        mCamera2CameraImpl = new Camera2CameraImpl(cameraManagerCompat, cameraId,
+                camera2CameraInfo, new CameraStateRegistry(1), sCameraExecutor, sCameraHandler);
+    }
+
+    @SuppressWarnings("SameParameterValue")
+    private void awaitCameraOpen() throws InterruptedException {
+        final Semaphore cameraOpenSemaphore = new Semaphore(0);
+        final Observable.Observer<CameraInternal.State> observer =
+                new Observable.Observer<CameraInternal.State>() {
+                    @Override
+                    public void onNewData(@Nullable CameraInternal.State state) {
+                        if (state == CameraInternal.State.OPEN) {
+                            cameraOpenSemaphore.release();
+                        }
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable t) {
+                        Logger.e("CameraReopenTest", "Camera state error: " + t.getMessage());
+                    }
+                };
+        mCamera2CameraImpl.getCameraState().addObserver(CameraXExecutors.directExecutor(),
+                observer);
+
+        try {
+            assertThat(cameraOpenSemaphore.tryAcquire(2_000, TimeUnit.MILLISECONDS)).isTrue();
+        } finally {
+            mCamera2CameraImpl.getCameraState().removeObserver(observer);
+        }
+    }
+
+    private int getMaxCameraReopenAttempts() {
+        return (int) Math.ceil(REOPEN_LIMIT_MS / (double) REOPEN_DELAY_MS);
+    }
+
+    /**
+     * Wraps a {@link CameraManagerCompatImpl} instance and controls camera opening by either
+     * allowing it to succeed or fail.
+     */
+    static class FailCameraOpenCameraManagerImpl implements CameraManagerCompatImpl {
+
+        @NonNull
+        private final CameraManagerCompatImpl mForwardCameraManagerCompatImpl;
+        @NonNull
+        private final Object mLock = new Object();
+        @GuardedBy("mLock")
+        @Nullable
+        private OnCameraOpenAttemptListener mOnCameraOpenAttemptListener;
+        @GuardedBy("mLock")
+        private boolean mShouldFailCameraOpen = true;
+
+        FailCameraOpenCameraManagerImpl() {
+            mForwardCameraManagerCompatImpl = CameraManagerCompatImpl.from(
+                    ApplicationProvider.getApplicationContext(),
+                    MainThreadAsyncHandler.getInstance());
+        }
+
+        @NonNull
+        @Override
+        public String[] getCameraIdList() throws CameraAccessExceptionCompat {
+            return mForwardCameraManagerCompatImpl.getCameraIdList();
+        }
+
+        @Override
+        public void registerAvailabilityCallback(@NonNull Executor executor,
+                @NonNull CameraManager.AvailabilityCallback callback) {
+            mForwardCameraManagerCompatImpl.registerAvailabilityCallback(executor, callback);
+        }
+
+        @Override
+        public void unregisterAvailabilityCallback(
+                @NonNull CameraManager.AvailabilityCallback callback) {
+            mForwardCameraManagerCompatImpl.unregisterAvailabilityCallback(callback);
+        }
+
+        @NonNull
+        @Override
+        public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
+                throws CameraAccessExceptionCompat {
+            return mForwardCameraManagerCompatImpl.getCameraCharacteristics(cameraId);
+        }
+
+        @NonNull
+        @Override
+        public CameraManager getCameraManager() {
+            return mForwardCameraManagerCompatImpl.getCameraManager();
+        }
+
+        @Override
+        public void openCamera(@NonNull String cameraId, @NonNull Executor executor,
+                @NonNull CameraDevice.StateCallback callback) throws CameraAccessExceptionCompat {
+            synchronized (mLock) {
+                if (mOnCameraOpenAttemptListener != null) {
+                    mOnCameraOpenAttemptListener.onCameraOpenAttempt();
+                }
+                if (mShouldFailCameraOpen) {
+                    // Throw any exception
+                    throw new SecurityException("Lacking privileges");
+                } else {
+                    mForwardCameraManagerCompatImpl.openCamera(cameraId, executor, callback);
+                }
+            }
+        }
+
+        public void setShouldFailCameraOpen(boolean shouldFailCameraOpen) {
+            synchronized (mLock) {
+                this.mShouldFailCameraOpen = shouldFailCameraOpen;
+            }
+        }
+
+        public void setOnCameraOpenAttemptListener(
+                @NonNull final OnCameraOpenAttemptListener listener) {
+            synchronized (mLock) {
+                this.mOnCameraOpenAttemptListener = listener;
+            }
+        }
+
+        interface OnCameraOpenAttemptListener {
+            /** Triggered whenever an attempt to open the camera is made. */
+            void onCameraOpenAttempt();
+        }
+    }
+}
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java
index de75e09..db9ad4c 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.internal.verification.VerificationModeFactory.times;
 
+import android.content.Context;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraDevice;
 import android.media.ImageReader;
@@ -147,7 +148,7 @@
         mSemaphore = new Semaphore(0);
         mCameraStateRegistry = new CameraStateRegistry(DEFAULT_AVAILABLE_CAMERA_COUNT);
         CameraManagerCompat cameraManagerCompat =
-                CameraManagerCompat.from(ApplicationProvider.getApplicationContext());
+                CameraManagerCompat.from((Context) ApplicationProvider.getApplicationContext());
         Camera2CameraInfoImpl camera2CameraInfo = new Camera2CameraInfoImpl(
                 mCameraId, cameraManagerCompat.getCameraCharacteristicsCompat(mCameraId));
         mCamera2CameraImpl = new Camera2CameraImpl(cameraManagerCompat, mCameraId,
@@ -551,6 +552,7 @@
         assertThat(currentState).isEqualTo(CameraInternal.State.RELEASED);
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void openNewCaptureSessionImmediateBeforePreviousCaptureSessionClosed()
             throws InterruptedException {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
index 0cf4896..30a066d 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
@@ -35,7 +35,7 @@
 import android.hardware.camera2.TotalCaptureResult;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.Camera2Config;
 import androidx.camera.camera2.internal.util.SemaphoreReleasingCamera2Callbacks;
 import androidx.camera.camera2.internal.util.SemaphoreReleasingCamera2Callbacks.DeviceStateCallback;
@@ -81,7 +81,7 @@
 @FlakyTest
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+@OptIn(markerClass = ExperimentalCamera2Interop.class)
 public final class Camera2ImplCameraXTest {
     @CameraSelector.LensFacing
     private static final int DEFAULT_LENS_FACING = CameraSelector.LENS_FACING_BACK;
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
index fc4877c..20e8154 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
@@ -43,7 +43,6 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.media.ImageReader.OnImageAvailableListener;
-import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -103,6 +102,7 @@
  * android.hardware.camera2.CameraDevice} can be opened since it is used to open a {@link
  * android.hardware.camera2.CaptureRequest}.
  */
+@SuppressWarnings("unchecked")
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public final class CaptureSessionTest {
@@ -745,7 +745,8 @@
         ArgumentCaptor<Throwable> throwableCaptor = ArgumentCaptor.forClass(Throwable.class);
         ListenableFuture<Void> openingFuture = captureSession.open(mTestParameters0.mSessionConfig,
                 mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build());
-        Futures.addCallback(openingFuture, mockFutureCallback, AsyncTask.THREAD_POOL_EXECUTOR);
+        Futures.addCallback(openingFuture, mockFutureCallback,
+                android.os.AsyncTask.THREAD_POOL_EXECUTOR);
         openingFuture.cancel(true);
 
         // The captureSession opening should callback onFailure with a CancellationException.
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java
index a053541..e84383d 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+import android.content.Context;
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraDevice;
@@ -40,7 +41,7 @@
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.internal.compat.CameraManagerCompat;
 import androidx.camera.camera2.internal.util.SemaphoreReleasingCamera2Callbacks;
 import androidx.camera.camera2.interop.Camera2Interop;
@@ -93,7 +94,7 @@
  */
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-@UseExperimental(markerClass = ExperimentalExposureCompensation.class)
+@OptIn(markerClass = ExperimentalExposureCompensation.class)
 public class ExposureDeviceTest {
 
     @CameraSelector.LensFacing
@@ -147,11 +148,12 @@
         mSemaphore = new Semaphore(0);
         mCameraStateRegistry = new CameraStateRegistry(DEFAULT_AVAILABLE_CAMERA_COUNT);
         CameraManagerCompat cameraManagerCompat =
-                CameraManagerCompat.from(ApplicationProvider.getApplicationContext());
+                CameraManagerCompat.from((Context) ApplicationProvider.getApplicationContext());
         Camera2CameraInfoImpl camera2CameraInfo = new Camera2CameraInfoImpl(
                 mCameraId, cameraManagerCompat.getCameraCharacteristicsCompat(mCameraId));
         mCamera2CameraImpl = new Camera2CameraImpl(
-                CameraManagerCompat.from(ApplicationProvider.getApplicationContext()), mCameraId,
+                CameraManagerCompat.from((Context) ApplicationProvider.getApplicationContext()),
+                mCameraId,
                 camera2CameraInfo,
                 mCameraStateRegistry, sCameraExecutor, sCameraHandler);
 
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
index 65f54a8..fc5b274 100644
--- 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
@@ -38,7 +38,7 @@
 import android.hardware.camera2.params.MeteringRectangle;
 import android.util.Range;
 
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.Camera2Config;
 import androidx.camera.camera2.internal.Camera2CameraControlImpl;
 import androidx.camera.core.CameraSelector;
@@ -70,7 +70,7 @@
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+@OptIn(markerClass = ExperimentalCamera2Interop.class)
 public final class Camera2CameraControlDeviceTest {
     private static final Range<Integer> FAKE_RANGE = new Range<>(0, 30);
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2InteropDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2InteropDeviceTest.java
index 074e01d..38c3594 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2InteropDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2InteropDeviceTest.java
@@ -36,7 +36,7 @@
 import android.hardware.camera2.params.MeteringRectangle;
 import android.util.Range;
 
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.Camera2Config;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraX;
@@ -65,7 +65,7 @@
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+@OptIn(markerClass = ExperimentalCamera2Interop.class)
 public final class Camera2InteropDeviceTest {
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private CameraSelector mCameraSelector;
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraCaptureResult.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraCaptureResult.java
index c4a51cc..928d8ef 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraCaptureResult.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraCaptureResult.java
@@ -16,7 +16,10 @@
 
 package androidx.camera.camera2.internal;
 
+import android.graphics.Rect;
+import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CaptureResult;
+import android.os.Build;
 
 import androidx.annotation.NonNull;
 import androidx.camera.core.Logger;
@@ -27,6 +30,7 @@
 import androidx.camera.core.impl.CameraCaptureMetaData.FlashState;
 import androidx.camera.core.impl.CameraCaptureResult;
 import androidx.camera.core.impl.TagBundle;
+import androidx.camera.core.impl.utils.ExifData;
 
 /** The camera2 implementation for the capture result of a single image capture. */
 public class Camera2CameraCaptureResult implements CameraCaptureResult {
@@ -203,6 +207,66 @@
         return mTagBundle;
     }
 
+    @Override
+    public void populateExifData(@NonNull ExifData.Builder exifData) {
+        // Call interface default to set flash mode
+        CameraCaptureResult.super.populateExifData(exifData);
+
+        // Set dimensions
+        Rect cropRegion = mCaptureResult.get(CaptureResult.SCALER_CROP_REGION);
+        if (cropRegion != null) {
+            exifData.setImageWidth(cropRegion.width())
+                    .setImageHeight(cropRegion.height());
+        }
+
+        // Set orientation
+        Integer jpegOrientation = mCaptureResult.get(CaptureResult.JPEG_ORIENTATION);
+        if (jpegOrientation != null) {
+            exifData.setOrientationDegrees(jpegOrientation);
+        }
+
+        // Set exposure time
+        Long exposureTimeNs = mCaptureResult.get(CaptureResult.SENSOR_EXPOSURE_TIME);
+        if (exposureTimeNs != null) {
+            exifData.setExposureTimeNanos(exposureTimeNs);
+        }
+
+        // Set the aperture
+        Float aperture = mCaptureResult.get(CaptureResult.LENS_APERTURE);
+        if (aperture != null) {
+            exifData.setLensFNumber(aperture);
+        }
+
+        // Set the ISO
+        Integer iso = mCaptureResult.get(CaptureResult.SENSOR_SENSITIVITY);
+        if (iso != null) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                Integer postRawSensitivityBoost =
+                        mCaptureResult.get(CaptureResult.CONTROL_POST_RAW_SENSITIVITY_BOOST);
+                if (postRawSensitivityBoost != null) {
+                    iso *= (int) (postRawSensitivityBoost / 100f);
+                }
+            }
+            exifData.setIso(iso);
+        }
+
+        // Set the focal length
+        Float focalLength = mCaptureResult.get(CaptureResult.LENS_FOCAL_LENGTH);
+        if (focalLength != null) {
+            exifData.setFocalLength(focalLength);
+        }
+
+        // Set white balance MANUAL/AUTO
+        Integer whiteBalanceMode = mCaptureResult.get(CaptureResult.CONTROL_AWB_MODE);
+        if (whiteBalanceMode != null) {
+            ExifData.WhiteBalanceMode wbMode = ExifData.WhiteBalanceMode.AUTO;
+            if (whiteBalanceMode == CameraMetadata.CONTROL_AWB_MODE_OFF) {
+                wbMode = ExifData.WhiteBalanceMode.MANUAL;
+            }
+            exifData.setWhiteBalanceMode(wbMode);
+        }
+    }
+
     @NonNull
     public CaptureResult getCaptureResult() {
         return mCaptureResult;
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 7adafc2..67e08ae 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -25,6 +25,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.os.Build;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Rational;
 import android.util.Size;
@@ -37,7 +38,6 @@
 import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
 import androidx.camera.camera2.internal.compat.CameraManagerCompat;
-import androidx.camera.camera2.internal.compat.quirk.CameraQuirks;
 import androidx.camera.core.CameraUnavailableException;
 import androidx.camera.core.Logger;
 import androidx.camera.core.Preview;
@@ -51,7 +51,6 @@
 import androidx.camera.core.impl.ImmediateSurface;
 import androidx.camera.core.impl.LiveDataObservable;
 import androidx.camera.core.impl.Observable;
-import androidx.camera.core.impl.Quirks;
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.SessionConfig.ValidatingBuilder;
 import androidx.camera.core.impl.UseCaseAttachState;
@@ -168,9 +167,6 @@
     private final SynchronizedCaptureSessionOpener.Builder mCaptureSessionOpenerBuilder;
     private final Set<String> mNotifyStateAttachedSet = new HashSet<>();
 
-    @NonNull
-    private final Quirks mCameraQuirks;
-
     /**
      * Constructor for a camera.
      *
@@ -203,10 +199,9 @@
         try {
             CameraCharacteristicsCompat cameraCharacteristicsCompat =
                     mCameraManager.getCameraCharacteristicsCompat(cameraId);
-            mCameraQuirks = CameraQuirks.get(cameraId, cameraCharacteristicsCompat);
             mCameraControlInternal = new Camera2CameraControlImpl(cameraCharacteristicsCompat,
                     executorScheduler, mExecutor, new ControlUpdateListenerInternal(),
-                    mCameraQuirks);
+                    cameraInfoImpl.getCameraQuirks());
             mCameraInfoInternal = cameraInfoImpl;
             mCameraInfoInternal.linkWithCameraControl(mCameraControlInternal);
         } catch (CameraAccessExceptionCompat e) {
@@ -238,7 +233,7 @@
     private void openInternal() {
         switch (mState) {
             case INITIALIZED:
-                openCameraDevice();
+                openCameraDevice(/*fromScheduledCameraReopen=*/false);
                 break;
             case CLOSING:
                 setState(InternalState.REOPENING);
@@ -880,20 +875,17 @@
         return mCameraInfoInternal;
     }
 
-    /** {@inheritDoc} */
-    @NonNull
-    @Override
-    public Quirks getCameraQuirks() {
-        return mCameraQuirks;
-    }
-
     /** Opens the camera device */
     // TODO(b/124268878): Handle SecurityException and require permission in manifest.
     @SuppressLint("MissingPermission")
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     @ExecutedBy("mExecutor")
-    void openCameraDevice() {
+    void openCameraDevice(boolean fromScheduledCameraReopen) {
+        if (!fromScheduledCameraReopen) {
+            mStateCallback.resetReopenMonitor();
+        }
         mStateCallback.cancelScheduledReopen();
+
         // Check that we have an available camera to open here before attempting
         // to open the camera again.
         if (!mCameraAvailability.isCameraAvailable() || !mCameraStateRegistry.tryOpenCamera(this)) {
@@ -1290,14 +1282,16 @@
 
         // Delay long enough to guarantee the app could have been backgrounded.
         // See ProcessLifecycleProvider for where this delay comes from.
-        private static final int REOPEN_DELAY_MS = 700;
+        static final int REOPEN_DELAY_MS = 700;
 
         @CameraExecutor
         private final Executor mExecutor;
         private final ScheduledExecutorService mScheduler;
         private ScheduledReopen mScheduledReopenRunnable;
-        @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-                ScheduledFuture<?> mScheduledReopenHandle;
+        @SuppressWarnings("WeakerAccess") // synthetic accessor
+        ScheduledFuture<?> mScheduledReopenHandle;
+        @NonNull
+        private final CameraReopenMonitor mCameraReopenMonitor = new CameraReopenMonitor();
 
         StateCallback(@NonNull @CameraExecutor Executor executor, @NonNull ScheduledExecutorService
                 scheduler) {
@@ -1352,7 +1346,7 @@
                                 mCameraDeviceError));
                         scheduleCameraReopen();
                     } else {
-                        openCameraDevice();
+                        openCameraDevice(/*fromScheduledCameraReopen=*/false);
                     }
                     break;
                 default:
@@ -1383,7 +1377,7 @@
                 case CLOSING:
                     Logger.e(TAG, String.format("CameraDevice.onError(): %s failed with %s while "
                                     + "in %s state. Will finish closing camera.",
-                                    cameraDevice.getId(), getErrorMessage(error), mState.name()));
+                            cameraDevice.getId(), getErrorMessage(error), mState.name()));
                     closeCamera(/*abortInFlightCaptures=*/false);
                     break;
                 case OPENING:
@@ -1446,17 +1440,25 @@
             closeCamera(/*abortInFlightCaptures=*/false);
         }
 
-        // TODO(b/173710127): Limit the number of reopen attempts, and report an error to the user
-        //  if they all fail.
         @ExecutedBy("mExecutor")
         void scheduleCameraReopen() {
             Preconditions.checkState(mScheduledReopenRunnable == null);
             Preconditions.checkState(mScheduledReopenHandle == null);
-            mScheduledReopenRunnable = new ScheduledReopen(mExecutor);
-            debugLog("Attempting camera re-open in " + REOPEN_DELAY_MS + "ms: "
-                    + mScheduledReopenRunnable);
-            mScheduledReopenHandle = mScheduler.schedule(mScheduledReopenRunnable,
-                    REOPEN_DELAY_MS, TimeUnit.MILLISECONDS);
+
+            if (mCameraReopenMonitor.canScheduleCameraReopen()) {
+                mScheduledReopenRunnable = new ScheduledReopen(mExecutor);
+                debugLog("Attempting camera re-open in " + REOPEN_DELAY_MS + "ms: "
+                        + mScheduledReopenRunnable);
+                mScheduledReopenHandle = mScheduler.schedule(mScheduledReopenRunnable,
+                        REOPEN_DELAY_MS, TimeUnit.MILLISECONDS);
+            } else {
+                // TODO(b/174685338): Report camera opening error to the user
+                Logger.e(TAG,
+                        "Camera reopening attempted for "
+                                + CameraReopenMonitor.REOPEN_LIMIT_MS
+                                + "ms without success.");
+                setState(InternalState.INITIALIZED);
+            }
         }
 
         /**
@@ -1490,6 +1492,15 @@
         }
 
         /**
+         * Resets the camera reopen attempts monitor. This should be called when the camera open is
+         * not triggered by a scheduled camera reopen, but rather by an explicit request.
+         */
+        @ExecutedBy("mExecutor")
+        void resetReopenMonitor() {
+            mCameraReopenMonitor.reset();
+        }
+
+        /**
          * A {@link Runnable} which will attempt to reopen the camera after a scheduled delay.
          */
         class ScheduledReopen implements Runnable {
@@ -1513,11 +1524,45 @@
                     // this is still the scheduled reopen.
                     if (!mCancelled) {
                         Preconditions.checkState(mState == InternalState.REOPENING);
-                        openCameraDevice();
+                        openCameraDevice(/*fromScheduledCameraReopen=*/true);
                     }
                 });
             }
         }
+
+        /** Keeps track of camera reopen attempts in order to limit them. */
+        class CameraReopenMonitor {
+            // Time limit since the first camera reopen attempt after which reopening the camera
+            // should no longer be attempted.
+            static final int REOPEN_LIMIT_MS = 10_000;
+            static final int INVALID_TIME = -1;
+            private long mFirstReopenTime = INVALID_TIME;
+
+            boolean canScheduleCameraReopen() {
+                final long now = SystemClock.uptimeMillis();
+
+                // If it's the first attempt to reopen the camera
+                if (mFirstReopenTime == INVALID_TIME) {
+                    mFirstReopenTime = now;
+                    return true;
+                }
+
+                final boolean hasReachedLimit = now - mFirstReopenTime >= REOPEN_LIMIT_MS;
+
+                // If the limit has been reached, prevent further attempts to reopen the camera,
+                // and reset [firstReopenTime].
+                if (hasReachedLimit) {
+                    reset();
+                    return false;
+                }
+
+                return true;
+            }
+
+            void reset() {
+                mFirstReopenTime = INVALID_TIME;
+            }
+        }
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
@@ -1563,7 +1608,7 @@
             mCameraAvailable = true;
 
             if (mState == InternalState.PENDING_OPEN) {
-                openCameraDevice();
+                openCameraDevice(/*fromScheduledCameraReopen=*/false);
             }
         }
 
@@ -1582,7 +1627,7 @@
         @ExecutedBy("mExecutor")
         public void onOpenAvailable() {
             if (mState == InternalState.PENDING_OPEN) {
-                openCameraDevice();
+                openCameraDevice(/*fromScheduledCameraReopen=*/false);
             }
         }
 
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
index 43c735b..b31c41c 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
@@ -26,6 +26,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.experimental.UseExperimental;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
+import androidx.camera.camera2.internal.compat.quirk.CameraQuirks;
 import androidx.camera.camera2.interop.Camera2CameraInfo;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.CameraSelector;
@@ -36,6 +37,7 @@
 import androidx.camera.core.impl.CameraCaptureCallback;
 import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.ImageOutputConfig.RotationValue;
+import androidx.camera.core.impl.Quirks;
 import androidx.camera.core.impl.utils.CameraOrientationUtil;
 import androidx.core.util.Preconditions;
 import androidx.lifecycle.LiveData;
@@ -80,6 +82,9 @@
     @Nullable
     private List<Pair<CameraCaptureCallback, Executor>> mCameraCaptureCallbacks = null;
 
+    @NonNull
+    private final Quirks mCameraQuirks;
+
     /**
      * Constructs an instance. Before {@link #linkWithCameraControl(Camera2CameraControlImpl)} is
      * called, camera control related API (torch/exposure/zoom) will return default values.
@@ -89,6 +94,7 @@
         mCameraId = Preconditions.checkNotNull(cameraId);
         mCameraCharacteristicsCompat = cameraCharacteristicsCompat;
         mCamera2CameraInfo = new Camera2CameraInfo(this);
+        mCameraQuirks = CameraQuirks.get(cameraId, cameraCharacteristicsCompat);
     }
 
     /**
@@ -336,6 +342,13 @@
         }
     }
 
+    /** {@inheritDoc} */
+    @NonNull
+    @Override
+    public Quirks getCameraQuirks() {
+        return mCameraQuirks;
+    }
+
     /**
      * Gets the implementation of {@link Camera2CameraInfo}.
      */
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java
index 5fb64ee..5efeb28 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java
@@ -29,6 +29,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresPermission;
+import androidx.annotation.RestrictTo;
 import androidx.camera.core.impl.utils.MainThreadAsyncHandler;
 
 import java.util.Map;
@@ -68,16 +69,18 @@
     @NonNull
     public static CameraManagerCompat from(@NonNull Context context,
             @NonNull Handler compatHandler) {
-        if (Build.VERSION.SDK_INT >= 29) {
-            return new CameraManagerCompat(new CameraManagerCompatApi29Impl(context));
-        } else if (Build.VERSION.SDK_INT >= 28) {
-            // Can use Executor directly on API 28+
-            return new CameraManagerCompat(CameraManagerCompatApi28Impl.create(context));
-        }
+        return new CameraManagerCompat(CameraManagerCompatImpl.from(context, compatHandler));
+    }
 
-        // Pass compat handler to implementation.
-        return new CameraManagerCompat(CameraManagerCompatBaseImpl.create(context,
-                compatHandler));
+    /**
+     * Get a {@link CameraManagerCompat} instance from a provided {@link CameraManagerCompatImpl}.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.TESTS)
+    @NonNull
+    public static CameraManagerCompat from(@NonNull final CameraManagerCompatImpl impl) {
+        return new CameraManagerCompat(impl);
     }
 
     /**
@@ -214,8 +217,14 @@
         return mImpl.getCameraManager();
     }
 
-    interface CameraManagerCompatImpl {
+    /** Provides backwards compatibility to {@link CameraManager} features. */
+    public interface CameraManagerCompatImpl {
 
+        /**
+         * Return the list of currently connected camera devices by identifier, including cameras
+         * that may be in use by other camera API clients.
+         */
+        @NonNull
         String[] getCameraIdList() throws CameraAccessExceptionCompat;
 
         void registerAvailabilityCallback(
@@ -236,6 +245,28 @@
 
         @NonNull
         CameraManager getCameraManager();
+
+        /**
+         * Returns a {@link CameraManagerCompatImpl} instance depending on the API level
+         *
+         * @param context       Context used to retrieve the {@link CameraManager}.
+         * @param compatHandler {@link Handler} used for all APIs taking an {@link Executor}
+         *                      argument on lower API levels. If the API level does not support
+         *                      directly executing on an Executor, it will first be posted to
+         *                      this handler and the executor will be called from there.
+         */
+        @NonNull
+        static CameraManagerCompatImpl from(@NonNull Context context,
+                @NonNull Handler compatHandler) {
+            if (Build.VERSION.SDK_INT >= 29) {
+                return new CameraManagerCompatApi29Impl(context);
+            } else if (Build.VERSION.SDK_INT >= 28) {
+                // Can use Executor directly on API 28+
+                return CameraManagerCompatApi28Impl.create(context);
+            }
+            // Pass compat handler to implementation.
+            return CameraManagerCompatBaseImpl.create(context, compatHandler);
+        }
     }
 
     static final class AvailabilityCallbackExecutorWrapper extends
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompatBaseImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompatBaseImpl.java
index eee8d32..c85a016 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompatBaseImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompatBaseImpl.java
@@ -51,6 +51,7 @@
                 new CameraManagerCompatParamsApi21(compatHandler));
     }
 
+    @NonNull
     @Override
     public String[] getCameraIdList() throws CameraAccessExceptionCompat {
         try {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CameraQuirks.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CameraQuirks.java
index 79983aa..473ea83 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CameraQuirks.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CameraQuirks.java
@@ -53,6 +53,9 @@
         if (AspectRatioLegacyApi21Quirk.load(cameraCharacteristicsCompat)) {
             quirks.add(new AspectRatioLegacyApi21Quirk());
         }
+        if (JpegHalCorruptImageQuirk.load(cameraCharacteristicsCompat)) {
+            quirks.add(new JpegHalCorruptImageQuirk());
+        }
         return new Quirks(quirks);
     }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/JpegHalCorruptImageQuirk.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/JpegHalCorruptImageQuirk.java
new file mode 100644
index 0000000..f33de77
--- /dev/null
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/JpegHalCorruptImageQuirk.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.internal.compat.quirk;
+
+import android.hardware.camera2.CameraCharacteristics;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
+import androidx.camera.core.internal.compat.quirk.SoftwareJpegEncodingPreferredQuirk;
+import androidx.core.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Quirk which denotes JPEGs produced directly from the HAL may sometimes be corrupted.
+ *
+ * <p>Corrupt images generally manifest as completely monochrome JPEGs, sometimes solid green.
+ * If possible, it is preferred that CameraX produce JPEGs from some other image format rather
+ * than receiving JPEGs directly from the HAL.
+ *
+ * @see <a href="https://issuetracker.google.com/159831206">issuetracker.google.com/159831206</a>
+ */
+public final class JpegHalCorruptImageQuirk implements SoftwareJpegEncodingPreferredQuirk {
+
+    private static final Set<String> KNOWN_AFFECTED_DEVICES = new HashSet<>(
+            Arrays.asList(
+                    "heroqltevzw"
+            ));
+
+    // TODO: This quirk is limited to FULL/LEVEL_3 cameras currently since it will use a YUV stream
+    //  for ImageCapture. On LIMITED and LEGACY this would limit the guaranteed surface
+    //  combinations.
+    private static final Set<Integer> SUPPORTED_HARDWARE_LEVELS = new HashSet<>();
+
+    static {
+        SUPPORTED_HARDWARE_LEVELS.add(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
+        if (Build.VERSION.SDK_INT >= 24) {
+            SUPPORTED_HARDWARE_LEVELS.add(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3);
+        }
+    }
+
+    static boolean load(@NonNull CameraCharacteristicsCompat characteristicsCompat) {
+
+        int hardwareLevel = Preconditions.checkNotNull(
+                characteristicsCompat.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL));
+        return KNOWN_AFFECTED_DEVICES.contains(Build.DEVICE.toLowerCase(Locale.US))
+                && SUPPORTED_HARDWARE_LEVELS.contains(hardwareLevel);
+    }
+}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CaptureOptionUnpackerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CaptureOptionUnpackerTest.java
index 06e68a0..8ba5a2a 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CaptureOptionUnpackerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CaptureOptionUnpackerTest.java
@@ -24,7 +24,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.os.Build;
 
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.interop.Camera2Interop;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
@@ -54,7 +54,7 @@
     }
 
     @Test
-    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
     public void unpackerExtractsCaptureCallbacks() {
         ImageCapture.Builder imageCaptureBuilder = new ImageCapture.Builder();
         CaptureCallback captureCallback = mock(CaptureCallback.class);
@@ -73,7 +73,7 @@
     }
 
     @Test
-    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
     public void unpackerExtractsOptions() {
         ImageCapture.Builder imageCaptureConfigBuilder = new ImageCapture.Builder();
 
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
index 0ec8db2..b367535 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
@@ -18,10 +18,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -151,7 +150,7 @@
     }
 
     private CameraManagerCompat getCameraManagerCompat() {
-        return CameraManagerCompat.from(ApplicationProvider.getApplicationContext());
+        return CameraManagerCompat.from((Context) ApplicationProvider.getApplicationContext());
     }
 
     private CameraCharacteristicsCompat getCameraCharacteristicsCompat(String cameraId)
@@ -372,7 +371,9 @@
         useCases.add(preview);
 
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(LEGACY_CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         // A legacy level camera device can't support JPEG (ImageCapture) + PRIV (VideoCapture) +
         // PRIV (Preview) combination. An IllegalArgumentException will be thrown when trying to
@@ -399,7 +400,9 @@
         useCases.add(preview);
 
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(LIMITED_CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         Map<UseCaseConfig<?>, Size> suggestedResolutionMap =
                 mSurfaceManager.getSuggestedResolutions(LIMITED_CAMERA_ID, Collections.emptyList(),
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2SessionOptionUnpackerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2SessionOptionUnpackerTest.java
index ca78fc8..a301832 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2SessionOptionUnpackerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2SessionOptionUnpackerTest.java
@@ -27,7 +27,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.os.Build;
 
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.impl.CameraEventCallbacks;
 import androidx.camera.camera2.interop.Camera2Interop;
@@ -58,7 +58,7 @@
     }
 
     @Test
-    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
     public void unpackerExtractsInteropCallbacks() {
         ImageCapture.Builder imageCaptureBuilder = new ImageCapture.Builder();
         CaptureCallback captureCallback = mock(CaptureCallback.class);
@@ -97,7 +97,7 @@
     }
 
     @Test
-    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+    @OptIn(markerClass = ExperimentalCamera2Interop.class)
     public void unpackerExtractsOptions() {
         ImageCapture.Builder imageCaptureConfigBuilder = new ImageCapture.Builder();
 
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/CameraSelectionOptimizerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/CameraSelectionOptimizerTest.java
index de4b238..144207b 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/CameraSelectionOptimizerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/CameraSelectionOptimizerTest.java
@@ -26,6 +26,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.os.Build;
 import android.os.Handler;
+import android.os.Looper;
 
 import androidx.camera.camera2.interop.Camera2CameraFilter;
 import androidx.camera.camera2.interop.Camera2CameraInfo;
@@ -59,7 +60,7 @@
         mCamera2CameraFactory =
                 spy(new Camera2CameraFactory(ApplicationProvider.getApplicationContext(),
                         CameraThreadConfig.create(CameraXExecutors.mainThreadExecutor(),
-                                new Handler()),
+                                new Handler(Looper.getMainLooper())),
                         null));
     }
 
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java
index 5e4dabd..58f8e22 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java
@@ -30,7 +30,7 @@
 import android.util.Range;
 import android.util.Rational;
 
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
 import androidx.camera.core.CameraControl;
 import androidx.camera.core.ExperimentalExposureCompensation;
@@ -57,7 +57,7 @@
 @RunWith(RobolectricTestRunner.class)
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 @DoNotInstrument
-@UseExperimental(markerClass = ExperimentalExposureCompensation.class)
+@OptIn(markerClass = ExperimentalExposureCompensation.class)
 public class ExposureControlTest {
 
     private static final String CAMERA0_ID = "0";
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
index 368e5fe..fcd9b35 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
@@ -93,6 +93,7 @@
 import java.util.concurrent.TimeoutException;
 
 /** Robolectric test for {@link SupportedSurfaceCombination} class */
+@SuppressWarnings("deprecation")
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
@@ -495,7 +496,9 @@
         List<UseCase> useCases = new ArrayList<>();
         useCases.add(fakeUseCase);
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         Map<UseCaseConfig<?>, Size> suggestedResolutionMap =
                 supportedSurfaceCombination.getSuggestedResolutions(Collections.emptyList(),
@@ -605,7 +608,9 @@
         useCases.add(imageCapture);
         useCases.add(imageAnalysis);
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         Map<UseCaseConfig<?>, Size> suggestedResolutionMap =
                 supportedSurfaceCombination.getSuggestedResolutions(Collections.emptyList(),
@@ -653,7 +658,9 @@
         List<UseCase> useCases = new ArrayList<>();
         useCases.add(preview);
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         Map<UseCaseConfig<?>, Size> suggestedResolutionMap =
                 supportedSurfaceCombination.getSuggestedResolutions(Collections.emptyList(),
@@ -743,7 +750,9 @@
         useCases.add(videoCapture);
         useCases.add(preview);
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         Map<UseCaseConfig<?>, Size> suggestedResolutionMap =
                 supportedSurfaceCombination.getSuggestedResolutions(Collections.emptyList(),
@@ -775,7 +784,9 @@
         useCases.add(preview);
 
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         Map<UseCaseConfig<?>, Size> suggestedResolutionMap =
                 supportedSurfaceCombination.getSuggestedResolutions(Collections.emptyList(),
@@ -818,7 +829,9 @@
         useCases.add(preview);
         useCases.add(imageAnalysis);
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         Map<UseCaseConfig<?>, Size> suggestedResolutionMap =
                 supportedSurfaceCombination.getSuggestedResolutions(Collections.emptyList(),
@@ -853,7 +866,9 @@
         useCases.add(preview);
         useCases.add(imageAnalysis);
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         Map<UseCaseConfig<?>, Size> suggestedResolutionMap =
                 supportedSurfaceCombination.getSuggestedResolutions(Collections.emptyList(),
@@ -936,7 +951,9 @@
         useCases.add(videoCapture);
         useCases.add(preview);
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         Map<UseCaseConfig<?>, Size> suggestedResolutionMap =
                 supportedSurfaceCombination.getSuggestedResolutions(Collections.emptyList(),
@@ -1117,7 +1134,9 @@
         useCases.add(imageCapture);
 
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap =
-                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(useCases,
+                Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
+                        mCameraFactory.getCamera(CAMERA_ID).getCameraInfoInternal(),
+                        useCases,
                         mUseCaseConfigFactory);
         Map<UseCaseConfig<?>, Size> suggestedResolutionMap =
                 supportedSurfaceCombination.getSuggestedResolutions(Collections.emptyList(),
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionBaseTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionBaseTest.java
index d1fe978..6776683 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionBaseTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionBaseTest.java
@@ -25,7 +25,6 @@
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
-import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Handler;
 import android.view.Surface;
@@ -45,6 +44,7 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 
+@SuppressWarnings({"deprecation", "unchecked"})
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
@@ -66,7 +66,7 @@
 
         mSyncCaptureSessionBaseImpl =
                 new SynchronizedCaptureSessionBaseImpl(mMockCaptureSessionRepository,
-                        AsyncTask.THREAD_POOL_EXECUTOR, mScheduledExecutorService,
+                        android.os.AsyncTask.THREAD_POOL_EXECUTOR, mScheduledExecutorService,
                         mock(Handler.class));
 
         mMockCaptureSession = mock(CameraCaptureSession.class);
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionStateCallbackTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionStateCallbackTest.java
index 3bb59ff..3790c3e 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionStateCallbackTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionStateCallbackTest.java
@@ -21,7 +21,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.hardware.camera2.CameraCaptureSession;
-import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Handler;
 import android.view.Surface;
@@ -48,13 +47,14 @@
 
     private SynchronizedCaptureSessionBaseImpl mCaptureSessionCompatBaseImpl;
 
+    @SuppressWarnings("deprecation")
     @Before
     public void setUp() {
         mMockCameraCaptureSessionStateCallback = mock(CameraCaptureSession.StateCallback.class);
         mMockStateCallback = mock(SynchronizedCaptureSession.StateCallback.class);
 
         mCaptureSessionCompatBaseImpl = new SynchronizedCaptureSessionBaseImpl(
-                mock(CaptureSessionRepository.class), AsyncTask.THREAD_POOL_EXECUTOR,
+                mock(CaptureSessionRepository.class), android.os.AsyncTask.THREAD_POOL_EXECUTOR,
                 mScheduledExecutorService, mock(Handler.class));
         mCaptureSessionCompatBaseImpl.createCaptureSessionCompat(mock(CameraCaptureSession.class));
 
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionTest.java
index 20d5d81..64635f2 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionTest.java
@@ -23,7 +23,6 @@
 
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraDevice;
-import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Handler;
 
@@ -64,10 +63,11 @@
     private DeferrableSurface mDeferrableSurface1;
     private DeferrableSurface mDeferrableSurface2;
 
+    @SuppressWarnings("deprecation")
     @Before
     public void setUp() {
         mCaptureSessionRepository =
-                new CaptureSessionRepository(AsyncTask.THREAD_POOL_EXECUTOR);
+                new CaptureSessionRepository(android.os.AsyncTask.THREAD_POOL_EXECUTOR);
 
         mDeferrableSurface1 = mock(DeferrableSurface.class);
         mDeferrableSurface2 = mock(DeferrableSurface.class);
@@ -81,8 +81,8 @@
         enabledFeature.add(SynchronizedCaptureSessionOpener.FEATURE_DEFERRABLE_SURFACE_CLOSE);
 
         mCaptureSessionOpenerBuilder = new SynchronizedCaptureSessionOpener.Builder(
-                AsyncTask.SERIAL_EXECUTOR, mScheduledExecutorService, mock(Handler.class),
-                mCaptureSessionRepository, -1);
+                android.os.AsyncTask.SERIAL_EXECUTOR, mScheduledExecutorService,
+                mock(Handler.class), mCaptureSessionRepository, -1);
         mSynchronizedCaptureSessionOpener = mCaptureSessionOpenerBuilder.build();
 
         mMockCaptureSession = mock(CameraCaptureSession.class);
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/CameraDeviceCompatTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/CameraDeviceCompatTest.java
index 2f994a7..5f0b6ff 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/CameraDeviceCompatTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/CameraDeviceCompatTest.java
@@ -46,6 +46,7 @@
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
+@SuppressWarnings("deprecation")
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/AeFpsRangeTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/AeFpsRangeTest.java
index 5d32584..de0fc74 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/AeFpsRangeTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/AeFpsRangeTest.java
@@ -44,6 +44,7 @@
 
     private static final String ANY_CAMERA_ID = "0";
 
+    @SuppressWarnings("unchecked")
     @Test
     public void validEntryExists_correctRangeIsSelected() {
         Range<Integer>[] availableFpsRanges = new Range[]{
@@ -62,6 +63,7 @@
         assertThat(pick).isEqualTo(new Range<>(15, 30));
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void noValidEntry_doesNotSetFpsRange() {
         Range<Integer>[] availableFpsRanges = new Range[]{
@@ -87,6 +89,7 @@
         assertThat(pick).isNull();
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void limitedDevices_doesNotSetFpsRange() {
         Range<Integer>[] availableFpsRanges = new Range[]{
@@ -101,6 +104,7 @@
         assertThat(pick).isNull();
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void fullDevices_doesNotSetFpsRange() {
         Range<Integer>[] availableFpsRanges = new Range[]{
@@ -115,6 +119,7 @@
         assertThat(pick).isNull();
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void level3Devices_doesNotSetFpsRange() {
         Range<Integer>[] availableFpsRanges = new Range[]{
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExcludedSupportedSizesContainerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExcludedSupportedSizesContainerTest.java
index 4dfd1dd..748720f 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExcludedSupportedSizesContainerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExcludedSupportedSizesContainerTest.java
@@ -72,7 +72,6 @@
         mConfig = config;
     }
 
-    @SuppressWarnings("ConfusingArgumentToVarargsMethod")
     @Test
     public void exclude() {
         // Set up device properties
@@ -88,7 +87,7 @@
         // Get sizes to exclude
         final List<Size> excludedSizes = excludedSupportedSizesContainer.get(mConfig.mImageFormat);
 
-        assertThat(excludedSizes).containsExactly(mConfig.mExcludedSizes);
+        assertThat(excludedSizes).containsExactly((Object[]) mConfig.mExcludedSizes);
     }
 
     static class Config {
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraFilterTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraFilterTest.java
index 820a234..e985477 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraFilterTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraFilterTest.java
@@ -24,7 +24,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.os.Build;
 
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.internal.Camera2CameraInfoImpl;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
 import androidx.camera.core.CameraFilter;
@@ -46,7 +46,7 @@
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+@OptIn(markerClass = ExperimentalCamera2Interop.class)
 public final class Camera2CameraFilterTest {
     private static final String BACK_ID = "0";
     private static final String FRONT_ID = "1";
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraInfoTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraInfoTest.java
index 9e3f9b4..d6883c0 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraInfoTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2CameraInfoTest.java
@@ -24,7 +24,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.os.Build;
 
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.internal.Camera2CameraInfoImpl;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
 import androidx.camera.core.impl.CameraInfoInternal;
@@ -38,7 +38,7 @@
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+@OptIn(markerClass = ExperimentalCamera2Interop.class)
 public final class Camera2CameraInfoTest {
 
     @Test
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2InteropTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2InteropTest.java
index 2594ad5..6fa48ed 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2InteropTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/interop/Camera2InteropTest.java
@@ -25,7 +25,7 @@
 import android.os.Build;
 import android.util.Range;
 
-import androidx.annotation.experimental.UseExperimental;
+import androidx.annotation.OptIn;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.internal.Camera2CaptureCallbacks;
 import androidx.camera.camera2.internal.CameraCaptureSessionStateCallbacks;
@@ -41,7 +41,7 @@
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 @org.robolectric.annotation.Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
+@OptIn(markerClass = ExperimentalCamera2Interop.class)
 public final class Camera2InteropTest {
     private static final int INVALID_TEMPLATE_TYPE = -1;
     private static final int INVALID_COLOR_CORRECTION_MODE = -1;
diff --git a/camera/camera-core/api/public_plus_experimental_1.0.0-beta12.txt b/camera/camera-core/api/public_plus_experimental_1.0.0-beta12.txt
index e432572..45f7159b 100644
--- a/camera/camera-core/api/public_plus_experimental_1.0.0-beta12.txt
+++ b/camera/camera-core/api/public_plus_experimental_1.0.0-beta12.txt
@@ -69,12 +69,14 @@
   }
 
   public final class CameraXConfig {
+    method @androidx.camera.core.ExperimentalAvailableCamerasLimiter public androidx.camera.core.CameraSelector? getAvailableCamerasSelector(androidx.camera.core.CameraSelector?);
     method @androidx.camera.core.ExperimentalLogging public int getMinimumLoggingLevel();
   }
 
   public static final class CameraXConfig.Builder {
     method public androidx.camera.core.CameraXConfig build();
     method public static androidx.camera.core.CameraXConfig.Builder fromConfig(androidx.camera.core.CameraXConfig);
+    method @androidx.camera.core.ExperimentalAvailableCamerasLimiter public androidx.camera.core.CameraXConfig.Builder setAvailableCamerasSelector(androidx.camera.core.CameraSelector);
     method public androidx.camera.core.CameraXConfig.Builder setCameraExecutor(java.util.concurrent.Executor);
     method @androidx.camera.core.ExperimentalLogging public androidx.camera.core.CameraXConfig.Builder setMinimumLoggingLevel(@IntRange(from=android.util.Log.DEBUG, to=android.util.Log.ERROR) int);
     method @androidx.camera.core.ExperimentalCustomizableThreads public androidx.camera.core.CameraXConfig.Builder setSchedulerHandler(android.os.Handler);
@@ -88,6 +90,9 @@
     ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraInfo, float, float);
   }
 
+  @experimental.Experimental @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalAvailableCamerasSelector {
+  }
+
   @experimental.Experimental @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCameraFilter {
   }
 
diff --git a/camera/camera-core/api/public_plus_experimental_current.txt b/camera/camera-core/api/public_plus_experimental_current.txt
index e432572..b10368c 100644
--- a/camera/camera-core/api/public_plus_experimental_current.txt
+++ b/camera/camera-core/api/public_plus_experimental_current.txt
@@ -69,12 +69,14 @@
   }
 
   public final class CameraXConfig {
+    method @androidx.camera.core.ExperimentalAvailableCamerasLimiter public androidx.camera.core.CameraSelector? getAvailableCamerasLimiter(androidx.camera.core.CameraSelector?);
     method @androidx.camera.core.ExperimentalLogging public int getMinimumLoggingLevel();
   }
 
   public static final class CameraXConfig.Builder {
     method public androidx.camera.core.CameraXConfig build();
     method public static androidx.camera.core.CameraXConfig.Builder fromConfig(androidx.camera.core.CameraXConfig);
+    method @androidx.camera.core.ExperimentalAvailableCamerasLimiter public androidx.camera.core.CameraXConfig.Builder setAvailableCamerasLimiter(androidx.camera.core.CameraSelector);
     method public androidx.camera.core.CameraXConfig.Builder setCameraExecutor(java.util.concurrent.Executor);
     method @androidx.camera.core.ExperimentalLogging public androidx.camera.core.CameraXConfig.Builder setMinimumLoggingLevel(@IntRange(from=android.util.Log.DEBUG, to=android.util.Log.ERROR) int);
     method @androidx.camera.core.ExperimentalCustomizableThreads public androidx.camera.core.CameraXConfig.Builder setSchedulerHandler(android.os.Handler);
@@ -88,6 +90,9 @@
     ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraInfo, float, float);
   }
 
+  @experimental.Experimental @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalAvailableCamerasLimiter {
+  }
+
   @experimental.Experimental @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCameraFilter {
   }
 
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index 0615e08..49d045a 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -15,7 +15,6 @@
  */
 
 import static androidx.build.dependencies.DependenciesKt.*
-import androidx.build.LibraryVersions
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
@@ -48,6 +47,7 @@
     testImplementation project(":camera:camera-testing"), {
         exclude group: "androidx.camera", module: "camera-core"
     }
+    testImplementation("androidx.exifinterface:exifinterface:1.0.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/CameraXTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/CameraXTest.java
index 8157db1..5c061ff 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/CameraXTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/CameraXTest.java
@@ -186,6 +186,7 @@
         assertThat(cameraX1.getCameraFactory()).isEqualTo(cameraFactory1);
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void canGetCameraXContext() {
         initCameraX();
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
index 8263ce2..8f3b4b0 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
@@ -192,6 +192,7 @@
     }
 
     // TODO(b/149336664): add a test to verify jpeg quality is 100 when CaptureMode is MAX_QUALITY.
+    @SuppressWarnings("unchecked")
     @Test
     public void captureWithMinLatency_jpegQualityIs95() throws InterruptedException {
         // Arrange.
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageSaverTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageSaverTest.java
index 7c49137..b00fd48 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageSaverTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageSaverTest.java
@@ -193,6 +193,7 @@
         mBackgroundExecutor.shutdown();
     }
 
+    @SuppressWarnings("deprecation")
     private void createDefaultPictureFolderIfNotExist() {
         File pictureFolder = Environment.getExternalStoragePublicDirectory(
                 Environment.DIRECTORY_PICTURES);
@@ -268,6 +269,7 @@
         mContentResolver.delete(saveLocationUri, null, null);
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void saveToUriWithEmptyCollection_onErrorCalled() throws InterruptedException {
         // Arrange.
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.java
index 34cba04..d6adbe0 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.java
@@ -37,6 +37,7 @@
 import androidx.camera.core.impl.ImageOutputConfig;
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.testing.fakes.FakeCameraInfoInternal;
 import androidx.camera.testing.fakes.FakeUseCase;
 import androidx.camera.testing.fakes.FakeUseCaseConfig;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -188,7 +189,10 @@
 
         TestUseCase testUseCase = new TestUseCase(useCaseConfig);
 
-        UseCaseConfig<?> mergedConfig = testUseCase.mergeConfigs(extendedConfig, defaultConfig);
+        FakeCameraInfoInternal cameraInfo = new FakeCameraInfoInternal();
+
+        UseCaseConfig<?> mergedConfig = testUseCase.mergeConfigs(cameraInfo, extendedConfig,
+                defaultConfig);
 
         assertThat(mergedConfig.getSurfaceOccupancyPriority()).isEqualTo(cameraDefaultPriority);
         assertThat(mergedConfig.getInputFormat()).isEqualTo(useCaseImageFormat);
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/CaptureConfigTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/CaptureConfigTest.java
index 0e55cdb..4c6bea7 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/CaptureConfigTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/CaptureConfigTest.java
@@ -250,7 +250,7 @@
     static class FakeMultiValueSet extends MultiValueSet<Object> {
         @NonNull
         @Override
-        public MultiValueSet clone() {
+        public MultiValueSet<Object> clone() {
             FakeMultiValueSet multiValueSet = new FakeMultiValueSet();
             multiValueSet.addAll(getAllItems());
             return multiValueSet;
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/DeferrableSurfacesTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/DeferrableSurfacesTest.java
index 389990c..2f1684c 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/DeferrableSurfacesTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/DeferrableSurfacesTest.java
@@ -76,7 +76,7 @@
 
     @Test
     @MediumTest
-    @SuppressWarnings("deprecation") /* AsyncTask */
+    @SuppressWarnings({"deprecation", "unchecked"}) /* AsyncTask */
     public void getSurfaceTimeoutTest() {
         DeferrableSurface fakeDeferrableSurface = getFakeDeferrableSurface();
 
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/ImmediateSurfaceTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/ImmediateSurfaceTest.java
index def62a21..dae33b0 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/ImmediateSurfaceTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/ImmediateSurfaceTest.java
@@ -63,6 +63,7 @@
         assertThat(surfaceListenableFuture.get()).isSameInstanceAs(mMockSurface);
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void surfaceClosedExceptionWhenClosed() {
         mImmediateSurface.close();
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/utils/ExifOutputStreamTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/utils/ExifOutputStreamTest.kt
new file mode 100644
index 0000000..8b88aa5
--- /dev/null
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/utils/ExifOutputStreamTest.kt
@@ -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.camera.core.impl.utils
+
+import android.graphics.Bitmap
+import android.os.Build
+import androidx.camera.core.impl.CameraCaptureMetaData
+import androidx.exifinterface.media.ExifInterface
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import java.io.File
+
+@LargeTest
+public class ExifOutputStreamTest {
+
+    @Test
+    public fun canSetExifOnCompressedBitmap() {
+        // Arrange.
+        val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
+        val exifData = ExifData.builderForDevice()
+            .setImageWidth(bitmap.width)
+            .setImageHeight(bitmap.height)
+            .setFlashState(CameraCaptureMetaData.FlashState.NONE)
+            .setExposureTimeNanos(0)
+            .build()
+
+        val fileWithExif = File.createTempFile("testWithExif", ".jpg")
+        val outputStreamWithExif = ExifOutputStream(fileWithExif.outputStream(), exifData)
+        fileWithExif.deleteOnExit()
+        val fileWithoutExif = File.createTempFile("testWithoutExif", ".jpg")
+        val outputStreamWithoutExif = fileWithoutExif.outputStream()
+        fileWithoutExif.deleteOnExit()
+
+        // Act.
+        bitmap.compress(Bitmap.CompressFormat.JPEG, 95, outputStreamWithExif)
+        bitmap.compress(Bitmap.CompressFormat.JPEG, 95, outputStreamWithoutExif)
+
+        // Verify with ExifInterface
+        val withExif = ExifInterface(fileWithExif.inputStream())
+        val withoutExif = ExifInterface(fileWithoutExif.inputStream())
+
+        // Assert.
+        // Model comes from default builder
+        assertThat(withExif.getAttribute(ExifInterface.TAG_MODEL)).isEqualTo(Build.MODEL)
+        assertThat(withoutExif.getAttribute(ExifInterface.TAG_MODEL)).isNull()
+
+        assertThat(withExif.getAttribute(ExifInterface.TAG_IMAGE_WIDTH)).isEqualTo("100")
+        assertThat(withoutExif.getAttribute(ExifInterface.TAG_IMAGE_WIDTH)).isEqualTo("100")
+
+        assertThat(withExif.getAttribute(ExifInterface.TAG_IMAGE_LENGTH)).isEqualTo("100")
+        assertThat(withoutExif.getAttribute(ExifInterface.TAG_IMAGE_LENGTH)).isEqualTo("100")
+
+        assertThat(withExif.getAttribute(ExifInterface.TAG_FLASH)?.toShort())
+            .isEqualTo(ExifInterface.FLAG_FLASH_NO_FLASH_FUNCTION)
+        assertThat(withoutExif.getAttribute(ExifInterface.TAG_FLASH)).isNull()
+
+        assertThat(withExif.getAttribute(ExifInterface.TAG_EXPOSURE_TIME)?.toFloat()?.toInt())
+            .isEqualTo(0)
+        assertThat(withoutExif.getAttribute(ExifInterface.TAG_EXPOSURE_TIME)).isNull()
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraSelector.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraSelector.java
index 13e5d31..e4d5954 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraSelector.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraSelector.java
@@ -32,7 +32,8 @@
 import java.util.List;
 
 /**
- * A set of requirements and priorities used to select a camera.
+ * A set of requirements and priorities used to select a camera or return a filtered set of
+ * cameras.
  */
 public final class CameraSelector {
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
index ea5eea6..2d630a2 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
@@ -31,6 +31,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.annotation.experimental.UseExperimental;
 import androidx.camera.core.impl.CameraDeviceSurfaceManager;
 import androidx.camera.core.impl.CameraFactory;
 import androidx.camera.core.impl.CameraInternal;
@@ -538,6 +539,7 @@
     /**
      * Initializes camera stack on the given thread and retry recursively until timeout.
      */
+    @UseExperimental(markerClass = ExperimentalAvailableCamerasLimiter.class)
     private void initAndRetryRecursively(
             @NonNull Executor cameraExecutor,
             long startMs,
@@ -562,8 +564,10 @@
                 CameraThreadConfig cameraThreadConfig = CameraThreadConfig.create(mCameraExecutor,
                         mSchedulerHandler);
 
+                CameraSelector availableCamerasLimiter =
+                        mCameraXConfig.getAvailableCamerasLimiter(null);
                 mCameraFactory = cameraFactoryProvider.newInstance(mAppContext,
-                        cameraThreadConfig, null);
+                        cameraThreadConfig, availableCamerasLimiter);
                 CameraDeviceSurfaceManager.Provider surfaceManagerProvider =
                         mCameraXConfig.getDeviceSurfaceManagerProvider(null);
                 if (surfaceManagerProvider == null) {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraXConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraXConfig.java
index 7290fb4..4ac5e41 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraXConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraXConfig.java
@@ -101,6 +101,10 @@
             Option.create(
                     "camerax.core.appConfig.minimumLoggingLevel",
                     int.class);
+    static final Option<CameraSelector> OPTION_AVAILABLE_CAMERAS_LIMITER =
+            Option.create(
+                    "camerax.core.appConfig.availableCamerasLimiter",
+                    CameraSelector.class);
 
     // *********************************************************************************************
 
@@ -178,6 +182,16 @@
         return mConfig.retrieveOption(OPTION_MIN_LOGGING_LEVEL, Logger.DEFAULT_MIN_LOG_LEVEL);
     }
 
+    /**
+     * Returns the {@link CameraSelector} used to determine the available cameras.
+     */
+    @ExperimentalAvailableCamerasLimiter
+    @Nullable
+    public CameraSelector getAvailableCamerasLimiter(@Nullable CameraSelector valueIfMissing) {
+        return mConfig.retrieveOption(OPTION_AVAILABLE_CAMERAS_LIMITER, valueIfMissing);
+    }
+
+
     /** @hide */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
@@ -329,6 +343,32 @@
             return this;
         }
 
+        /**
+         * Sets a {@link CameraSelector} to determine the available cameras which defines
+         * which cameras can be used in the application.
+         *
+         * <p>Only cameras selected by this CameraSelector can be used in the applications. If
+         * the application binds the use cases with a CameraSelector that selects a unavailable
+         * camera, a {@link IllegalArgumentException} will be thrown.
+         *
+         * <p>This configuration can help CameraX optimize the latency of CameraX initialization.
+         * The tasks CameraX initialization performs include enumerating cameras, querying
+         * CameraCharacteristics and retrieving properties preparing for resolution determination.
+         * On some low end devices, these could take significant amount of time. Using the API
+         * can avoid the initialization of unnecessary cameras and speed up the time for camera
+         * start-up. For example, if the application uses only back cameras, it can set this
+         * configuration by CameraSelector.DEFAULT_BACK_CAMERA and then CameraX will avoid
+         * initializing front cameras to reduce the latency.
+         */
+        @ExperimentalAvailableCamerasLimiter
+        @NonNull
+        public Builder setAvailableCamerasLimiter(
+                @NonNull CameraSelector availableCameraSelector) {
+            getMutableConfig().insertOption(OPTION_AVAILABLE_CAMERAS_LIMITER,
+                    availableCameraSelector);
+            return this;
+        }
+
         @NonNull
         private MutableConfig getMutableConfig() {
             return mMutableConfig;
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalAvailableCamerasLimiter.java b/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalAvailableCamerasLimiter.java
new file mode 100644
index 0000000..99fc74f
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalAvailableCamerasLimiter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import androidx.annotation.experimental.Experimental;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Denotes that the annotated method uses an experimental API that configures CameraX to
+ * limit the available cameras applications can use in order to optimize the initialization
+ * latency.
+ *
+ * <p>Once the configuration is enabled, only cameras selected by this CameraSelector can be used
+ * in the applications. If the application binds the use cases with a CameraSelector that selects
+ * a unavailable camera, a {@link IllegalArgumentException} will be thrown.
+ *
+ * <p>CameraX initialization performs tasks including enumerating cameras, querying
+ * CameraCharacteristics and retrieving properties preparing for resolution determination. On
+ * some low end devices, these could take significant amount of time. Using the API can avoid the
+ * initialization of unnecessary cameras and speed up the time for camera start-up. For example,
+ * if the application uses only back cameras, it can set this configuration by
+ * CameraSelector.DEFAULT_BACK_CAMERA and then CameraX will avoid initializing front cameras to
+ * reduce the latency.
+ */
+@Retention(CLASS)
+@Experimental
+public @interface ExperimentalAvailableCamerasLimiter {
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index a962f19..a6c3b87 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -37,6 +37,7 @@
 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_RESOLUTION;
 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_ROTATION;
 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_USE_CASE_EVENT_CALLBACK;
+import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_USE_SOFTWARE_JPEG_ENCODER;
 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_FORMAT;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAMERA_SELECTOR;
 import static androidx.camera.core.internal.utils.ImageUtil.min;
@@ -52,6 +53,7 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.provider.MediaStore;
@@ -80,6 +82,7 @@
 import androidx.camera.core.impl.CameraCaptureMetaData.AwbState;
 import androidx.camera.core.impl.CameraCaptureResult;
 import androidx.camera.core.impl.CameraCaptureResult.EmptyCameraCaptureResult;
+import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.CameraInternal;
 import androidx.camera.core.impl.CaptureBundle;
 import androidx.camera.core.impl.CaptureConfig;
@@ -108,6 +111,8 @@
 import androidx.camera.core.impl.utils.futures.Futures;
 import androidx.camera.core.internal.IoConfig;
 import androidx.camera.core.internal.TargetConfig;
+import androidx.camera.core.internal.YuvToJpegProcessor;
+import androidx.camera.core.internal.compat.quirk.SoftwareJpegEncodingPreferredQuirk;
 import androidx.camera.core.internal.compat.workaround.ExifRotationAvailability;
 import androidx.camera.core.internal.utils.ImageUtil;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
@@ -290,6 +295,11 @@
      */
     private CaptureProcessor mCaptureProcessor;
 
+    /**
+     * Whether the software JPEG pipeline will be used.
+     */
+    private boolean mUseSoftwareJpeg = false;
+
     ////////////////////////////////////////////////////////////////////////////////////////////
     // [UseCase attached dynamic] - Can change but is only available when the UseCase is attached.
     ////////////////////////////////////////////////////////////////////////////////////////////
@@ -356,18 +366,52 @@
                                     resolution.getHeight(), getImageFormat(), MAX_IMAGES, 0));
             mMetadataMatchingCaptureCallback = new CameraCaptureCallback() {
             };
-        } else if (mCaptureProcessor != null) {
+        } else if (mCaptureProcessor != null || mUseSoftwareJpeg) {
+            // Capture processor set from configuration takes precedence over software JPEG.
+            YuvToJpegProcessor softwareJpegProcessor = null;
+            CaptureProcessor captureProcessor = mCaptureProcessor;
+            int inputFormat = getImageFormat();
+            int outputFormat = getImageFormat();
+            if (mUseSoftwareJpeg) {
+                Preconditions.checkState(mCaptureProcessor == null, "CaptureProcessor should not "
+                        + "be set if software JPEG is to be used.");
+                // API check to satisfy linter
+                if (Build.VERSION.SDK_INT >= 26) {
+                    Logger.i(TAG, "Using software JPEG encoder.");
+                    captureProcessor = softwareJpegProcessor =
+                            new YuvToJpegProcessor(getJpegQuality(), mMaxCaptureStages);
+                    outputFormat = ImageFormat.JPEG;
+                } else {
+                    // Note: This should never be hit due to SDK_INT check before setting
+                    // useSoftwareJpeg.
+                    throw new IllegalStateException("Software JPEG only supported on API 26+");
+                }
+            }
+
             // TODO: To allow user to use an Executor for the image processing.
             mProcessingImageReader =
                     new ProcessingImageReader(
                             resolution.getWidth(),
                             resolution.getHeight(),
-                            getImageFormat(), mMaxCaptureStages,
+                            inputFormat,
+                            mMaxCaptureStages,
                             /* postProcessExecutor */mExecutor,
                             getCaptureBundle(CaptureBundles.singleDefaultCaptureBundle()),
-                            mCaptureProcessor);
+                            captureProcessor,
+                            outputFormat);
             mMetadataMatchingCaptureCallback = mProcessingImageReader.getCameraCaptureCallback();
             mImageReader = new SafeCloseImageReaderProxy(mProcessingImageReader);
+            if (softwareJpegProcessor != null) {
+                // Close the JPEG processor once ProcessingImageReader is done.
+                // Processor is assigned to an effectively final variable here for the lambda.
+                YuvToJpegProcessor processorToClose = softwareJpegProcessor;
+                mProcessingImageReader.getCloseFuture().addListener(() -> {
+                    // API check to satisfy linter
+                    if (Build.VERSION.SDK_INT >= 26) {
+                        processorToClose.close();
+                    }
+                }, CameraXExecutors.directExecutor());
+            }
         } else {
             MetadataImageReader metadataImageReader = new MetadataImageReader(resolution.getWidth(),
                     resolution.getHeight(), getImageFormat(), MAX_IMAGES);
@@ -463,7 +507,24 @@
     @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
     @Override
-    UseCaseConfig<?> onMergeConfig(@NonNull UseCaseConfig.Builder<?, ?, ?> builder) {
+    UseCaseConfig<?> onMergeConfig(@NonNull CameraInfoInternal cameraInfo,
+            @NonNull UseCaseConfig.Builder<?, ?, ?> builder) {
+        // Request software JPEG encoder if quirk exists on this device and the software JPEG
+        // option has not already been explicitly set.
+        if (cameraInfo.getCameraQuirks().contains(SoftwareJpegEncodingPreferredQuirk.class)) {
+            if (!builder.getMutableConfig().retrieveOption(OPTION_USE_SOFTWARE_JPEG_ENCODER,
+                    true)) {
+                Logger.w(TAG, "Device quirk suggests software JPEG encoder, but it has been "
+                        + "explicitly disabled.");
+            } else {
+                Logger.i(TAG, "Requesting software JPEG due to device quirk.");
+                builder.getMutableConfig().insertOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, true);
+            }
+        }
+
+        // If software JPEG is requested, disable if it can't be supported on current API level.
+        boolean useSoftwareJpeg = enforceSoftwareJpegConstraints(builder.getMutableConfig());
+
         // Update the input format base on the other options set (mainly whether processing
         // is done)
         Integer bufferFormat = builder.getMutableConfig().retrieveOption(OPTION_BUFFER_FORMAT,
@@ -473,9 +534,11 @@
                     builder.getMutableConfig().retrieveOption(OPTION_CAPTURE_PROCESSOR, null)
                             == null,
                     "Cannot set buffer format with CaptureProcessor defined.");
-            builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, bufferFormat);
+            builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT,
+                    useSoftwareJpeg ? ImageFormat.YUV_420_888 : bufferFormat);
         } else {
-            if (builder.getMutableConfig().retrieveOption(OPTION_CAPTURE_PROCESSOR, null) != null) {
+            if (builder.getMutableConfig().retrieveOption(OPTION_CAPTURE_PROCESSOR, null) != null
+                    || useSoftwareJpeg) {
                 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT,
                         ImageFormat.YUV_420_888);
             } else {
@@ -1156,6 +1219,43 @@
     }
 
     /**
+     * Disables software JPEG if it is requested and the provided config and/or device
+     * characteristics are incompatible.
+     *
+     * @return {@code true} if software JPEG will be used after applying constraints.
+     */
+    static boolean enforceSoftwareJpegConstraints(@NonNull MutableConfig mutableConfig) {
+        // Software encoder currently only supports API 26+.
+        if (mutableConfig.retrieveOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, false)) {
+            boolean supported = true;
+            if (Build.VERSION.SDK_INT < 26) {
+                Logger.w(TAG, "Software JPEG only supported on API 26+, but current API level is "
+                        + Build.VERSION.SDK_INT);
+                supported = false;
+            }
+
+            Integer bufferFormat = mutableConfig.retrieveOption(OPTION_BUFFER_FORMAT, null);
+            if (bufferFormat != null && bufferFormat != ImageFormat.JPEG) {
+                Logger.w(TAG, "Software JPEG cannot be used with non-JPEG output buffer format.");
+                supported = false;
+            }
+
+            if (mutableConfig.retrieveOption(OPTION_CAPTURE_PROCESSOR, null) != null) {
+                Logger.w(TAG, "CaptureProcessor is set, unable to use software JPEG.");
+                supported = false;
+            }
+
+            if (!supported) {
+                Logger.w(TAG, "Unable to support software JPEG. Disabling.");
+                mutableConfig.insertOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, false);
+            }
+
+            return supported;
+        }
+        return false;
+    }
+
+    /**
      * {@inheritDoc}
      *
      * @hide
@@ -1165,6 +1265,7 @@
     public void onDetached() {
         abortImageCaptureRequests();
         clearPipeline();
+        mUseSoftwareJpeg = false;
         mExecutor.shutdown();
     }
 
@@ -1187,6 +1288,10 @@
         mCaptureBundle = useCaseConfig.getCaptureBundle(
                 CaptureBundles.singleDefaultCaptureBundle());
 
+        // This will only be set to true if software JPEG was requested and
+        // enforceSoftwareJpegConstraints() hasn't removed the request.
+        mUseSoftwareJpeg = useCaseConfig.isSoftwareJpegEncoderRequested();
+
         mExecutor =
                 Executors.newFixedThreadPool(
                         1,
@@ -1418,7 +1523,15 @@
         if (mProcessingImageReader != null) {
             // If the Processor is provided, check if we have valid CaptureBundle and update
             // ProcessingImageReader before actually issuing a take picture request.
-            captureBundle = getCaptureBundle(null);
+            if (mUseSoftwareJpeg) {
+                captureBundle = getCaptureBundle(CaptureBundles.singleDefaultCaptureBundle());
+                if (captureBundle.getCaptureStages().size() > 1) {
+                    return Futures.immediateFailedFuture(new IllegalArgumentException(
+                            "Software JPEG not supported with CaptureBundle size > 1."));
+                }
+            } else {
+                captureBundle = getCaptureBundle(null);
+            }
 
             if (captureBundle == null) {
                 return Futures.immediateFailedFuture(new IllegalArgumentException(
@@ -2722,6 +2835,15 @@
             return this;
         }
 
+        /** @hide */
+        @NonNull
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        public Builder setSoftwareJpegEncoderRequested(boolean requestSoftwareJpeg) {
+            getMutableConfig().insertOption(OPTION_USE_SOFTWARE_JPEG_ENCODER,
+                    requestSoftwareJpeg);
+            return this;
+        }
+
         // Implementations of IoConfig.Builder default methods
 
         /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageInfo.java
index b8a2b6b..f5feed0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageInfo.java
@@ -19,6 +19,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.camera.core.impl.TagBundle;
+import androidx.camera.core.impl.utils.ExifData;
 
 /** Metadata for an image. */
 public interface ImageInfo {
@@ -64,4 +65,12 @@
      */
     // TODO(b/122806727) Need to correctly set EXIF in JPEG images
     int getRotationDegrees();
+
+    /**
+     * Adds any stored EXIF information in this ImageInfo into the provided ExifData builder.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    void populateExifData(@NonNull ExifData.Builder exifBuilder);
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImmutableImageInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/ImmutableImageInfo.java
index 1e7a45d..010bc49 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImmutableImageInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImmutableImageInfo.java
@@ -18,6 +18,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.camera.core.impl.TagBundle;
+import androidx.camera.core.impl.utils.ExifData;
 
 import com.google.auto.value.AutoValue;
 
@@ -37,4 +38,10 @@
 
     @Override
     public abstract int getRotationDegrees();
+
+    @Override
+    public void populateExifData(@NonNull ExifData.Builder exifBuilder) {
+        // Only have access to orientation information.
+        exifBuilder.setOrientationDegrees(getRotationDegrees());
+    }
 }
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 e01c28d..e5f4e41 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
@@ -60,6 +60,7 @@
 import androidx.annotation.experimental.UseExperimental;
 import androidx.camera.core.impl.CameraCaptureCallback;
 import androidx.camera.core.impl.CameraCaptureResult;
+import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.CameraInternal;
 import androidx.camera.core.impl.CaptureConfig;
 import androidx.camera.core.impl.CaptureProcessor;
@@ -465,7 +466,8 @@
     @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
     @Override
-    UseCaseConfig<?> onMergeConfig(@NonNull UseCaseConfig.Builder<?, ?, ?> builder) {
+    UseCaseConfig<?> onMergeConfig(@NonNull CameraInfoInternal cameraInfo,
+            @NonNull UseCaseConfig.Builder<?, ?, ?> builder) {
         if (builder.getMutableConfig().retrieveOption(OPTION_PREVIEW_CAPTURE_PROCESSOR, null)
                 != null) {
             builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, ImageFormat.YUV_420_888);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ProcessingImageReader.java b/camera/camera-core/src/main/java/androidx/camera/core/ProcessingImageReader.java
index 5221385..1c2e30a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ProcessingImageReader.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ProcessingImageReader.java
@@ -16,6 +16,7 @@
 
 package androidx.camera.core;
 
+import android.graphics.ImageFormat;
 import android.media.ImageReader;
 import android.util.Size;
 import android.view.Surface;
@@ -30,6 +31,7 @@
 import androidx.camera.core.impl.ImageReaderProxy;
 import androidx.camera.core.impl.utils.futures.FutureCallback;
 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;
@@ -111,6 +113,11 @@
                             mInputImageReader.close();
                             mSettableImageProxyBundle.close();
                             mOutputImageReader.close();
+
+                            if (mCloseCompleter != null) {
+                                // Notify listeners of close
+                                mCloseCompleter.set(null);
+                            }
                         }
                     }
                 }
@@ -141,6 +148,11 @@
     @Nullable
     Executor mExecutor;
 
+    @GuardedBy("mLock")
+    CallbackToFutureAdapter.Completer<Void> mCloseCompleter;
+    @GuardedBy("mLock")
+    private ListenableFuture<Void> mCloseFuture;
+
     /** The Executor to execute the image post processing task. */
     @NonNull
     final Executor mPostProcessExecutor;
@@ -157,12 +169,19 @@
 
     private final List<Integer> mCaptureIdList = new ArrayList<>();
 
+    ProcessingImageReader(int width, int height, int format, int maxImages,
+            @NonNull Executor postProcessExecutor,
+            @NonNull CaptureBundle captureBundle, @NonNull CaptureProcessor captureProcessor) {
+        this(width, height, format, maxImages, postProcessExecutor, captureBundle,
+                captureProcessor, format);
+    }
+
     /**
      * Create a {@link ProcessingImageReader} with specific configurations.
      *
      * @param width               Width of the ImageReader
      * @param height              Height of the ImageReader
-     * @param format              Image format
+     * @param inputFormat         Input image format
      * @param maxImages           Maximum Image number the ImageReader can hold. The capacity should
      *                            be greater than the captureBundle size in order to hold all the
      *                            Images needed with this processing.
@@ -170,32 +189,51 @@
      * @param captureBundle       The {@link CaptureBundle} includes the processing information
      * @param captureProcessor    The {@link CaptureProcessor} to be invoked when the Images are
      *                            ready
+     * @param outputFormat        Output image format
      */
-    ProcessingImageReader(int width, int height, int format, int maxImages,
+    ProcessingImageReader(int width, int height, int inputFormat, int maxImages,
             @NonNull Executor postProcessExecutor,
-            @NonNull CaptureBundle captureBundle, @NonNull CaptureProcessor captureProcessor) {
-        this(new MetadataImageReader(width, height, format, maxImages), postProcessExecutor,
-                captureBundle, captureProcessor);
+            @NonNull CaptureBundle captureBundle, @NonNull CaptureProcessor captureProcessor,
+            int outputFormat) {
+        this(new MetadataImageReader(width, height, inputFormat, maxImages), postProcessExecutor,
+                captureBundle, captureProcessor, outputFormat);
+    }
+
+    ProcessingImageReader(@NonNull MetadataImageReader imageReader,
+            @NonNull Executor postProcExecutor,
+            @NonNull CaptureBundle capBundle,
+            @NonNull CaptureProcessor capProcessor) {
+        this(imageReader, postProcExecutor, capBundle, capProcessor, imageReader.getImageFormat());
     }
 
     ProcessingImageReader(@NonNull MetadataImageReader imageReader,
             @NonNull Executor postProcessExecutor,
             @NonNull CaptureBundle captureBundle,
-            @NonNull CaptureProcessor captureProcessor) {
+            @NonNull CaptureProcessor captureProcessor,
+            int outputFormat) {
         if (imageReader.getMaxImages() < captureBundle.getCaptureStages().size()) {
             throw new IllegalArgumentException(
                     "MetadataImageReader is smaller than CaptureBundle.");
         }
 
         mInputImageReader = imageReader;
+
+        // For JPEG ImageReaders, the Surface that is created will have format BLOB which can
+        // only be allocated with a height of 1. The output Image from the image reader will read
+        // its dimensions from the JPEG data's EXIF in order to set the final dimensions.
+        int outputWidth = imageReader.getWidth();
+        int outputHeight = imageReader.getHeight();
+        if (outputFormat == ImageFormat.JPEG) {
+            outputWidth = imageReader.getWidth() * imageReader.getHeight();
+            outputHeight = 1;
+        }
         mOutputImageReader = new AndroidImageReaderProxy(
-                ImageReader.newInstance(imageReader.getWidth(),
-                        imageReader.getHeight(), imageReader.getImageFormat(),
+                ImageReader.newInstance(outputWidth, outputHeight, outputFormat,
                         imageReader.getMaxImages()));
 
         mPostProcessExecutor = postProcessExecutor;
         mCaptureProcessor = captureProcessor;
-        mCaptureProcessor.onOutputSurface(mOutputImageReader.getSurface(), getImageFormat());
+        mCaptureProcessor.onOutputSurface(mOutputImageReader.getSurface(), outputFormat);
         mCaptureProcessor.onResolutionUpdate(
                 new Size(mInputImageReader.getWidth(), mInputImageReader.getHeight()));
 
@@ -235,12 +273,45 @@
                 mInputImageReader.close();
                 mSettableImageProxyBundle.close();
                 mOutputImageReader.close();
+
+                if (mCloseCompleter != null) {
+                    mCloseCompleter.set(null);
+                }
             }
 
             mClosed = true;
         }
     }
 
+    /**
+     * Returns a future that will complete when the ProcessingImageReader is actually closed.
+     *
+     * @return A future that signals when the ProcessingImageReader is actually closed
+     * (after all processing). Cancelling this future has no effect.
+     */
+    @NonNull
+    ListenableFuture<Void> getCloseFuture() {
+        ListenableFuture<Void> closeFuture;
+        synchronized (mLock) {
+            if (mClosed && !mProcessing) {
+                // Everything should be closed. Return immediate future.
+                closeFuture = Futures.immediateFuture(null);
+            } else {
+                if (mCloseFuture == null) {
+                    mCloseFuture = CallbackToFutureAdapter.getFuture(completer -> {
+                        // Should already be locked, but lock again to satisfy linter.
+                        synchronized (mLock) {
+                            mCloseCompleter = completer;
+                        }
+                        return "ProcessingImageReader-close";
+                    });
+                }
+                closeFuture = Futures.nonCancellationPropagating(mCloseFuture);
+            }
+        }
+        return closeFuture;
+    }
+
     @Override
     public int getHeight() {
         synchronized (mLock) {
@@ -258,7 +329,7 @@
     @Override
     public int getImageFormat() {
         synchronized (mLock) {
-            return mInputImageReader.getImageFormat();
+            return mOutputImageReader.getImageFormat();
         }
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
index ec92f42..ca32637 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
@@ -30,6 +30,7 @@
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
 import androidx.camera.core.impl.CameraControlInternal;
+import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.CameraInternal;
 import androidx.camera.core.impl.Config;
 import androidx.camera.core.impl.Config.Option;
@@ -164,6 +165,7 @@
     /**
      * Create a merged {@link UseCaseConfig} from the UseCase, camera, and an extended config.
      *
+     * @param cameraInfo          info about the camera which may be used to resolve conflicts.
      * @param extendedConfig      configs that take priority over the UseCase's default config
      * @param cameraDefaultConfig configs that have lower priority than the UseCase's default.
      *                            This Config comes from the camera implementation.
@@ -175,6 +177,7 @@
     @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
     public UseCaseConfig<?> mergeConfigs(
+            @NonNull CameraInfoInternal cameraInfo,
             @Nullable UseCaseConfig<?> extendedConfig,
             @Nullable UseCaseConfig<?> cameraDefaultConfig) {
         MutableOptionsBundle mergedConfig;
@@ -199,8 +202,7 @@
 
         if (extendedConfig != null) {
             // If any options need special handling, this is the place to do it. For now we'll
-            // just copy
-            // over all options.
+            // just copy over all options.
             for (Option<?> opt : extendedConfig.listOptions()) {
                 @SuppressWarnings("unchecked") // Options/values are being copied directly
                         Option<Object> objectOpt = (Option<Object>) opt;
@@ -222,7 +224,7 @@
             mergedConfig.removeOption(ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO);
         }
 
-        return onMergeConfig(getUseCaseConfigBuilder(mergedConfig));
+        return onMergeConfig(cameraInfo, getUseCaseConfigBuilder(mergedConfig));
     }
 
     /**
@@ -231,8 +233,9 @@
      * <p> This can be overridden by a UseCase which need to do additional verification of the
      * configs to make sure there are no conflicting options.
      *
-     * @param builder the builder containing the merged configs requiring addition conflict
-     *                resolution
+     * @param cameraInfo info about the camera which may be used to resolve conflicts.
+     * @param builder    the builder containing the merged configs requiring addition conflict
+     *                   resolution
      * @return the conflict resolved config
      * @throws IllegalArgumentException if there exists conflicts in the merged config that can
      * not be resolved
@@ -240,7 +243,8 @@
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
-    UseCaseConfig<?> onMergeConfig(@NonNull UseCaseConfig.Builder<?, ?, ?> builder) {
+    UseCaseConfig<?> onMergeConfig(@NonNull CameraInfoInternal cameraInfo,
+            @NonNull UseCaseConfig.Builder<?, ?, ?> builder) {
         return builder.getUseCaseConfig();
     }
 
@@ -263,7 +267,16 @@
             UseCaseConfigUtil.updateTargetRotationAndRelatedConfigs(builder, targetRotation);
             mUseCaseConfig = builder.getUseCaseConfig();
 
-            mCurrentConfig = mergeConfigs(mExtendedConfig, mCameraConfig);
+            // Only merge configs if currently attached to a camera. Otherwise, set the current
+            // config to the use case config and mergeConfig() will be called once the use case
+            // is attached to a camera.
+            CameraInternal camera = getCamera();
+            if (camera == null) {
+                mCurrentConfig = mUseCaseConfig;
+            } else {
+                mCurrentConfig = mergeConfigs(camera.getCameraInfoInternal(), mExtendedConfig,
+                        mCameraConfig);
+            }
 
             return true;
         }
@@ -531,7 +544,8 @@
 
         mExtendedConfig = extendedConfig;
         mCameraConfig = cameraConfig;
-        mCurrentConfig = mergeConfigs(mExtendedConfig, mCameraConfig);
+        mCurrentConfig = mergeConfigs(camera.getCameraInfoInternal(), mExtendedConfig,
+                mCameraConfig);
 
         EventCallback eventCallback = mCurrentConfig.getUseCaseEventCallback(null);
         if (eventCallback != null) {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraCaptureResult.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraCaptureResult.java
index abdc327..a808987 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraCaptureResult.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraCaptureResult.java
@@ -22,6 +22,7 @@
 import androidx.camera.core.impl.CameraCaptureMetaData.AfState;
 import androidx.camera.core.impl.CameraCaptureMetaData.AwbState;
 import androidx.camera.core.impl.CameraCaptureMetaData.FlashState;
+import androidx.camera.core.impl.utils.ExifData;
 
 /**
  * The result of a single image capture.
@@ -60,6 +61,11 @@
     @NonNull
     TagBundle getTagBundle();
 
+    /** Populates the given Exif.Builder with attributes from this CameraCaptureResult. */
+    default void populateExifData(@NonNull ExifData.Builder exifBuilder) {
+        exifBuilder.setFlashState(getFlashState());
+    }
+
     /** An implementation of CameraCaptureResult which always return default results. */
     final class EmptyCameraCaptureResult implements CameraCaptureResult {
 
@@ -106,7 +112,7 @@
         @Override
         @NonNull
         public TagBundle getTagBundle() {
-            return null;
+            return TagBundle.emptyBundle();
         }
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraFactory.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraFactory.java
index 3efbda6..1e87b19 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraFactory.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraFactory.java
@@ -40,14 +40,14 @@
          *
          * @param context the android context
          * @param threadConfig the thread config to run the camera operations
-         * @param availableCamerasSelector a CameraSelector used to specify which cameras will be
+         * @param availableCamerasLimiter a CameraSelector used to specify which cameras will be
          *                                 loaded and available to CameraX.
          * @return the factory instance
          * @throws InitializationException if it fails to create the factory.
          */
         @NonNull CameraFactory newInstance(@NonNull Context context,
                 @NonNull CameraThreadConfig threadConfig,
-                @Nullable CameraSelector availableCamerasSelector) throws InitializationException;
+                @Nullable CameraSelector availableCamerasLimiter) throws InitializationException;
     }
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
index e751bc1..7dc5669 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
@@ -62,4 +62,8 @@
      * {@link #addSessionCaptureCallback(Executor, CameraCaptureCallback)}.
      */
     void removeSessionCaptureCallback(@NonNull CameraCaptureCallback callback);
+
+    /** Returns a list of quirks related to the camera. */
+    @NonNull
+    Quirks getCameraQuirks();
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
index 9c5226f..8422efc 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
@@ -153,10 +153,6 @@
     @NonNull
     CameraInfoInternal getCameraInfoInternal();
 
-    /** Returns a list of quirks related to the camera. */
-    @NonNull
-    Quirks getCameraQuirks();
-
     ////////////////////////////////////////////////////////////////////////////////////////////////
     // Camera interface
     ////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageCaptureConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageCaptureConfig.java
index 0eff9aa..13e4c7a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageCaptureConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageCaptureConfig.java
@@ -51,8 +51,10 @@
     public static final Option<Integer> OPTION_MAX_CAPTURE_STAGES =
             Option.create("camerax.core.imageCapture.maxCaptureStages", Integer.class);
     public static final Option<ImageReaderProxyProvider> OPTION_IMAGE_READER_PROXY_PROVIDER =
-            Option.create("camerax.core.imageAnalysis.imageReaderProxyProvider",
+            Option.create("camerax.core.imageCapture.imageReaderProxyProvider",
                     ImageReaderProxyProvider.class);
+    public static final Option<Boolean> OPTION_USE_SOFTWARE_JPEG_ENCODER =
+            Option.create("camerax.core.imageCapture.useSoftwareJpegEncoder", boolean.class);
 
     // *********************************************************************************************
 
@@ -223,6 +225,16 @@
         return retrieveOption(OPTION_IMAGE_READER_PROXY_PROVIDER, null);
     }
 
+    /**
+     * Returns whether ImageCapture should use a software JPEG encoder, if available.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public boolean isSoftwareJpegEncoderRequested() {
+        return retrieveOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, false);
+    }
+
     // Implementations of IO default methods
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/Quirks.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/Quirks.java
index 2246074..5e3871f 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/Quirks.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/Quirks.java
@@ -39,6 +39,10 @@
     /**
      * Retrieves a {@link Quirk} instance given its type.
      *
+     * <p>Unlike {@link #contains(Class)}, a quirk can only be retrieved by the exact class. If a
+     * superclass or superinterface is provided, {@code null} will be returned, even if a quirk
+     * with the provided superclass or superinterface exists in this collection.
+     *
      * @param quirkClass The type of quirk to retrieve.
      * @return A {@link Quirk} instance of the provided type, or {@code null} if it isn't found.
      */
@@ -52,4 +56,23 @@
         }
         return null;
     }
+
+    /**
+     * Returns whether this collection of quirks contains a quirk with the provided type.
+     *
+     * <p>This checks whether the provided quirk type is the exact class, a superclass, or a
+     * superinterface of any of the contained quirks, and will return true in all cases.
+     * @param quirkClass The type of quirk to check for existence in this container.
+     * @return {@code true} if this container contains a quirk with the given type, {@code false}
+     * otherwise.
+     */
+    public boolean contains(@NonNull Class<? extends Quirk> quirkClass) {
+        for (Quirk quirk : mQuirks) {
+            if (quirkClass.isAssignableFrom(quirk.getClass())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ByteOrderedDataInputStream.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ByteOrderedDataInputStream.java
new file mode 100644
index 0000000..318761b
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ByteOrderedDataInputStream.java
@@ -0,0 +1,288 @@
+/*
+ * 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.core.impl.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteOrder;
+
+/**
+ * An input stream to parse EXIF data area, which can be written in either little or big endian
+ * order.
+ */
+// Note: This class is adapted from {@link androidx.exifinterface.media.ExifInterface}
+final class ByteOrderedDataInputStream extends InputStream implements DataInput {
+    private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
+    private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
+
+    private final DataInputStream mDataInputStream;
+    private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final int mLength;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+            int mPosition;
+
+    ByteOrderedDataInputStream(InputStream in) throws IOException {
+        this(in, ByteOrder.BIG_ENDIAN);
+    }
+
+    ByteOrderedDataInputStream(InputStream in, ByteOrder byteOrder) throws IOException {
+        mDataInputStream = new DataInputStream(in);
+        mLength = mDataInputStream.available();
+        mPosition = 0;
+        // TODO (b/142218289): Need to handle case where input stream does not support mark
+        mDataInputStream.mark(mLength);
+        mByteOrder = byteOrder;
+    }
+
+    ByteOrderedDataInputStream(byte[] bytes) throws IOException {
+        this(new ByteArrayInputStream(bytes));
+    }
+
+    public void setByteOrder(ByteOrder byteOrder) {
+        mByteOrder = byteOrder;
+    }
+
+    public void seek(long byteCount) throws IOException {
+        if (mPosition > byteCount) {
+            mPosition = 0;
+            mDataInputStream.reset();
+            // TODO (b/142218289): Need to handle case where input stream does not support mark
+            mDataInputStream.mark(mLength);
+        } else {
+            byteCount -= mPosition;
+        }
+
+        if (skipBytes((int) byteCount) != (int) byteCount) {
+            throw new IOException("Couldn't seek up to the byteCount");
+        }
+    }
+
+    public int peek() {
+        return mPosition;
+    }
+
+    @Override
+    public int available() throws IOException {
+        return mDataInputStream.available();
+    }
+
+    @Override
+    public int read() throws IOException {
+        ++mPosition;
+        return mDataInputStream.read();
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        int bytesRead = mDataInputStream.read(b, off, len);
+        mPosition += bytesRead;
+        return bytesRead;
+    }
+
+    @Override
+    public int readUnsignedByte() throws IOException {
+        ++mPosition;
+        return mDataInputStream.readUnsignedByte();
+    }
+
+    @Override
+    public String readLine() {
+        throw new UnsupportedOperationException("readLine() not implemented.");
+    }
+
+    @Override
+    public boolean readBoolean() throws IOException {
+        ++mPosition;
+        return mDataInputStream.readBoolean();
+    }
+
+    @Override
+    public char readChar() throws IOException {
+        mPosition += 2;
+        return mDataInputStream.readChar();
+    }
+
+    @Override
+    public String readUTF() throws IOException {
+        mPosition += 2;
+        return mDataInputStream.readUTF();
+    }
+
+    @Override
+    public void readFully(byte[] buffer, int offset, int length) throws IOException {
+        mPosition += length;
+        if (mPosition > mLength) {
+            throw new EOFException();
+        }
+        if (mDataInputStream.read(buffer, offset, length) != length) {
+            throw new IOException("Couldn't read up to the length of buffer");
+        }
+    }
+
+    @Override
+    public void readFully(byte[] buffer) throws IOException {
+        mPosition += buffer.length;
+        if (mPosition > mLength) {
+            throw new EOFException();
+        }
+        if (mDataInputStream.read(buffer, 0, buffer.length) != buffer.length) {
+            throw new IOException("Couldn't read up to the length of buffer");
+        }
+    }
+
+    @Override
+    public byte readByte() throws IOException {
+        ++mPosition;
+        if (mPosition > mLength) {
+            throw new EOFException();
+        }
+        int ch = mDataInputStream.read();
+        if (ch < 0) {
+            throw new EOFException();
+        }
+        return (byte) ch;
+    }
+
+    @Override
+    public short readShort() throws IOException {
+        mPosition += 2;
+        if (mPosition > mLength) {
+            throw new EOFException();
+        }
+        int ch1 = mDataInputStream.read();
+        int ch2 = mDataInputStream.read();
+        if ((ch1 | ch2) < 0) {
+            throw new EOFException();
+        }
+        if (mByteOrder == LITTLE_ENDIAN) {
+            return (short) ((ch2 << 8) + ch1);
+        } else if (mByteOrder == BIG_ENDIAN) {
+            return (short) ((ch1 << 8) + ch2);
+        }
+        throw new IOException("Invalid byte order: " + mByteOrder);
+    }
+
+    @Override
+    public int readInt() throws IOException {
+        mPosition += 4;
+        if (mPosition > mLength) {
+            throw new EOFException();
+        }
+        int ch1 = mDataInputStream.read();
+        int ch2 = mDataInputStream.read();
+        int ch3 = mDataInputStream.read();
+        int ch4 = mDataInputStream.read();
+        if ((ch1 | ch2 | ch3 | ch4) < 0) {
+            throw new EOFException();
+        }
+        if (mByteOrder == LITTLE_ENDIAN) {
+            return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
+        } else if (mByteOrder == BIG_ENDIAN) {
+            return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
+        }
+        throw new IOException("Invalid byte order: " + mByteOrder);
+    }
+
+    @Override
+    public int skipBytes(int byteCount) throws IOException {
+        int totalSkip = Math.min(byteCount, mLength - mPosition);
+        int skipped = 0;
+        while (skipped < totalSkip) {
+            skipped += mDataInputStream.skipBytes(totalSkip - skipped);
+        }
+        mPosition += skipped;
+        return skipped;
+    }
+
+    @Override
+    public int readUnsignedShort() throws IOException {
+        mPosition += 2;
+        if (mPosition > mLength) {
+            throw new EOFException();
+        }
+        int ch1 = mDataInputStream.read();
+        int ch2 = mDataInputStream.read();
+        if ((ch1 | ch2) < 0) {
+            throw new EOFException();
+        }
+        if (mByteOrder == LITTLE_ENDIAN) {
+            return ((ch2 << 8) + ch1);
+        } else if (mByteOrder == BIG_ENDIAN) {
+            return ((ch1 << 8) + ch2);
+        }
+        throw new IOException("Invalid byte order: " + mByteOrder);
+    }
+
+    public long readUnsignedInt() throws IOException {
+        return readInt() & 0xffffffffL;
+    }
+
+    @Override
+    public long readLong() throws IOException {
+        mPosition += 8;
+        if (mPosition > mLength) {
+            throw new EOFException();
+        }
+        int ch1 = mDataInputStream.read();
+        int ch2 = mDataInputStream.read();
+        int ch3 = mDataInputStream.read();
+        int ch4 = mDataInputStream.read();
+        int ch5 = mDataInputStream.read();
+        int ch6 = mDataInputStream.read();
+        int ch7 = mDataInputStream.read();
+        int ch8 = mDataInputStream.read();
+        if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) {
+            throw new EOFException();
+        }
+        if (mByteOrder == LITTLE_ENDIAN) {
+            return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40)
+                    + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16)
+                    + ((long) ch2 << 8) + (long) ch1);
+        } else if (mByteOrder == BIG_ENDIAN) {
+            return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40)
+                    + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16)
+                    + ((long) ch7 << 8) + (long) ch8);
+        }
+        throw new IOException("Invalid byte order: " + mByteOrder);
+    }
+
+    @Override
+    public float readFloat() throws IOException {
+        return Float.intBitsToFloat(readInt());
+    }
+
+    @Override
+    public double readDouble() throws IOException {
+        return Double.longBitsToDouble(readLong());
+    }
+
+    @Override
+    public void mark(int readlimit) {
+        synchronized (mDataInputStream) {
+            mDataInputStream.mark(readlimit);
+        }
+    }
+
+    public int getLength() {
+        return mLength;
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ByteOrderedDataOutputStream.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ByteOrderedDataOutputStream.java
new file mode 100644
index 0000000..5f830f8
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ByteOrderedDataOutputStream.java
@@ -0,0 +1,88 @@
+/*
+ * 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.core.impl.utils;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteOrder;
+
+/**
+ * An output stream to write EXIF data area, which can be written in either little or big endian
+ * order.
+ */
+// Note: This class is adapted from {@link androidx.exifinterface.media.ExifInterface}
+class ByteOrderedDataOutputStream extends FilterOutputStream {
+    final OutputStream mOutputStream;
+    private ByteOrder mByteOrder;
+
+    ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder) {
+        super(out);
+        mOutputStream = out;
+        mByteOrder = byteOrder;
+    }
+
+    public void setByteOrder(ByteOrder byteOrder) {
+        mByteOrder = byteOrder;
+    }
+
+    @Override
+    public void write(byte[] bytes) throws IOException {
+        mOutputStream.write(bytes);
+    }
+
+    @Override
+    public void write(byte[] bytes, int offset, int length) throws IOException {
+        mOutputStream.write(bytes, offset, length);
+    }
+
+    public void writeByte(int val) throws IOException {
+        mOutputStream.write(val);
+    }
+
+    public void writeShort(short val) throws IOException {
+        if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
+            mOutputStream.write((val >>> 0) & 0xFF);
+            mOutputStream.write((val >>> 8) & 0xFF);
+        } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
+            mOutputStream.write((val >>> 8) & 0xFF);
+            mOutputStream.write((val >>> 0) & 0xFF);
+        }
+    }
+
+    public void writeInt(int val) throws IOException {
+        if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
+            mOutputStream.write((val >>> 0) & 0xFF);
+            mOutputStream.write((val >>> 8) & 0xFF);
+            mOutputStream.write((val >>> 16) & 0xFF);
+            mOutputStream.write((val >>> 24) & 0xFF);
+        } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
+            mOutputStream.write((val >>> 24) & 0xFF);
+            mOutputStream.write((val >>> 16) & 0xFF);
+            mOutputStream.write((val >>> 8) & 0xFF);
+            mOutputStream.write((val >>> 0) & 0xFF);
+        }
+    }
+
+    public void writeUnsignedShort(int val) throws IOException {
+        writeShort((short) val);
+    }
+
+    public void writeUnsignedInt(long val) throws IOException {
+        writeInt((int) val);
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifAttribute.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifAttribute.java
new file mode 100644
index 0000000..02a1d39
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifAttribute.java
@@ -0,0 +1,462 @@
+/*
+ * 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.core.impl.utils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.camera.core.Logger;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * A class for indicating EXIF attribute.
+ *
+ * This class was pulled from the {@link androidx.exifinterface.media.ExifInterface} class.
+ */
+final class ExifAttribute {
+    private static final String TAG = "ExifAttribute";
+    public static final long BYTES_OFFSET_UNKNOWN = -1;
+
+    // See JPEG File Interchange Format Version 1.02.
+    // The following values are defined for handling JPEG streams. In this implementation, we are
+    // not only getting information from EXIF but also from some JPEG special segments such as
+    // MARKER_COM for user comment and MARKER_SOFx for image width and height.
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    static final Charset ASCII = StandardCharsets.US_ASCII;
+
+    // Formats for the value in IFD entry (See TIFF 6.0 Section 2, "Image File Directory".)
+    static final int IFD_FORMAT_BYTE = 1;
+    static final int IFD_FORMAT_STRING = 2;
+    static final int IFD_FORMAT_USHORT = 3;
+    static final int IFD_FORMAT_ULONG = 4;
+    static final int IFD_FORMAT_URATIONAL = 5;
+    static final int IFD_FORMAT_SBYTE = 6;
+    static final int IFD_FORMAT_UNDEFINED = 7;
+    static final int IFD_FORMAT_SSHORT = 8;
+    static final int IFD_FORMAT_SLONG = 9;
+    static final int IFD_FORMAT_SRATIONAL = 10;
+    static final int IFD_FORMAT_SINGLE = 11;
+    static final int IFD_FORMAT_DOUBLE = 12;
+    // Names for the data formats for debugging purpose.
+    static final String[] IFD_FORMAT_NAMES = new String[] {
+            "", "BYTE", "STRING", "USHORT", "ULONG", "URATIONAL", "SBYTE", "UNDEFINED", "SSHORT",
+            "SLONG", "SRATIONAL", "SINGLE", "DOUBLE", "IFD"
+    };
+    // Sizes of the components of each IFD value format
+    static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] {
+            0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 1
+    };
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    static final byte[] EXIF_ASCII_PREFIX = new byte[] {
+            0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0
+    };
+
+    public final int format;
+    public final int numberOfComponents;
+    public final long bytesOffset;
+    public final byte[] bytes;
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
+        this(format, numberOfComponents, BYTES_OFFSET_UNKNOWN, bytes);
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    ExifAttribute(int format, int numberOfComponents, long bytesOffset, byte[] bytes) {
+        this.format = format;
+        this.numberOfComponents = numberOfComponents;
+        this.bytesOffset = bytesOffset;
+        this.bytes = bytes;
+    }
+
+    @NonNull
+    public static ExifAttribute createUShort(@NonNull int[] values, @NonNull ByteOrder byteOrder) {
+        final ByteBuffer buffer = ByteBuffer.wrap(
+                new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_USHORT] * values.length]);
+        buffer.order(byteOrder);
+        for (int value : values) {
+            buffer.putShort((short) value);
+        }
+        return new ExifAttribute(IFD_FORMAT_USHORT, values.length, buffer.array());
+    }
+
+    @NonNull
+    public static ExifAttribute createUShort(int value, @NonNull ByteOrder byteOrder) {
+        return createUShort(new int[] {value}, byteOrder);
+    }
+
+    @NonNull
+    public static ExifAttribute createULong(@NonNull long[] values, @NonNull ByteOrder byteOrder) {
+        final ByteBuffer buffer = ByteBuffer.wrap(
+                new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_ULONG] * values.length]);
+        buffer.order(byteOrder);
+        for (long value : values) {
+            buffer.putInt((int) value);
+        }
+        return new ExifAttribute(IFD_FORMAT_ULONG, values.length, buffer.array());
+    }
+
+    @NonNull
+    public static ExifAttribute createULong(long value, @NonNull ByteOrder byteOrder) {
+        return createULong(new long[] {value}, byteOrder);
+    }
+
+    @NonNull
+    public static ExifAttribute createSLong(@NonNull int[] values, @NonNull ByteOrder byteOrder) {
+        final ByteBuffer buffer = ByteBuffer.wrap(
+                new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SLONG] * values.length]);
+        buffer.order(byteOrder);
+        for (int value : values) {
+            buffer.putInt(value);
+        }
+        return new ExifAttribute(IFD_FORMAT_SLONG, values.length, buffer.array());
+    }
+
+    @NonNull
+    public static ExifAttribute createSLong(int value, @NonNull ByteOrder byteOrder) {
+        return createSLong(new int[] {value}, byteOrder);
+    }
+
+    @NonNull
+    public static ExifAttribute createByte(@NonNull String value) {
+        // Exception for GPSAltitudeRef tag
+        if (value.length() == 1 && value.charAt(0) >= '0' && value.charAt(0) <= '1') {
+            final byte[] bytes = new byte[] { (byte) (value.charAt(0) - '0') };
+            return new ExifAttribute(IFD_FORMAT_BYTE, bytes.length, bytes);
+        }
+        final byte[] ascii = value.getBytes(ASCII);
+        return new ExifAttribute(IFD_FORMAT_BYTE, ascii.length, ascii);
+    }
+
+    @NonNull
+    public static ExifAttribute createString(@NonNull String value) {
+        final byte[] ascii = (value + '\0').getBytes(ASCII);
+        return new ExifAttribute(IFD_FORMAT_STRING, ascii.length, ascii);
+    }
+
+    @NonNull
+    public static ExifAttribute createURational(@NonNull LongRational[] values,
+            @NonNull ByteOrder byteOrder) {
+        final ByteBuffer buffer = ByteBuffer.wrap(
+                new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_URATIONAL] * values.length]);
+        buffer.order(byteOrder);
+        for (LongRational value : values) {
+            buffer.putInt((int) value.getNumerator());
+            buffer.putInt((int) value.getDenominator());
+        }
+        return new ExifAttribute(IFD_FORMAT_URATIONAL, values.length, buffer.array());
+    }
+
+    @NonNull
+    public static ExifAttribute createURational(@NonNull LongRational value,
+            @NonNull ByteOrder byteOrder) {
+        return createURational(new LongRational[] {value}, byteOrder);
+    }
+
+    @NonNull
+    public static ExifAttribute createSRational(@NonNull LongRational[] values,
+            @NonNull ByteOrder byteOrder) {
+        final ByteBuffer buffer = ByteBuffer.wrap(
+                new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SRATIONAL] * values.length]);
+        buffer.order(byteOrder);
+        for (LongRational value : values) {
+            buffer.putInt((int) value.getNumerator());
+            buffer.putInt((int) value.getDenominator());
+        }
+        return new ExifAttribute(IFD_FORMAT_SRATIONAL, values.length, buffer.array());
+    }
+
+    @NonNull
+    public static ExifAttribute createSRational(@NonNull LongRational value,
+            @NonNull ByteOrder byteOrder) {
+        return createSRational(new LongRational[] {value}, byteOrder);
+    }
+
+    @NonNull
+    public static ExifAttribute createDouble(@NonNull double[] values,
+            @NonNull ByteOrder byteOrder) {
+        final ByteBuffer buffer = ByteBuffer.wrap(
+                new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_DOUBLE] * values.length]);
+        buffer.order(byteOrder);
+        for (double value : values) {
+            buffer.putDouble(value);
+        }
+        return new ExifAttribute(IFD_FORMAT_DOUBLE, values.length, buffer.array());
+    }
+
+    @NonNull
+    public static ExifAttribute createDouble(double value, @NonNull ByteOrder byteOrder) {
+        return createDouble(new double[] {value}, byteOrder);
+    }
+
+    @Override
+    public String toString() {
+        return "(" + IFD_FORMAT_NAMES[format] + ", data length:" + bytes.length + ")";
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    Object getValue(ByteOrder byteOrder) {
+        ByteOrderedDataInputStream inputStream = null;
+        try {
+            inputStream = new ByteOrderedDataInputStream(bytes);
+            inputStream.setByteOrder(byteOrder);
+            switch (format) {
+                case IFD_FORMAT_BYTE:
+                case IFD_FORMAT_SBYTE: {
+                    // Exception for GPSAltitudeRef tag
+                    if (bytes.length == 1 && bytes[0] >= 0 && bytes[0] <= 1) {
+                        return new String(new char[] { (char) (bytes[0] + '0') });
+                    }
+                    return new String(bytes, ASCII);
+                }
+                case IFD_FORMAT_UNDEFINED:
+                case IFD_FORMAT_STRING: {
+                    int index = 0;
+                    if (numberOfComponents >= EXIF_ASCII_PREFIX.length) {
+                        boolean same = true;
+                        for (int i = 0; i < EXIF_ASCII_PREFIX.length; ++i) {
+                            if (bytes[i] != EXIF_ASCII_PREFIX[i]) {
+                                same = false;
+                                break;
+                            }
+                        }
+                        if (same) {
+                            index = EXIF_ASCII_PREFIX.length;
+                        }
+                    }
+
+                    StringBuilder stringBuilder = new StringBuilder();
+                    while (index < numberOfComponents) {
+                        int ch = bytes[index];
+                        if (ch == 0) {
+                            break;
+                        }
+                        if (ch >= 32) {
+                            stringBuilder.append((char) ch);
+                        } else {
+                            stringBuilder.append('?');
+                        }
+                        ++index;
+                    }
+                    return stringBuilder.toString();
+                }
+                case IFD_FORMAT_USHORT: {
+                    final int[] values = new int[numberOfComponents];
+                    for (int i = 0; i < numberOfComponents; ++i) {
+                        values[i] = inputStream.readUnsignedShort();
+                    }
+                    return values;
+                }
+                case IFD_FORMAT_ULONG: {
+                    final long[] values = new long[numberOfComponents];
+                    for (int i = 0; i < numberOfComponents; ++i) {
+                        values[i] = inputStream.readUnsignedInt();
+                    }
+                    return values;
+                }
+                case IFD_FORMAT_URATIONAL: {
+                    final LongRational[] values = new LongRational[numberOfComponents];
+                    for (int i = 0; i < numberOfComponents; ++i) {
+                        final long numerator = inputStream.readUnsignedInt();
+                        final long denominator = inputStream.readUnsignedInt();
+                        values[i] = new LongRational(numerator, denominator);
+                    }
+                    return values;
+                }
+                case IFD_FORMAT_SSHORT: {
+                    final int[] values = new int[numberOfComponents];
+                    for (int i = 0; i < numberOfComponents; ++i) {
+                        values[i] = inputStream.readShort();
+                    }
+                    return values;
+                }
+                case IFD_FORMAT_SLONG: {
+                    final int[] values = new int[numberOfComponents];
+                    for (int i = 0; i < numberOfComponents; ++i) {
+                        values[i] = inputStream.readInt();
+                    }
+                    return values;
+                }
+                case IFD_FORMAT_SRATIONAL: {
+                    final LongRational[] values = new LongRational[numberOfComponents];
+                    for (int i = 0; i < numberOfComponents; ++i) {
+                        final long numerator = inputStream.readInt();
+                        final long denominator = inputStream.readInt();
+                        values[i] = new LongRational(numerator, denominator);
+                    }
+                    return values;
+                }
+                case IFD_FORMAT_SINGLE: {
+                    final double[] values = new double[numberOfComponents];
+                    for (int i = 0; i < numberOfComponents; ++i) {
+                        values[i] = inputStream.readFloat();
+                    }
+                    return values;
+                }
+                case IFD_FORMAT_DOUBLE: {
+                    final double[] values = new double[numberOfComponents];
+                    for (int i = 0; i < numberOfComponents; ++i) {
+                        values[i] = inputStream.readDouble();
+                    }
+                    return values;
+                }
+                default:
+                    return null;
+            }
+        } catch (IOException e) {
+            Logger.w(TAG, "IOException occurred during reading a value", e);
+            return null;
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    Logger.e(TAG, "IOException occurred while closing InputStream", e);
+                }
+            }
+        }
+    }
+
+    public double getDoubleValue(@NonNull ByteOrder byteOrder) {
+        Object value = getValue(byteOrder);
+        if (value == null) {
+            throw new NumberFormatException("NULL can't be converted to a double value");
+        }
+        if (value instanceof String) {
+            return Double.parseDouble((String) value);
+        }
+        if (value instanceof long[]) {
+            long[] array = (long[]) value;
+            if (array.length == 1) {
+                return array[0];
+            }
+            throw new NumberFormatException("There are more than one component");
+        }
+        if (value instanceof int[]) {
+            int[] array = (int[]) value;
+            if (array.length == 1) {
+                return array[0];
+            }
+            throw new NumberFormatException("There are more than one component");
+        }
+        if (value instanceof double[]) {
+            double[] array = (double[]) value;
+            if (array.length == 1) {
+                return array[0];
+            }
+            throw new NumberFormatException("There are more than one component");
+        }
+        if (value instanceof LongRational[]) {
+            LongRational[] array = (LongRational[]) value;
+            if (array.length == 1) {
+                return array[0].toDouble();
+            }
+            throw new NumberFormatException("There are more than one component");
+        }
+        throw new NumberFormatException("Couldn't find a double value");
+    }
+
+    public int getIntValue(@NonNull ByteOrder byteOrder) {
+        Object value = getValue(byteOrder);
+        if (value == null) {
+            throw new NumberFormatException("NULL can't be converted to a integer value");
+        }
+        if (value instanceof String) {
+            return Integer.parseInt((String) value);
+        }
+        if (value instanceof long[]) {
+            long[] array = (long[]) value;
+            if (array.length == 1) {
+                return (int) array[0];
+            }
+            throw new NumberFormatException("There are more than one component");
+        }
+        if (value instanceof int[]) {
+            int[] array = (int[]) value;
+            if (array.length == 1) {
+                return array[0];
+            }
+            throw new NumberFormatException("There are more than one component");
+        }
+        throw new NumberFormatException("Couldn't find a integer value");
+    }
+
+    @Nullable
+    public String getStringValue(@NonNull ByteOrder byteOrder) {
+        Object value = getValue(byteOrder);
+        if (value == null) {
+            return null;
+        }
+        if (value instanceof String) {
+            return (String) value;
+        }
+
+        final StringBuilder stringBuilder = new StringBuilder();
+        if (value instanceof long[]) {
+            long[] array = (long[]) value;
+            for (int i = 0; i < array.length; ++i) {
+                stringBuilder.append(array[i]);
+                if (i + 1 != array.length) {
+                    stringBuilder.append(",");
+                }
+            }
+            return stringBuilder.toString();
+        }
+        if (value instanceof int[]) {
+            int[] array = (int[]) value;
+            for (int i = 0; i < array.length; ++i) {
+                stringBuilder.append(array[i]);
+                if (i + 1 != array.length) {
+                    stringBuilder.append(",");
+                }
+            }
+            return stringBuilder.toString();
+        }
+        if (value instanceof double[]) {
+            double[] array = (double[]) value;
+            for (int i = 0; i < array.length; ++i) {
+                stringBuilder.append(array[i]);
+                if (i + 1 != array.length) {
+                    stringBuilder.append(",");
+                }
+            }
+            return stringBuilder.toString();
+        }
+        if (value instanceof LongRational[]) {
+            LongRational[] array = (LongRational[]) value;
+            for (int i = 0; i < array.length; ++i) {
+                stringBuilder.append(array[i].getNumerator());
+                stringBuilder.append('/');
+                stringBuilder.append(array[i].getDenominator());
+                if (i + 1 != array.length) {
+                    stringBuilder.append(",");
+                }
+            }
+            return stringBuilder.toString();
+        }
+        return null;
+    }
+
+    public int size() {
+        return IFD_FORMAT_BYTES_PER_FORMAT[format] * numberOfComponents;
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifData.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifData.java
new file mode 100644
index 0000000..ea5d08a
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifData.java
@@ -0,0 +1,967 @@
+/*
+ * 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.core.impl.utils;
+
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_BYTE;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_DOUBLE;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_SLONG;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_SRATIONAL;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_STRING;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_ULONG;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_UNDEFINED;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_URATIONAL;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_USHORT;
+import static androidx.exifinterface.media.ExifInterface.CONTRAST_NORMAL;
+import static androidx.exifinterface.media.ExifInterface.EXPOSURE_PROGRAM_NOT_DEFINED;
+import static androidx.exifinterface.media.ExifInterface.FILE_SOURCE_DSC;
+import static androidx.exifinterface.media.ExifInterface.FLAG_FLASH_FIRED;
+import static androidx.exifinterface.media.ExifInterface.FLAG_FLASH_NO_FLASH_FUNCTION;
+import static androidx.exifinterface.media.ExifInterface.GPS_DIRECTION_TRUE;
+import static androidx.exifinterface.media.ExifInterface.GPS_DISTANCE_KILOMETERS;
+import static androidx.exifinterface.media.ExifInterface.GPS_SPEED_KILOMETERS_PER_HOUR;
+import static androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_FLASH;
+import static androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_UNKNOWN;
+import static androidx.exifinterface.media.ExifInterface.METERING_MODE_UNKNOWN;
+import static androidx.exifinterface.media.ExifInterface.ORIENTATION_NORMAL;
+import static androidx.exifinterface.media.ExifInterface.RENDERED_PROCESS_NORMAL;
+import static androidx.exifinterface.media.ExifInterface.RESOLUTION_UNIT_INCHES;
+import static androidx.exifinterface.media.ExifInterface.SATURATION_NORMAL;
+import static androidx.exifinterface.media.ExifInterface.SCENE_CAPTURE_TYPE_STANDARD;
+import static androidx.exifinterface.media.ExifInterface.SCENE_TYPE_DIRECTLY_PHOTOGRAPHED;
+import static androidx.exifinterface.media.ExifInterface.SENSITIVITY_TYPE_ISO_SPEED;
+import static androidx.exifinterface.media.ExifInterface.SHARPNESS_NORMAL;
+import static androidx.exifinterface.media.ExifInterface.TAG_APERTURE_VALUE;
+import static androidx.exifinterface.media.ExifInterface.TAG_BRIGHTNESS_VALUE;
+import static androidx.exifinterface.media.ExifInterface.TAG_COLOR_SPACE;
+import static androidx.exifinterface.media.ExifInterface.TAG_COMPONENTS_CONFIGURATION;
+import static androidx.exifinterface.media.ExifInterface.TAG_CONTRAST;
+import static androidx.exifinterface.media.ExifInterface.TAG_CUSTOM_RENDERED;
+import static androidx.exifinterface.media.ExifInterface.TAG_DATETIME;
+import static androidx.exifinterface.media.ExifInterface.TAG_DATETIME_DIGITIZED;
+import static androidx.exifinterface.media.ExifInterface.TAG_DATETIME_ORIGINAL;
+import static androidx.exifinterface.media.ExifInterface.TAG_EXIF_VERSION;
+import static androidx.exifinterface.media.ExifInterface.TAG_EXPOSURE_BIAS_VALUE;
+import static androidx.exifinterface.media.ExifInterface.TAG_EXPOSURE_MODE;
+import static androidx.exifinterface.media.ExifInterface.TAG_EXPOSURE_PROGRAM;
+import static androidx.exifinterface.media.ExifInterface.TAG_EXPOSURE_TIME;
+import static androidx.exifinterface.media.ExifInterface.TAG_FILE_SOURCE;
+import static androidx.exifinterface.media.ExifInterface.TAG_FLASH;
+import static androidx.exifinterface.media.ExifInterface.TAG_FLASHPIX_VERSION;
+import static androidx.exifinterface.media.ExifInterface.TAG_FOCAL_LENGTH;
+import static androidx.exifinterface.media.ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT;
+import static androidx.exifinterface.media.ExifInterface.TAG_F_NUMBER;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_ALTITUDE;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_ALTITUDE_REF;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_DEST_BEARING_REF;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_DEST_DISTANCE_REF;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_IMG_DIRECTION_REF;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_LATITUDE;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_LATITUDE_REF;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_LONGITUDE;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_LONGITUDE_REF;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_SPEED_REF;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_TIMESTAMP;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_TRACK_REF;
+import static androidx.exifinterface.media.ExifInterface.TAG_GPS_VERSION_ID;
+import static androidx.exifinterface.media.ExifInterface.TAG_IMAGE_LENGTH;
+import static androidx.exifinterface.media.ExifInterface.TAG_IMAGE_WIDTH;
+import static androidx.exifinterface.media.ExifInterface.TAG_INTEROPERABILITY_INDEX;
+import static androidx.exifinterface.media.ExifInterface.TAG_ISO_SPEED_RATINGS;
+import static androidx.exifinterface.media.ExifInterface.TAG_LIGHT_SOURCE;
+import static androidx.exifinterface.media.ExifInterface.TAG_MAKE;
+import static androidx.exifinterface.media.ExifInterface.TAG_MAX_APERTURE_VALUE;
+import static androidx.exifinterface.media.ExifInterface.TAG_METERING_MODE;
+import static androidx.exifinterface.media.ExifInterface.TAG_MODEL;
+import static androidx.exifinterface.media.ExifInterface.TAG_ORIENTATION;
+import static androidx.exifinterface.media.ExifInterface.TAG_PHOTOGRAPHIC_SENSITIVITY;
+import static androidx.exifinterface.media.ExifInterface.TAG_PIXEL_X_DIMENSION;
+import static androidx.exifinterface.media.ExifInterface.TAG_PIXEL_Y_DIMENSION;
+import static androidx.exifinterface.media.ExifInterface.TAG_RESOLUTION_UNIT;
+import static androidx.exifinterface.media.ExifInterface.TAG_SATURATION;
+import static androidx.exifinterface.media.ExifInterface.TAG_SCENE_CAPTURE_TYPE;
+import static androidx.exifinterface.media.ExifInterface.TAG_SCENE_TYPE;
+import static androidx.exifinterface.media.ExifInterface.TAG_SENSING_METHOD;
+import static androidx.exifinterface.media.ExifInterface.TAG_SENSITIVITY_TYPE;
+import static androidx.exifinterface.media.ExifInterface.TAG_SHARPNESS;
+import static androidx.exifinterface.media.ExifInterface.TAG_SHUTTER_SPEED_VALUE;
+import static androidx.exifinterface.media.ExifInterface.TAG_SOFTWARE;
+import static androidx.exifinterface.media.ExifInterface.TAG_SUBSEC_TIME;
+import static androidx.exifinterface.media.ExifInterface.TAG_SUBSEC_TIME_DIGITIZED;
+import static androidx.exifinterface.media.ExifInterface.TAG_SUBSEC_TIME_ORIGINAL;
+import static androidx.exifinterface.media.ExifInterface.TAG_WHITE_BALANCE;
+import static androidx.exifinterface.media.ExifInterface.TAG_X_RESOLUTION;
+import static androidx.exifinterface.media.ExifInterface.TAG_Y_CB_CR_POSITIONING;
+import static androidx.exifinterface.media.ExifInterface.TAG_Y_RESOLUTION;
+import static androidx.exifinterface.media.ExifInterface.WHITE_BALANCE_AUTO;
+import static androidx.exifinterface.media.ExifInterface.WHITE_BALANCE_MANUAL;
+import static androidx.exifinterface.media.ExifInterface.Y_CB_CR_POSITIONING_CENTERED;
+
+import android.os.Build;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.camera.core.Logger;
+import androidx.camera.core.impl.CameraCaptureMetaData;
+import androidx.core.util.Preconditions;
+import androidx.exifinterface.media.ExifInterface;
+
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class stores the EXIF header in IFDs according to the JPEG specification.
+ */
+// Note: This class is adapted from {@link androidx.exifinterface.media.ExifInterface}, and is
+// currently expected to be used for writing a subset of Exif values. Support for other mime
+// types besides JPEG have been removed. Support for thumbnails/strips has been removed along
+// with many exif tags. If more tags are required, the source code for ExifInterface should be
+// referenced and can be adapted to this class.
+public class ExifData {
+    private static final String TAG = "ExifData";
+    private static final boolean DEBUG = false;
+
+    /**
+     * Enum representing the white balance mode.
+     */
+    public enum WhiteBalanceMode {
+        /** AWB is turned on. */
+        AUTO,
+        /** AWB is turned off. */
+        MANUAL
+    }
+
+    // Names for the data formats for debugging purpose.
+    static final String[] IFD_FORMAT_NAMES = new String[]{
+            "", "BYTE", "STRING", "USHORT", "ULONG", "URATIONAL", "SBYTE", "UNDEFINED", "SSHORT",
+            "SLONG", "SRATIONAL", "SINGLE", "DOUBLE", "IFD"
+    };
+
+    /**
+     * Private tags used for pointing the other IFD offsets.
+     * The types of the following tags are int.
+     * See JEITA CP-3451C Section 4.6.3: Exif-specific IFD.
+     * For SubIFD, see Note 1 of Adobe PageMaker® 6.0 TIFF Technical Notes.
+     */
+    static final String TAG_EXIF_IFD_POINTER = "ExifIFDPointer";
+    static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer";
+    static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer";
+    static final String TAG_SUB_IFD_POINTER = "SubIFDPointer";
+
+    // Primary image IFD TIFF tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
+    // This is only a subset of the tags defined in ExifInterface
+    private static final ExifTag[] IFD_TIFF_TAGS = new ExifTag[]{
+            // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images.
+            new ExifTag(TAG_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
+            new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
+            new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
+            new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
+            new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
+            new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
+            new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
+    };
+
+    // Primary image IFD Exif Private tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
+    // This is only a subset of the tags defined in ExifInterface
+    private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[]{
+            new ExifTag(TAG_EXPOSURE_TIME, 33434, IFD_FORMAT_URATIONAL),
+            new ExifTag(TAG_F_NUMBER, 33437, IFD_FORMAT_URATIONAL),
+            new ExifTag(TAG_EXPOSURE_PROGRAM, 34850, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_PHOTOGRAPHIC_SENSITIVITY, 34855, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_SENSITIVITY_TYPE, 34864, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_EXIF_VERSION, 36864, IFD_FORMAT_STRING),
+            new ExifTag(TAG_DATETIME_ORIGINAL, 36867, IFD_FORMAT_STRING),
+            new ExifTag(TAG_DATETIME_DIGITIZED, 36868, IFD_FORMAT_STRING),
+            new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121, IFD_FORMAT_UNDEFINED),
+            new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377, IFD_FORMAT_SRATIONAL),
+            new ExifTag(TAG_APERTURE_VALUE, 37378, IFD_FORMAT_URATIONAL),
+            new ExifTag(TAG_BRIGHTNESS_VALUE, 37379, IFD_FORMAT_SRATIONAL),
+            new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380, IFD_FORMAT_SRATIONAL),
+            new ExifTag(TAG_MAX_APERTURE_VALUE, 37381, IFD_FORMAT_URATIONAL),
+            new ExifTag(TAG_METERING_MODE, 37383, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_LIGHT_SOURCE, 37384, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_FLASH, 37385, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_FOCAL_LENGTH, 37386, IFD_FORMAT_URATIONAL),
+            new ExifTag(TAG_SUBSEC_TIME, 37520, IFD_FORMAT_STRING),
+            new ExifTag(TAG_SUBSEC_TIME_ORIGINAL, 37521, IFD_FORMAT_STRING),
+            new ExifTag(TAG_SUBSEC_TIME_DIGITIZED, 37522, IFD_FORMAT_STRING),
+            new ExifTag(TAG_FLASHPIX_VERSION, 40960, IFD_FORMAT_UNDEFINED),
+            new ExifTag(TAG_COLOR_SPACE, 40961, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_PIXEL_X_DIMENSION, 40962, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_SENSING_METHOD, 41495, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_FILE_SOURCE, 41728, IFD_FORMAT_UNDEFINED),
+            new ExifTag(TAG_SCENE_TYPE, 41729, IFD_FORMAT_UNDEFINED),
+            new ExifTag(TAG_CUSTOM_RENDERED, 41985, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_EXPOSURE_MODE, 41986, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_WHITE_BALANCE, 41987, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_CONTRAST, 41992, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_SATURATION, 41993, IFD_FORMAT_USHORT),
+            new ExifTag(TAG_SHARPNESS, 41994, IFD_FORMAT_USHORT)
+    };
+
+    // Primary image IFD GPS Info tags (See JEITA CP-3451C Section 4.6.6 Tag Support Levels)
+    // This is only a subset of the tags defined in ExifInterface
+    private static final ExifTag[] IFD_GPS_TAGS = new ExifTag[]{
+            new ExifTag(TAG_GPS_VERSION_ID, 0, IFD_FORMAT_BYTE),
+            new ExifTag(TAG_GPS_LATITUDE_REF, 1, IFD_FORMAT_STRING),
+            // Allow SRATIONAL to be compatible with apps using wrong format and
+            // even if it is negative, it may be valid latitude / longitude.
+            new ExifTag(TAG_GPS_LATITUDE, 2, IFD_FORMAT_URATIONAL, IFD_FORMAT_SRATIONAL),
+            new ExifTag(TAG_GPS_LONGITUDE_REF, 3, IFD_FORMAT_STRING),
+            new ExifTag(TAG_GPS_LONGITUDE, 4, IFD_FORMAT_URATIONAL, IFD_FORMAT_SRATIONAL),
+            new ExifTag(TAG_GPS_ALTITUDE_REF, 5, IFD_FORMAT_BYTE),
+            new ExifTag(TAG_GPS_ALTITUDE, 6, IFD_FORMAT_URATIONAL),
+            new ExifTag(TAG_GPS_TIMESTAMP, 7, IFD_FORMAT_URATIONAL),
+            new ExifTag(TAG_GPS_SPEED_REF, 12, IFD_FORMAT_STRING),
+            new ExifTag(TAG_GPS_TRACK_REF, 14, IFD_FORMAT_STRING),
+            new ExifTag(TAG_GPS_IMG_DIRECTION_REF, 16, IFD_FORMAT_STRING),
+            new ExifTag(TAG_GPS_DEST_BEARING_REF, 23, IFD_FORMAT_STRING),
+            new ExifTag(TAG_GPS_DEST_DISTANCE_REF, 25, IFD_FORMAT_STRING)
+    };
+
+    // List of tags for pointing to the other image file directory offset.
+    static final ExifTag[] EXIF_POINTER_TAGS = new ExifTag[]{
+            new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
+    };
+
+    // Primary image IFD Interoperability tag (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
+    private static final ExifTag[] IFD_INTEROPERABILITY_TAGS = new ExifTag[]{
+            new ExifTag(TAG_INTEROPERABILITY_INDEX, 1, IFD_FORMAT_STRING)
+    };
+
+    // List of Exif tag groups
+    static final ExifTag[][] EXIF_TAGS = new ExifTag[][]{
+            IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS
+    };
+
+    // Indices for the above tags. Note these must stay in sync with the order of EXIF_TAGS.
+    static final int IFD_TYPE_PRIMARY = 0;
+    static final int IFD_TYPE_EXIF = 1;
+    static final int IFD_TYPE_GPS = 2;
+    static final int IFD_TYPE_INTEROPERABILITY = 3;
+
+    // NOTE: This is a subset of the tags from ExifInterface. Only supports tags in this class.
+    static final HashSet<String> sTagSetForCompatibility = new HashSet<>(Arrays.asList(
+            TAG_F_NUMBER, TAG_EXPOSURE_TIME, TAG_GPS_TIMESTAMP));
+
+    private static final int MM_IN_MICRONS = 1000;
+
+    private final List<Map<String, ExifAttribute>> mAttributes;
+    private final ByteOrder mByteOrder;
+
+    ExifData(ByteOrder order, List<Map<String, ExifAttribute>> attributes) {
+        Preconditions.checkState(attributes.size() == EXIF_TAGS.length, "Malformed attributes "
+                + "list. Number of IFDs mismatch.");
+        mByteOrder = order;
+        mAttributes = attributes;
+    }
+
+    /**
+     * Gets the byte order.
+     */
+    @NonNull
+    public ByteOrder getByteOrder() {
+        return mByteOrder;
+    }
+
+    @NonNull
+    Map<String, ExifAttribute> getAttributes(int ifdIndex) {
+        Preconditions.checkArgumentInRange(ifdIndex, 0, EXIF_TAGS.length,
+                "Invalid IFD index: " + ifdIndex + ". Index should be between [0, EXIF_TAGS"
+                        + ".length] ");
+        return mAttributes.get(ifdIndex);
+    }
+
+    /**
+     * Returns the value of the specified tag or {@code null} if there
+     * is no such tag in the image file.
+     *
+     * @param tag the name of the tag.
+     */
+    @Nullable
+    public String getAttribute(@NonNull String tag) {
+        ExifAttribute attribute = getExifAttribute(tag);
+        if (attribute != null) {
+            if (!sTagSetForCompatibility.contains(tag)) {
+                return attribute.getStringValue(mByteOrder);
+            }
+            if (tag.equals(TAG_GPS_TIMESTAMP)) {
+                // Convert the rational values to the custom formats for backwards compatibility.
+                if (attribute.format != IFD_FORMAT_URATIONAL
+                        && attribute.format != IFD_FORMAT_SRATIONAL) {
+                    Logger.w(TAG,
+                            "GPS Timestamp format is not rational. format=" + attribute.format);
+                    return null;
+                }
+                LongRational[] array =
+                        (LongRational[]) attribute.getValue(mByteOrder);
+                if (array == null || array.length != 3) {
+                    Logger.w(TAG, "Invalid GPS Timestamp array. array=" + Arrays.toString(array));
+                    return null;
+                }
+                return String.format(Locale.US, "%02d:%02d:%02d",
+                        (int) ((float) array[0].getNumerator() / array[0].getDenominator()),
+                        (int) ((float) array[1].getNumerator() / array[1].getDenominator()),
+                        (int) ((float) array[2].getNumerator() / array[2].getDenominator()));
+            }
+            try {
+                return Double.toString(attribute.getDoubleValue(mByteOrder));
+            } catch (NumberFormatException e) {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag.
+     *
+     * @param tag the name of the tag.
+     */
+    @SuppressWarnings("deprecation")
+    @Nullable
+    private ExifAttribute getExifAttribute(@NonNull String tag) {
+        // Maintain compatibility.
+        if (TAG_ISO_SPEED_RATINGS.equals(tag)) {
+            if (DEBUG) {
+                Logger.d(TAG, "getExifAttribute: Replacing TAG_ISO_SPEED_RATINGS with "
+                        + "TAG_PHOTOGRAPHIC_SENSITIVITY.");
+            }
+            tag = TAG_PHOTOGRAPHIC_SENSITIVITY;
+        }
+        // Retrieves all tag groups. The value from primary image tag group has a higher priority
+        // than the value from the thumbnail tag group if there are more than one candidates.
+        for (int i = 0; i < EXIF_TAGS.length; ++i) {
+            ExifAttribute value = mAttributes.get(i).get(tag);
+            if (value != null) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Generates an empty builder suitable for generating ExifData for JPEG from the current device.
+     */
+    @NonNull
+    public static Builder builderForDevice() {
+        // Add PRIMARY defaults. EXIF and GPS defaults will be added in build()
+        return new Builder(ByteOrder.BIG_ENDIAN)
+                .setAttribute(TAG_ORIENTATION, String.valueOf(ORIENTATION_NORMAL))
+                .setAttribute(TAG_X_RESOLUTION, "72/1")
+                .setAttribute(TAG_Y_RESOLUTION, "72/1")
+                .setAttribute(TAG_RESOLUTION_UNIT, String.valueOf(RESOLUTION_UNIT_INCHES))
+                .setAttribute(TAG_Y_CB_CR_POSITIONING,
+                        String.valueOf(Y_CB_CR_POSITIONING_CENTERED))
+                // Defaults derived from device
+                .setAttribute(TAG_MAKE, Build.MANUFACTURER)
+                .setAttribute(TAG_MODEL, Build.MODEL);
+    }
+
+    /**
+     * Builder for the {@link ExifData} class.
+     */
+    public static final class Builder {
+        // Pattern to check gps timestamp
+        private static final Pattern GPS_TIMESTAMP_PATTERN =
+                Pattern.compile("^(\\d{2}):(\\d{2}):(\\d{2})$");
+        // Pattern to check date time primary format (e.g. 2020:01:01 00:00:00)
+        private static final Pattern DATETIME_PRIMARY_FORMAT_PATTERN =
+                Pattern.compile("^(\\d{4}):(\\d{2}):(\\d{2})\\s(\\d{2}):(\\d{2}):(\\d{2})$");
+        // Pattern to check date time secondary format (e.g. 2020-01-01 00:00:00)
+        private static final Pattern DATETIME_SECONDARY_FORMAT_PATTERN =
+                Pattern.compile("^(\\d{4})-(\\d{2})-(\\d{2})\\s(\\d{2}):(\\d{2}):(\\d{2})$");
+        private static final int DATETIME_VALUE_STRING_LENGTH = 19;
+
+        // Mappings from tag name to tag number and each item represents one IFD tag group.
+        static final List<HashMap<String, ExifTag>> sExifTagMapsForWriting =
+                Collections.list(new Enumeration<HashMap<String, ExifTag>>() {
+                    int mIfdIndex = 0;
+
+                    @Override
+                    public boolean hasMoreElements() {
+                        return mIfdIndex < EXIF_TAGS.length;
+                    }
+
+                    @Override
+                    public HashMap<String, ExifTag> nextElement() {
+                        // Build up the hash tables to look up Exif tags for writing Exif tags.
+                        HashMap<String, ExifTag> map = new HashMap<>();
+                        for (ExifTag tag : EXIF_TAGS[mIfdIndex]) {
+                            map.put(tag.name, tag);
+                        }
+                        mIfdIndex++;
+                        return map;
+                    }
+                });
+
+        final List<Map<String, ExifAttribute>> mAttributes = Collections.list(
+                new Enumeration<Map<String, ExifAttribute>>() {
+                    int mIfdIndex = 0;
+
+                    @Override
+                    public boolean hasMoreElements() {
+                        return mIfdIndex < EXIF_TAGS.length;
+                    }
+
+                    @Override
+                    public Map<String, ExifAttribute> nextElement() {
+                        mIfdIndex++;
+                        return new HashMap<>();
+                    }
+                });
+        private final ByteOrder mByteOrder;
+
+        Builder(@NonNull ByteOrder byteOrder) {
+            mByteOrder = byteOrder;
+        }
+
+        /**
+         * Sets the width of the image.
+         *
+         * @param width the width of the image.
+         */
+        @NonNull
+        public Builder setImageWidth(int width) {
+            return setAttribute(TAG_IMAGE_WIDTH, String.valueOf(width));
+        }
+
+        /**
+         * Sets the height of the image.
+         *
+         * @param height the height of the image.
+         */
+        @NonNull
+        public Builder setImageHeight(int height) {
+            return setAttribute(TAG_IMAGE_LENGTH, String.valueOf(height));
+        }
+
+        /**
+         * Sets the orientation of the image in degrees.
+         *
+         * @param orientationDegrees the orientation in degrees. Can be one of (0, 90, 180, 270)
+         */
+        @NonNull
+        public Builder setOrientationDegrees(int orientationDegrees) {
+            int orientationEnum;
+            switch (orientationDegrees) {
+                case 0:
+                    orientationEnum = ExifInterface.ORIENTATION_NORMAL;
+                    break;
+                case 90:
+                    orientationEnum = ExifInterface.ORIENTATION_ROTATE_90;
+                    break;
+                case 180:
+                    orientationEnum = ExifInterface.ORIENTATION_ROTATE_180;
+                    break;
+                case 270:
+                    orientationEnum = ExifInterface.ORIENTATION_ROTATE_270;
+                    break;
+                default:
+                    Logger.w(TAG,
+                            "Unexpected orientation value: " + orientationDegrees
+                                    + ". Must be one of 0, 90, 180, 270.");
+                    orientationEnum = ExifInterface.ORIENTATION_UNDEFINED;
+                    break;
+            }
+            return setAttribute(TAG_ORIENTATION, String.valueOf(orientationEnum));
+        }
+
+        /**
+         * Sets the flash information from
+         * {@link androidx.camera.core.impl.CameraCaptureMetaData.FlashState}.
+         *
+         * @param flashState the state of the flash at capture time.
+         */
+        @NonNull
+        public Builder setFlashState(@NonNull CameraCaptureMetaData.FlashState flashState) {
+            if (flashState == CameraCaptureMetaData.FlashState.UNKNOWN) {
+                // Cannot set flash state information
+                return this;
+            }
+
+            short value;
+            switch (flashState) {
+                case READY:
+                    value = 0;
+                    break;
+                case NONE:
+                    value = FLAG_FLASH_NO_FLASH_FUNCTION;
+                    break;
+                case FIRED:
+                    value = FLAG_FLASH_FIRED;
+                    break;
+                default:
+                    Logger.w(TAG, "Unknown flash state: " + flashState);
+                    return this;
+            }
+
+            if ((value & FLAG_FLASH_FIRED) == FLAG_FLASH_FIRED) {
+                // Set light source to flash
+                setAttribute(TAG_LIGHT_SOURCE, String.valueOf(LIGHT_SOURCE_FLASH));
+            }
+
+
+            return setAttribute(TAG_FLASH, String.valueOf(value));
+        }
+
+        /**
+         * Sets the amount of time the sensor was exposed for, in nanoseconds.
+         * @param exposureTimeNs The exposure time in nanoseconds.
+         */
+        @NonNull
+        public Builder setExposureTimeNanos(long exposureTimeNs) {
+            return setAttribute(TAG_EXPOSURE_TIME,
+                    String.valueOf(exposureTimeNs / (double) TimeUnit.SECONDS.toNanos(1)));
+        }
+
+        /**
+         * Sets the lens f-number.
+         *
+         * <p>The lens f-number has precision 1.xx, for example, 1.80.
+         * @param fNumber The f-number.
+         */
+        @NonNull
+        public Builder setLensFNumber(float fNumber) {
+            return setAttribute(TAG_F_NUMBER, String.valueOf(fNumber));
+        }
+
+        /**
+         * Sets the ISO.
+         *
+         * @param iso the standard ISO sensitivity value, as defined in ISO 12232:2006.
+         */
+        @NonNull
+        public Builder setIso(int iso) {
+            return setAttribute(TAG_SENSITIVITY_TYPE, String.valueOf(SENSITIVITY_TYPE_ISO_SPEED))
+                    .setAttribute(TAG_PHOTOGRAPHIC_SENSITIVITY, String.valueOf(Math.min(65535,
+                            iso)));
+        }
+
+        /**
+         * Sets lens focal length, in millimeters.
+         *
+         * @param focalLength The lens focal length in millimeters.
+         */
+        @NonNull
+        public Builder setFocalLength(float focalLength) {
+            LongRational focalLengthRational =
+                    new LongRational((long) (focalLength * MM_IN_MICRONS), MM_IN_MICRONS);
+            return setAttribute(TAG_FOCAL_LENGTH, focalLengthRational.toString());
+        }
+
+        /**
+         * Sets the white balance mode.
+         *
+         * @param whiteBalanceMode The white balance mode. One of {@link WhiteBalanceMode#AUTO}
+         *                        or {@link WhiteBalanceMode#MANUAL}.
+         */
+        @NonNull
+        public Builder setWhiteBalanceMode(@NonNull WhiteBalanceMode whiteBalanceMode) {
+            String wbString = null;
+            switch (whiteBalanceMode) {
+                case AUTO:
+                    wbString = String.valueOf(WHITE_BALANCE_AUTO);
+                    break;
+                case MANUAL:
+                    wbString = String.valueOf(WHITE_BALANCE_MANUAL);
+                    break;
+            }
+            return setAttribute(TAG_WHITE_BALANCE, wbString);
+        }
+
+        /**
+         * Sets the value of the specified tag.
+         *
+         * @param tag   the name of the tag.
+         * @param value the value of the tag.
+         */
+        @NonNull
+        public Builder setAttribute(@NonNull String tag, @NonNull String value) {
+            setAttributeInternal(tag, value, mAttributes);
+            return this;
+        }
+
+        /**
+         * Removes the attribute with the given tag.
+         *
+         * @param tag the name of the tag.
+         */
+        @NonNull
+        public Builder removeAttribute(@NonNull String tag) {
+            setAttributeInternal(tag, null, mAttributes);
+            return this;
+        }
+
+        private void setAttributeIfMissing(@NonNull String tag, @NonNull String value,
+                @NonNull List<Map<String, ExifAttribute>> attributes) {
+            for (Map<String, ExifAttribute> attrs : attributes) {
+                if (attrs.containsKey(tag)) {
+                    // Attr already exists
+                    return;
+                }
+            }
+
+            // Add missing attribute.
+            setAttributeInternal(tag, value, attributes);
+        }
+
+        @SuppressWarnings("deprecation")
+        // Allows null values to remove attributes
+        private void setAttributeInternal(@NonNull String tag, @Nullable String value,
+                @NonNull List<Map<String, ExifAttribute>> attributes) {
+            // Validate and convert if necessary.
+            if (TAG_DATETIME.equals(tag) || TAG_DATETIME_ORIGINAL.equals(tag)
+                    || TAG_DATETIME_DIGITIZED.equals(tag)) {
+                if (value != null) {
+                    boolean isPrimaryFormat = DATETIME_PRIMARY_FORMAT_PATTERN.matcher(value).find();
+                    boolean isSecondaryFormat = DATETIME_SECONDARY_FORMAT_PATTERN.matcher(
+                            value).find();
+                    // Validate
+                    if (value.length() != DATETIME_VALUE_STRING_LENGTH
+                            || (!isPrimaryFormat && !isSecondaryFormat)) {
+                        Logger.w(TAG, "Invalid value for " + tag + " : " + value);
+                        return;
+                    }
+                    // If datetime value has secondary format (e.g. 2020-01-01 00:00:00), convert it
+                    // to primary format (e.g. 2020:01:01 00:00:00) since it is the format in the
+                    // official documentation.
+                    // See JEITA CP-3451C Section 4.6.4. D. Other Tags, DateTime
+                    if (isSecondaryFormat) {
+                        // Replace "-" with ":" to match the primary format.
+                        value = value.replaceAll("-", ":");
+                    }
+                }
+            }
+            // Maintain compatibility.
+            if (TAG_ISO_SPEED_RATINGS.equals(tag)) {
+                if (DEBUG) {
+                    Logger.d(TAG, "setAttribute: Replacing TAG_ISO_SPEED_RATINGS with "
+                            + "TAG_PHOTOGRAPHIC_SENSITIVITY.");
+                }
+                tag = TAG_PHOTOGRAPHIC_SENSITIVITY;
+            }
+            // Convert the given value to rational values for backwards compatibility.
+            if (value != null && sTagSetForCompatibility.contains(tag)) {
+                if (tag.equals(TAG_GPS_TIMESTAMP)) {
+                    Matcher m = GPS_TIMESTAMP_PATTERN.matcher(value);
+                    if (!m.find()) {
+                        Logger.w(TAG, "Invalid value for " + tag + " : " + value);
+                        return;
+                    }
+                    value = Integer.parseInt(Preconditions.checkNotNull(m.group(1))) + "/1,"
+                            + Integer.parseInt(Preconditions.checkNotNull(m.group(2))) + "/1,"
+                            + Integer.parseInt(Preconditions.checkNotNull(m.group(3))) + "/1";
+                } else {
+                    try {
+                        double doubleValue = Double.parseDouble(value);
+                        value = new LongRational(doubleValue).toString();
+                    } catch (NumberFormatException e) {
+                        Logger.w(TAG, "Invalid value for " + tag + " : " + value, e);
+                        return;
+                    }
+                }
+            }
+
+            for (int i = 0; i < EXIF_TAGS.length; ++i) {
+                final ExifTag exifTag = sExifTagMapsForWriting.get(i).get(tag);
+                if (exifTag != null) {
+                    if (value == null) {
+                        attributes.get(i).remove(tag);
+                        continue;
+                    }
+                    Pair<Integer, Integer> guess = guessDataFormat(value);
+                    int dataFormat;
+                    if (exifTag.primaryFormat == guess.first
+                            || exifTag.primaryFormat == guess.second) {
+                        dataFormat = exifTag.primaryFormat;
+                    } else if (exifTag.secondaryFormat != -1 && (
+                            exifTag.secondaryFormat == guess.first
+                                    || exifTag.secondaryFormat == guess.second)) {
+                        dataFormat = exifTag.secondaryFormat;
+                    } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE
+                            || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED
+                            || exifTag.primaryFormat == IFD_FORMAT_STRING) {
+                        dataFormat = exifTag.primaryFormat;
+                    } else {
+                        if (DEBUG) {
+                            Logger.d(TAG, "Given tag (" + tag
+                                    + ") value didn't match with one of expected "
+                                    + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
+                                    + (exifTag.secondaryFormat == -1 ? "" : ", "
+                                    + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
+                                    + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? ""
+                                    : ", "
+                                            + IFD_FORMAT_NAMES[guess.second]) + ")");
+                        }
+                        continue;
+                    }
+                    switch (dataFormat) {
+                        case IFD_FORMAT_BYTE: {
+                            attributes.get(i).put(tag, ExifAttribute.createByte(value));
+                            break;
+                        }
+                        case IFD_FORMAT_UNDEFINED:
+                        case IFD_FORMAT_STRING: {
+                            attributes.get(i).put(tag, ExifAttribute.createString(value));
+                            break;
+                        }
+                        case IFD_FORMAT_USHORT: {
+                            final String[] values = value.split(",", -1);
+                            final int[] intArray = new int[values.length];
+                            for (int j = 0; j < values.length; ++j) {
+                                intArray[j] = Integer.parseInt(values[j]);
+                            }
+                            attributes.get(i).put(tag,
+                                    ExifAttribute.createUShort(intArray, mByteOrder));
+                            break;
+                        }
+                        case IFD_FORMAT_SLONG: {
+                            final String[] values = value.split(",", -1);
+                            final int[] intArray = new int[values.length];
+                            for (int j = 0; j < values.length; ++j) {
+                                intArray[j] = Integer.parseInt(values[j]);
+                            }
+                            attributes.get(i).put(tag,
+                                    ExifAttribute.createSLong(intArray, mByteOrder));
+                            break;
+                        }
+                        case IFD_FORMAT_ULONG: {
+                            final String[] values = value.split(",", -1);
+                            final long[] longArray = new long[values.length];
+                            for (int j = 0; j < values.length; ++j) {
+                                longArray[j] = Long.parseLong(values[j]);
+                            }
+                            attributes.get(i).put(tag,
+                                    ExifAttribute.createULong(longArray, mByteOrder));
+                            break;
+                        }
+                        case IFD_FORMAT_URATIONAL: {
+                            final String[] values = value.split(",", -1);
+                            final LongRational[] rationalArray = new LongRational[values.length];
+                            for (int j = 0; j < values.length; ++j) {
+                                final String[] numbers = values[j].split("/", -1);
+                                rationalArray[j] = new LongRational(
+                                        (long) Double.parseDouble(numbers[0]),
+                                        (long) Double.parseDouble(numbers[1]));
+                            }
+                            attributes.get(i).put(tag,
+                                    ExifAttribute.createURational(rationalArray, mByteOrder));
+                            break;
+                        }
+                        case IFD_FORMAT_SRATIONAL: {
+                            final String[] values = value.split(",", -1);
+                            final LongRational[] rationalArray = new LongRational[values.length];
+                            for (int j = 0; j < values.length; ++j) {
+                                final String[] numbers = values[j].split("/", -1);
+                                rationalArray[j] = new LongRational(
+                                        (long) Double.parseDouble(numbers[0]),
+                                        (long) Double.parseDouble(numbers[1]));
+                            }
+                            attributes.get(i).put(tag,
+                                    ExifAttribute.createSRational(rationalArray, mByteOrder));
+                            break;
+                        }
+                        case IFD_FORMAT_DOUBLE: {
+                            final String[] values = value.split(",", -1);
+                            final double[] doubleArray = new double[values.length];
+                            for (int j = 0; j < values.length; ++j) {
+                                doubleArray[j] = Double.parseDouble(values[j]);
+                            }
+                            attributes.get(i).put(tag,
+                                    ExifAttribute.createDouble(doubleArray, mByteOrder));
+                            break;
+                        }
+                        default:
+                            if (DEBUG) {
+                                Logger.d(TAG,
+                                        "Data format isn't one of expected formats: " + dataFormat);
+                            }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Builds an {@link ExifData} from the current state of the builder.
+         */
+        @NonNull
+        public ExifData build() {
+            // Create a read-only copy of all attributes. This needs to be a deep copy since
+            // build() can be called multiple times. We'll remove null values as well.
+            List<Map<String, ExifAttribute>> attributes = Collections.list(
+                    new Enumeration<Map<String, ExifAttribute>>() {
+                        final Enumeration<Map<String, ExifAttribute>> mMapEnumeration =
+                                Collections.enumeration(mAttributes);
+
+                        @Override
+                        public boolean hasMoreElements() {
+                            return mMapEnumeration.hasMoreElements();
+                        }
+
+                        @Override
+                        public Map<String, ExifAttribute> nextElement() {
+                            return new HashMap<>(mMapEnumeration.nextElement());
+                        }
+                    });
+            // Add EXIF defaults if needed
+            if (!attributes.get(IFD_TYPE_EXIF).isEmpty()) {
+                setAttributeIfMissing(TAG_EXPOSURE_PROGRAM,
+                        String.valueOf(EXPOSURE_PROGRAM_NOT_DEFINED), attributes);
+                setAttributeIfMissing(TAG_EXIF_VERSION, "0230", attributes);
+                // Default is for YCbCr components
+                setAttributeIfMissing(TAG_COMPONENTS_CONFIGURATION, "1,2,3,0", attributes);
+                setAttributeIfMissing(TAG_METERING_MODE, String.valueOf(METERING_MODE_UNKNOWN),
+                        attributes);
+                setAttributeIfMissing(TAG_LIGHT_SOURCE, String.valueOf(LIGHT_SOURCE_UNKNOWN),
+                        attributes);
+                setAttributeIfMissing(TAG_FLASHPIX_VERSION, "0100", attributes);
+                setAttributeIfMissing(TAG_FOCAL_PLANE_RESOLUTION_UNIT,
+                        String.valueOf(RESOLUTION_UNIT_INCHES), attributes);
+                setAttributeIfMissing(TAG_FILE_SOURCE, String.valueOf(FILE_SOURCE_DSC), attributes);
+                setAttributeIfMissing(TAG_SCENE_TYPE,
+                        String.valueOf(SCENE_TYPE_DIRECTLY_PHOTOGRAPHED), attributes);
+                setAttributeIfMissing(TAG_CUSTOM_RENDERED, String.valueOf(RENDERED_PROCESS_NORMAL),
+                        attributes);
+                setAttributeIfMissing(TAG_SCENE_CAPTURE_TYPE,
+                        String.valueOf(SCENE_CAPTURE_TYPE_STANDARD), attributes);
+                setAttributeIfMissing(TAG_CONTRAST, String.valueOf(CONTRAST_NORMAL), attributes);
+                setAttributeIfMissing(TAG_SATURATION, String.valueOf(SATURATION_NORMAL),
+                        attributes);
+                setAttributeIfMissing(TAG_SHARPNESS, String.valueOf(SHARPNESS_NORMAL), attributes);
+            }
+            // Add GPS defaults if needed
+            if (!attributes.get(IFD_TYPE_GPS).isEmpty()) {
+                setAttributeIfMissing(TAG_GPS_VERSION_ID, "2300", attributes);
+                setAttributeIfMissing(TAG_GPS_SPEED_REF, GPS_SPEED_KILOMETERS_PER_HOUR, attributes);
+                setAttributeIfMissing(TAG_GPS_TRACK_REF, GPS_DIRECTION_TRUE, attributes);
+                setAttributeIfMissing(TAG_GPS_IMG_DIRECTION_REF, GPS_DIRECTION_TRUE, attributes);
+                setAttributeIfMissing(TAG_GPS_DEST_BEARING_REF, GPS_DIRECTION_TRUE, attributes);
+                setAttributeIfMissing(TAG_GPS_DEST_DISTANCE_REF, GPS_DISTANCE_KILOMETERS,
+                        attributes);
+            }
+            return new ExifData(mByteOrder, attributes);
+        }
+
+        /**
+         * Determines the data format of EXIF entry value.
+         *
+         * @param entryValue The value to be determined.
+         * @return Returns two data formats guessed as a pair in integer. If there is no two
+         * candidate
+         * data formats for the given entry value, returns {@code -1} in the second of the pair.
+         */
+        private static Pair<Integer, Integer> guessDataFormat(String entryValue) {
+            // See TIFF 6.0 Section 2, "Image File Directory".
+            // Take the first component if there are more than one component.
+            if (entryValue.contains(",")) {
+                String[] entryValues = entryValue.split(",", -1);
+                Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]);
+                if (dataFormat.first == IFD_FORMAT_STRING) {
+                    return dataFormat;
+                }
+                for (int i = 1; i < entryValues.length; ++i) {
+                    final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]);
+                    int first = -1, second = -1;
+                    if (guessDataFormat.first.equals(dataFormat.first)
+                            || guessDataFormat.second.equals(dataFormat.first)) {
+                        first = dataFormat.first;
+                    }
+                    if (dataFormat.second != -1 && (guessDataFormat.first.equals(dataFormat.second)
+                            || guessDataFormat.second.equals(dataFormat.second))) {
+                        second = dataFormat.second;
+                    }
+                    if (first == -1 && second == -1) {
+                        return new Pair<>(IFD_FORMAT_STRING, -1);
+                    }
+                    if (first == -1) {
+                        dataFormat = new Pair<>(second, -1);
+                        continue;
+                    }
+                    if (second == -1) {
+                        dataFormat = new Pair<>(first, -1);
+                    }
+                }
+                return dataFormat;
+            }
+
+            if (entryValue.contains("/")) {
+                String[] rationalNumber = entryValue.split("/", -1);
+                if (rationalNumber.length == 2) {
+                    try {
+                        long numerator = (long) Double.parseDouble(rationalNumber[0]);
+                        long denominator = (long) Double.parseDouble(rationalNumber[1]);
+                        if (numerator < 0L || denominator < 0L) {
+                            return new Pair<>(IFD_FORMAT_SRATIONAL, -1);
+                        }
+                        if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) {
+                            return new Pair<>(IFD_FORMAT_URATIONAL, -1);
+                        }
+                        return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL);
+                    } catch (NumberFormatException e) {
+                        // Ignored
+                    }
+                }
+                return new Pair<>(IFD_FORMAT_STRING, -1);
+            }
+            try {
+                long longValue = Long.parseLong(entryValue);
+                if (longValue >= 0 && longValue <= 65535) {
+                    return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG);
+                }
+                if (longValue < 0) {
+                    return new Pair<>(IFD_FORMAT_SLONG, -1);
+                }
+                return new Pair<>(IFD_FORMAT_ULONG, -1);
+            } catch (NumberFormatException e) {
+                // Ignored
+            }
+            try {
+                Double.parseDouble(entryValue);
+                return new Pair<>(IFD_FORMAT_DOUBLE, -1);
+            } catch (NumberFormatException e) {
+                // Ignored
+            }
+            return new Pair<>(IFD_FORMAT_STRING, -1);
+        }
+    }
+}
+
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifOutputStream.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifOutputStream.java
new file mode 100644
index 0000000..a2594fd
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifOutputStream.java
@@ -0,0 +1,387 @@
+/*
+ * 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.core.impl.utils;
+
+import static androidx.camera.core.impl.utils.ExifAttribute.ASCII;
+import static androidx.camera.core.impl.utils.ExifData.Builder.sExifTagMapsForWriting;
+import static androidx.camera.core.impl.utils.ExifData.EXIF_POINTER_TAGS;
+import static androidx.camera.core.impl.utils.ExifData.EXIF_TAGS;
+import static androidx.camera.core.impl.utils.ExifData.IFD_TYPE_EXIF;
+import static androidx.camera.core.impl.utils.ExifData.IFD_TYPE_GPS;
+import static androidx.camera.core.impl.utils.ExifData.IFD_TYPE_INTEROPERABILITY;
+import static androidx.camera.core.impl.utils.ExifData.IFD_TYPE_PRIMARY;
+
+import androidx.annotation.NonNull;
+import androidx.camera.core.Logger;
+import androidx.core.util.Preconditions;
+
+import java.io.BufferedOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * This class provides a way to replace the Exif header of a JPEG image.
+ * <p>
+ * Below is an example of writing EXIF data into a file
+ *
+ * <pre>
+ * public static void writeExif(byte[] jpeg, ExifData exif, String path) {
+ *     OutputStream os = null;
+ *     try {
+ *         os = new FileOutputStream(path);
+ *         // Set the exif header on the output stream
+ *         ExifOutputStream eos = new ExifOutputStream(os, exif);
+ *         // Write the original jpeg out, the header will be added into the file.
+ *         eos.write(jpeg);
+ *     } catch (FileNotFoundException e) {
+ *         e.printStackTrace();
+ *     } catch (IOException e) {
+ *         e.printStackTrace();
+ *     } finally {
+ *         if (os != null) {
+ *             try {
+ *                 os.close();
+ *             } catch (IOException e) {
+ *                 e.printStackTrace();
+ *             }
+ *         }
+ *     }
+ * }
+ * </pre>
+ */
+public final class ExifOutputStream extends FilterOutputStream {
+    private static final String TAG = "ExifOutputStream";
+    private static final boolean DEBUG = false;
+    private static final int STREAMBUFFER_SIZE = 0x00010000; // 64Kb
+
+    private static final int STATE_SOI = 0;
+    private static final int STATE_FRAME_HEADER = 1;
+    private static final int STATE_JPEG_DATA = 2;
+
+    // Identifier for EXIF APP1 segment in JPEG
+    private static final byte[] IDENTIFIER_EXIF_APP1 = "Exif\0\0".getBytes(ASCII);
+
+    // Types of Exif byte alignments (see JEITA CP-3451C Section 4.5.2)
+    private static final short BYTE_ALIGN_II = 0x4949;  // II: Intel order
+    private static final short BYTE_ALIGN_MM = 0x4d4d;  // MM: Motorola order
+
+    // TIFF Header Fixed Constant (see JEITA CP-3451C Section 4.5.2)
+    private static final byte START_CODE = 0x2a; // 42
+    private static final int IFD_OFFSET = 8;
+
+    private final ExifData mExifData;
+    private final byte[] mSingleByteArray = new byte[1];
+    private final ByteBuffer mBuffer = ByteBuffer.allocate(4);
+    private int mState = STATE_SOI;
+    private int mByteToSkip;
+    private int mByteToCopy;
+
+    /**
+     * Creates an ExifOutputStream that wraps the given {@link OutputStream} and overwrites exif
+     * with the provided {@link ExifData}.
+     * @param ou OutputStream which will be sent the final output.
+     * @param exifData Exif data which will overwrite any exif data sent to this stream.
+     */
+    public ExifOutputStream(@NonNull OutputStream ou, @NonNull ExifData exifData) {
+        super(new BufferedOutputStream(ou, STREAMBUFFER_SIZE));
+        mExifData = exifData;
+    }
+
+    private int requestByteToBuffer(int requestByteCount, byte[] buffer, int offset, int length) {
+        int byteNeeded = requestByteCount - mBuffer.position();
+        int byteToRead = Math.min(length, byteNeeded);
+        mBuffer.put(buffer, offset, byteToRead);
+        return byteToRead;
+    }
+
+    /**
+     * Writes the image out. The input data should be a valid JPEG format. After
+     * writing, it's Exif header will be replaced by the given header.
+     */
+    @Override
+    public void write(@NonNull byte[] buffer, int offset, int length) throws IOException {
+        while ((mByteToSkip > 0 || mByteToCopy > 0 || mState != STATE_JPEG_DATA)
+                && length > 0) {
+            if (mByteToSkip > 0) {
+                int byteToProcess = Math.min(length, mByteToSkip);
+                length -= byteToProcess;
+                mByteToSkip -= byteToProcess;
+                offset += byteToProcess;
+            }
+            if (mByteToCopy > 0) {
+                int byteToProcess = Math.min(length, mByteToCopy);
+                out.write(buffer, offset, byteToProcess);
+                length -= byteToProcess;
+                mByteToCopy -= byteToProcess;
+                offset += byteToProcess;
+            }
+            if (length == 0) {
+                return;
+            }
+            switch (mState) {
+                case STATE_SOI:
+                    int byteRead = requestByteToBuffer(2, buffer, offset, length);
+                    offset += byteRead;
+                    length -= byteRead;
+                    if (mBuffer.position() < 2) {
+                        return;
+                    }
+                    mBuffer.rewind();
+                    if (mBuffer.getShort() != JpegHeader.SOI) {
+                        throw new IOException("Not a valid jpeg image, cannot write exif");
+                    }
+                    out.write(mBuffer.array(), 0, 2);
+                    mState = STATE_FRAME_HEADER;
+                    mBuffer.rewind();
+                    ByteOrderedDataOutputStream dataOutputStream =
+                            new ByteOrderedDataOutputStream(out, ByteOrder.BIG_ENDIAN);
+                    dataOutputStream.writeShort(JpegHeader.APP1);
+                    writeExifSegment(dataOutputStream);
+                    break;
+                case STATE_FRAME_HEADER:
+                    // We ignore the APP1 segment and copy all other segments
+                    // until SOF tag.
+                    byteRead = requestByteToBuffer(4, buffer, offset, length);
+                    offset += byteRead;
+                    length -= byteRead;
+                    // Check if this image data doesn't contain SOF.
+                    if (mBuffer.position() == 2) {
+                        short tag = mBuffer.getShort();
+                        if (tag == JpegHeader.EOI) {
+                            out.write(mBuffer.array(), 0, 2);
+                            mBuffer.rewind();
+                        }
+                    }
+                    if (mBuffer.position() < 4) {
+                        return;
+                    }
+                    mBuffer.rewind();
+                    short marker = mBuffer.getShort();
+                    if (marker == JpegHeader.APP1) {
+                        mByteToSkip = (mBuffer.getShort() & 0x0000ffff) - 2;
+                        mState = STATE_JPEG_DATA;
+                    } else if (!JpegHeader.isSofMarker(marker)) {
+                        out.write(mBuffer.array(), 0, 4);
+                        mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2;
+                    } else {
+                        out.write(mBuffer.array(), 0, 4);
+                        mState = STATE_JPEG_DATA;
+                    }
+                    mBuffer.rewind();
+            }
+        }
+        if (length > 0) {
+            out.write(buffer, offset, length);
+        }
+    }
+
+    /**
+     * Writes the one bytes out. The input data should be a valid JPEG format.
+     * After writing, it's Exif header will be replaced by the given header.
+     */
+    @Override
+    public void write(int oneByte) throws IOException {
+        mSingleByteArray[0] = (byte) (0xff & oneByte);
+        write(mSingleByteArray);
+    }
+
+    /**
+     * Equivalent to calling write(buffer, 0, buffer.length).
+     */
+    @Override
+    public void write(@NonNull byte[] buffer) throws IOException {
+        write(buffer, 0, buffer.length);
+    }
+
+    // Writes an Exif segment into the given output stream.
+    private void writeExifSegment(@NonNull ByteOrderedDataOutputStream dataOutputStream)
+            throws IOException {
+        // The following variables are for calculating each IFD tag group size in bytes.
+        int[] ifdOffsets = new int[EXIF_TAGS.length];
+        int[] ifdDataSizes = new int[EXIF_TAGS.length];
+
+        // Remove IFD pointer tags (we'll re-add it later.)
+        for (ExifTag tag : EXIF_POINTER_TAGS) {
+            for (int ifdIndex = 0; ifdIndex < EXIF_TAGS.length; ++ifdIndex) {
+                mExifData.getAttributes(ifdIndex).remove(tag.name);
+            }
+        }
+
+        // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
+        // offset when there is one or more tags in the thumbnail IFD.
+        if (!mExifData.getAttributes(IFD_TYPE_EXIF).isEmpty()) {
+            mExifData.getAttributes(IFD_TYPE_PRIMARY).put(EXIF_POINTER_TAGS[1].name,
+                    ExifAttribute.createULong(0, mExifData.getByteOrder()));
+        }
+        if (!mExifData.getAttributes(IFD_TYPE_GPS).isEmpty()) {
+            mExifData.getAttributes(IFD_TYPE_PRIMARY).put(EXIF_POINTER_TAGS[2].name,
+                    ExifAttribute.createULong(0, mExifData.getByteOrder()));
+        }
+        if (!mExifData.getAttributes(IFD_TYPE_INTEROPERABILITY).isEmpty()) {
+            mExifData.getAttributes(IFD_TYPE_EXIF).put(EXIF_POINTER_TAGS[3].name,
+                    ExifAttribute.createULong(0, mExifData.getByteOrder()));
+        }
+
+        // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
+        // value which has a bigger size than 4 bytes.
+        for (int i = 0; i < EXIF_TAGS.length; ++i) {
+            int sum = 0;
+            for (Map.Entry<String, ExifAttribute> entry : mExifData.getAttributes(i).entrySet()) {
+                final ExifAttribute exifAttribute = entry.getValue();
+                final int size = exifAttribute.size();
+                if (size > 4) {
+                    sum += size;
+                }
+            }
+            ifdDataSizes[i] += sum;
+        }
+
+        // Calculate IFD offsets.
+        // 8 bytes are for TIFF headers: 2 bytes (byte order) + 2 bytes (identifier) + 4 bytes
+        // (offset of IFDs)
+        int position = 8;
+        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
+            if (!mExifData.getAttributes(ifdType).isEmpty()) {
+                ifdOffsets[ifdType] = position;
+                position += 2 + mExifData.getAttributes(ifdType).size() * 12 + 4
+                        + ifdDataSizes[ifdType];
+            }
+        }
+
+        int totalSize = position;
+        // Add 8 bytes for APP1 size and identifier data
+        totalSize += 8;
+        if (DEBUG) {
+            for (int i = 0; i < EXIF_TAGS.length; ++i) {
+                Logger.d(TAG, String.format(Locale.US, "index: %d, offsets: %d, tag count: %d, "
+                                + "data sizes: %d, total size: %d", i, ifdOffsets[i],
+                        mExifData.getAttributes(i).size(),
+                        ifdDataSizes[i], totalSize));
+            }
+        }
+
+        // Update IFD pointer tags with the calculated offsets.
+        if (!mExifData.getAttributes(IFD_TYPE_EXIF).isEmpty()) {
+            mExifData.getAttributes(IFD_TYPE_PRIMARY).put(EXIF_POINTER_TAGS[1].name,
+                    ExifAttribute.createULong(ifdOffsets[IFD_TYPE_EXIF], mExifData.getByteOrder()));
+        }
+        if (!mExifData.getAttributes(IFD_TYPE_GPS).isEmpty()) {
+            mExifData.getAttributes(IFD_TYPE_PRIMARY).put(EXIF_POINTER_TAGS[2].name,
+                    ExifAttribute.createULong(ifdOffsets[IFD_TYPE_GPS], mExifData.getByteOrder()));
+        }
+        if (!mExifData.getAttributes(IFD_TYPE_INTEROPERABILITY).isEmpty()) {
+            mExifData.getAttributes(IFD_TYPE_EXIF).put(EXIF_POINTER_TAGS[3].name,
+                    ExifAttribute.createULong(
+                            ifdOffsets[IFD_TYPE_INTEROPERABILITY], mExifData.getByteOrder()));
+        }
+
+        // Write JPEG specific data (APP1 size, APP1 identifier)
+        dataOutputStream.writeUnsignedShort(totalSize);
+        dataOutputStream.write(IDENTIFIER_EXIF_APP1);
+
+        // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
+        dataOutputStream.writeShort(mExifData.getByteOrder() == ByteOrder.BIG_ENDIAN
+                ? BYTE_ALIGN_MM : BYTE_ALIGN_II);
+        dataOutputStream.setByteOrder(mExifData.getByteOrder());
+        dataOutputStream.writeUnsignedShort(START_CODE);
+        dataOutputStream.writeUnsignedInt(IFD_OFFSET);
+
+        // Write IFD groups. See JEITA CP-3451C Section 4.5.8. Figure 9.
+        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
+            if (!mExifData.getAttributes(ifdType).isEmpty()) {
+                // See JEITA CP-3451C Section 4.6.2: IFD structure.
+                // Write entry count
+                dataOutputStream.writeUnsignedShort(mExifData.getAttributes(ifdType).size());
+
+                // Write entry info
+                int dataOffset = ifdOffsets[ifdType] + 2 + mExifData.getAttributes(ifdType).size()
+                        * 12 + 4;
+                for (Map.Entry<String, ExifAttribute> entry : mExifData.getAttributes(
+                        ifdType).entrySet()) {
+                    // Convert tag name to tag number.
+                    final ExifTag tag = sExifTagMapsForWriting.get(ifdType).get(entry.getKey());
+                    final int tagNumber =
+                            Preconditions.checkNotNull(tag,
+                                    "Tag not supported: " + entry.getKey() + ". Tag needs to be "
+                                            + "ported from ExifInterface to ExifData.").number;
+                    final ExifAttribute attribute = entry.getValue();
+                    final int size = attribute.size();
+
+                    dataOutputStream.writeUnsignedShort(tagNumber);
+                    dataOutputStream.writeUnsignedShort(attribute.format);
+                    dataOutputStream.writeInt(attribute.numberOfComponents);
+                    if (size > 4) {
+                        dataOutputStream.writeUnsignedInt(dataOffset);
+                        dataOffset += size;
+                    } else {
+                        dataOutputStream.write(attribute.bytes);
+                        // Fill zero up to 4 bytes
+                        if (size < 4) {
+                            for (int i = size; i < 4; ++i) {
+                                dataOutputStream.writeByte(0);
+                            }
+                        }
+                    }
+                }
+
+                // Write the next offset. Since we aren't handling thumbnails, this is just 0.
+                dataOutputStream.writeUnsignedInt(0);
+
+                // Write values of data field exceeding 4 bytes after the next offset.
+                for (Map.Entry<String, ExifAttribute> entry : mExifData.getAttributes(
+                        ifdType).entrySet()) {
+                    ExifAttribute attribute = entry.getValue();
+
+                    if (attribute.bytes.length > 4) {
+                        dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length);
+                    }
+                }
+            }
+        }
+
+        // Reset the byte order to big endian in order to write remaining parts of the JPEG file.
+        dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
+    }
+
+    static final class JpegHeader {
+        public static final short SOI =  (short) 0xFFD8;
+        public static final short APP1 = (short) 0xFFE1;
+        public static final short EOI = (short) 0xFFD9;
+
+        /**
+         *  SOF (start of frame). All value between SOF0 and SOF15 is SOF marker except for DHT,
+         *  JPG, and DAC marker.
+         */
+        public static final short SOF0 = (short) 0xFFC0;
+        public static final short SOF15 = (short) 0xFFCF;
+        public static final short DHT = (short) 0xFFC4;
+        public static final short JPG = (short) 0xFFC8;
+        public static final short DAC = (short) 0xFFCC;
+
+        public static boolean isSofMarker(short marker) {
+            return marker >= SOF0 && marker <= SOF15 && marker != DHT && marker != JPG
+                    && marker != DAC;
+        }
+
+        private JpegHeader() {}
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifTag.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifTag.java
new file mode 100644
index 0000000..c0df33b
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ExifTag.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.camera.core.impl.utils;
+
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_DOUBLE;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_SINGLE;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_SLONG;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_SSHORT;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_ULONG;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_UNDEFINED;
+import static androidx.camera.core.impl.utils.ExifAttribute.IFD_FORMAT_USHORT;
+
+import androidx.exifinterface.media.ExifInterface;
+
+/**
+ * This class stores information of an EXIF tag. For more information about
+ * defined EXIF tags, please read the Jeita EXIF 2.2 standard.
+ *
+ * This class was pulled from the {@link ExifInterface} class.
+ *
+ * @see ExifInterface
+ */
+class ExifTag {
+    public final int number;
+    public final String name;
+    public final int primaryFormat;
+    public final int secondaryFormat;
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    ExifTag(String name, int number, int format) {
+        this.name = name;
+        this.number = number;
+        this.primaryFormat = format;
+        this.secondaryFormat = -1;
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    ExifTag(String name, int number, int primaryFormat, int secondaryFormat) {
+        this.name = name;
+        this.number = number;
+        this.primaryFormat = primaryFormat;
+        this.secondaryFormat = secondaryFormat;
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    boolean isFormatCompatible(int format) {
+        if (primaryFormat == IFD_FORMAT_UNDEFINED || format == IFD_FORMAT_UNDEFINED) {
+            return true;
+        } else if (primaryFormat == format || secondaryFormat == format) {
+            return true;
+        } else if ((primaryFormat == IFD_FORMAT_ULONG || secondaryFormat == IFD_FORMAT_ULONG)
+                && format == IFD_FORMAT_USHORT) {
+            return true;
+        } else if ((primaryFormat == IFD_FORMAT_SLONG || secondaryFormat == IFD_FORMAT_SLONG)
+                && format == IFD_FORMAT_SSHORT) {
+            return true;
+        } else return (primaryFormat == IFD_FORMAT_DOUBLE || secondaryFormat == IFD_FORMAT_DOUBLE)
+                && format == IFD_FORMAT_SINGLE;
+    }
+}
+
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/LongRational.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/LongRational.java
new file mode 100644
index 0000000..3b6353d
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/LongRational.java
@@ -0,0 +1,72 @@
+/*
+ * 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.core.impl.utils;
+
+import androidx.annotation.NonNull;
+
+/**
+ * The rational data type of EXIF tag. Contains a pair of longs representing the
+ * numerator and denominator of a Rational number.
+ */
+final class LongRational {
+
+    private final long mNumerator;
+    private final long mDenominator;
+
+    /**
+     * Create a Rational with a given numerator and denominator.
+     */
+    LongRational(long nominator, long denominator) {
+        mNumerator = nominator;
+        mDenominator = denominator;
+    }
+
+    /**
+     * Creates a Rational from a double.
+     */
+    LongRational(double value) {
+        this((long) (value * 10000), 10000);
+    }
+
+    /**
+     * Gets the numerator of the rational.
+     */
+    long getNumerator() {
+        return mNumerator;
+    }
+
+    /**
+     * Gets the denominator of the rational
+     */
+    long getDenominator() {
+        return mDenominator;
+    }
+
+    /**
+     * Gets the rational value as type double. Will cause a divide-by-zero error
+     * if the denominator is 0.
+     */
+    double toDouble() {
+        return mNumerator / (double) mDenominator;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return mNumerator + "/" + mDenominator;
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraCaptureResultImageInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraCaptureResultImageInfo.java
index e951da0..ea4f5a5 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraCaptureResultImageInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraCaptureResultImageInfo.java
@@ -20,6 +20,7 @@
 import androidx.camera.core.ImageInfo;
 import androidx.camera.core.impl.CameraCaptureResult;
 import androidx.camera.core.impl.TagBundle;
+import androidx.camera.core.impl.utils.ExifData;
 
 /** An ImageInfo that is created by a {@link CameraCaptureResult}. */
 public final class CameraCaptureResultImageInfo implements ImageInfo {
@@ -46,6 +47,11 @@
         return 0;
     }
 
+    @Override
+    public void populateExifData(@NonNull ExifData.Builder exifBuilder) {
+        mCameraCaptureResult.populateExifData(exifBuilder);
+    }
+
     @NonNull
     public CameraCaptureResult getCameraCaptureResult() {
         return mCameraCaptureResult;
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 8d0754a..2c16a41 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
@@ -344,7 +344,8 @@
                 ConfigPair configPair = configPairMap.get(useCase);
                 // Combine with default configuration.
                 UseCaseConfig<?> combinedUseCaseConfig =
-                        useCase.mergeConfigs(configPair.mExtendedConfig, configPair.mCameraConfig);
+                        useCase.mergeConfigs(cameraInfoInternal, configPair.mExtendedConfig,
+                                configPair.mCameraConfig);
                 configToUseCaseMap.put(combinedUseCaseConfig, useCase);
             }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/YuvToJpegProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/YuvToJpegProcessor.java
new file mode 100644
index 0000000..40c4d50
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/YuvToJpegProcessor.java
@@ -0,0 +1,261 @@
+/*
+ * 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.core.internal;
+
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.graphics.YuvImage;
+import android.media.Image;
+import android.media.ImageWriter;
+import android.util.Size;
+import android.view.Surface;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.ImageProxy;
+import androidx.camera.core.Logger;
+import androidx.camera.core.impl.CaptureProcessor;
+import androidx.camera.core.impl.ImageProxyBundle;
+import androidx.camera.core.impl.utils.ExifData;
+import androidx.camera.core.impl.utils.ExifOutputStream;
+import androidx.camera.core.internal.compat.ImageWriterCompat;
+import androidx.camera.core.internal.utils.ImageUtil;
+import androidx.core.util.Preconditions;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * A CaptureProcessor which produces JPEGs from input YUV images.
+ */
+@RequiresApi(26)
+public class YuvToJpegProcessor implements CaptureProcessor {
+    private static final String TAG = "YuvToJpegProcessor";
+
+    private static final Rect UNINITIALIZED_RECT = new Rect(0, 0, 0, 0);
+
+    @IntRange(from = 0, to = 100)
+    private final int mQuality;
+    private final int mMaxImages;
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private boolean mClosed = false;
+    @GuardedBy("mLock")
+    private int mProcessingImages = 0;
+    @GuardedBy("mLock")
+    private ImageWriter mImageWriter;
+    @GuardedBy("mLock")
+    private Rect mImageRect = UNINITIALIZED_RECT;
+
+    public YuvToJpegProcessor(@IntRange(from = 0, to = 100) int quality, int maxImages) {
+        mQuality = quality;
+        mMaxImages = maxImages;
+    }
+
+    @Override
+    public void onOutputSurface(@NonNull Surface surface, int imageFormat) {
+        Preconditions.checkState(imageFormat == ImageFormat.JPEG, "YuvToJpegProcessor only "
+                + "supports JPEG output format.");
+        synchronized (mLock) {
+            if (!mClosed) {
+                if (mImageWriter != null) {
+                    throw new IllegalStateException("Output surface already set.");
+                }
+                mImageWriter = ImageWriterCompat.newInstance(surface, mMaxImages, imageFormat);
+            } else {
+                Logger.w(TAG, "Cannot set output surface. Processor is closed.");
+            }
+        }
+    }
+
+    @Override
+    public void process(@NonNull ImageProxyBundle bundle) {
+        List<Integer> ids = bundle.getCaptureIds();
+        Preconditions.checkArgument(ids.size() == 1,
+                "Processing image bundle have single capture id, but found " + ids.size());
+
+        ListenableFuture<ImageProxy> imageProxyListenableFuture = bundle.getImageProxy(ids.get(0));
+        Preconditions.checkArgument(imageProxyListenableFuture.isDone());
+
+        ImageWriter imageWriter;
+        Rect imageRect;
+        boolean processing;
+        synchronized (mLock) {
+            imageWriter = mImageWriter;
+            processing = !mClosed;
+            imageRect = mImageRect;
+            if (processing) {
+                mProcessingImages++;
+            }
+        }
+
+        ImageProxy imageProxy = null;
+        Image jpegImage = null;
+        try {
+            imageProxy = imageProxyListenableFuture.get();
+            if (!processing) {
+                Logger.w(TAG, "Image enqueued for processing on closed processor.");
+                imageProxy.close();
+                imageProxy = null;
+                return;
+            }
+
+            jpegImage = imageWriter.dequeueInputImage();
+
+            imageProxy = imageProxyListenableFuture.get();
+            Preconditions.checkState(imageProxy.getFormat() == ImageFormat.YUV_420_888,
+                    "Input image is not expected YUV_420_888 image format");
+            byte[] yuvBytes = ImageUtil.yuv_420_888toNv21(imageProxy);
+
+            YuvImage yuvImage = new YuvImage(yuvBytes, ImageFormat.NV21, imageProxy.getWidth(),
+                    imageProxy.getHeight(), null);
+
+            ByteBuffer jpegBuf = jpegImage.getPlanes()[0].getBuffer();
+            int initialPos = jpegBuf.position();
+            OutputStream os = new ExifOutputStream(new ByteBufferOutputStream(jpegBuf),
+                    getExifData(imageProxy));
+            yuvImage.compressToJpeg(imageRect, mQuality, os);
+
+            // Input can now be closed.
+            imageProxy.close();
+            imageProxy = null;
+
+            // Set limits on jpeg buffer and rewind
+            jpegBuf.limit(jpegBuf.position());
+            jpegBuf.position(initialPos);
+
+            // Enqueue the completed jpeg image
+            imageWriter.queueInputImage(jpegImage);
+            jpegImage = null;
+        } catch (InterruptedException | ExecutionException e) {
+            // InterruptedException should not be possible here since
+            // imageProxyListenableFuture.isDone() returned true, but we have to handle the
+            // exception case so bundle it with ExecutionException.
+            if (processing) {
+                Logger.e(TAG, "Failed to process YUV -> JPEG", e);
+                // Something went wrong attempting to retrieve ImageProxy. Enqueue an invalid buffer
+                // to make sure the downstream isn't blocked.
+                jpegImage = imageWriter.dequeueInputImage();
+                ByteBuffer jpegBuf = jpegImage.getPlanes()[0].getBuffer();
+                jpegBuf.rewind();
+                jpegBuf.limit(0);
+                imageWriter.queueInputImage(jpegImage);
+            }
+        } finally {
+            boolean shouldCloseImageWriter;
+            synchronized (mLock) {
+                // Note: order of condition is important here due to short circuit of &&
+                shouldCloseImageWriter = processing && (mProcessingImages-- == 0) && mClosed;
+            }
+
+            // Fallback in case something went wrong during processing.
+            if (jpegImage != null) {
+                jpegImage.close();
+            }
+            if (imageProxy != null) {
+                imageProxy.close();
+            }
+
+            if (shouldCloseImageWriter) {
+                imageWriter.close();
+                Logger.d(TAG, "Closed after completion of last image processed.");
+            }
+        }
+    }
+
+    /**
+     * Closes the YuvToJpegProcessor so that no more processing will occur.
+     *
+     * This should only be called once no more images will be produced for processing. Otherwise
+     * the images may not be propagated to the output surface and the pipeline could stall.
+     */
+    public void close() {
+        synchronized (mLock) {
+            if (!mClosed) {
+                mClosed = true;
+                // Close the ImageWriter if no images are currently processing. Otherwise the
+                // ImageWriter will be closed once the last image is closed.
+                if (mProcessingImages == 0 && mImageWriter != null) {
+                    Logger.d(TAG, "No processing in progress. Closing immediately.");
+                    mImageWriter.close();
+                } else {
+                    Logger.d(TAG, "close() called while processing. Will close after completion.");
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onResolutionUpdate(@NonNull Size size) {
+        synchronized (mLock) {
+            mImageRect = new Rect(0, 0, size.getWidth(), size.getHeight());
+        }
+    }
+
+    @NonNull
+    private static ExifData getExifData(@NonNull ImageProxy imageProxy) {
+        ExifData.Builder builder = ExifData.builderForDevice();
+        imageProxy.getImageInfo().populateExifData(builder);
+        return builder.setImageWidth(imageProxy.getWidth())
+                .setImageHeight(imageProxy.getHeight())
+                .build();
+    }
+
+    private static final class ByteBufferOutputStream extends OutputStream {
+
+        private final ByteBuffer mByteBuffer;
+
+        ByteBufferOutputStream(@NonNull ByteBuffer buf) {
+            mByteBuffer = buf;
+        }
+
+        @Override
+        public void write(int b) throws IOException {
+            if (!mByteBuffer.hasRemaining()) {
+                throw new EOFException("Output ByteBuffer has no bytes remaining.");
+            }
+
+            mByteBuffer.put((byte) b);
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) throws IOException {
+            if (b == null) {
+                throw new NullPointerException();
+            } else if ((off < 0) || (off > b.length) || (len < 0)
+                    || ((off + len) > b.length) || ((off + len) < 0)) {
+                throw new IndexOutOfBoundsException();
+            } else if (len == 0) {
+                return;
+            } else if (mByteBuffer.remaining() < len) {
+                throw new EOFException("Output ByteBuffer has insufficient bytes remaining.");
+            }
+
+            mByteBuffer.put(b, off, len);
+        }
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/ImageWriterCompat.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/ImageWriterCompat.java
new file mode 100644
index 0000000..773b1ad
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/ImageWriterCompat.java
@@ -0,0 +1,91 @@
+/*
+ * 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.core.internal.compat;
+
+import android.media.ImageWriter;
+import android.os.Build;
+import android.view.Surface;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+/**
+ * Helper for accessing features of {@link ImageWriter} in a backwards compatible fashion.
+ */
+@RequiresApi(26)
+public final class ImageWriterCompat {
+
+    /**
+     * <p>
+     * Create a new ImageWriter with given number of max Images and format.
+     * </p>
+     * <p>
+     * The {@code maxImages} parameter determines the maximum number of
+     * {@link android.media.Image} objects that can be be dequeued from the
+     * {@code ImageWriter} simultaneously. Requesting more buffers will use up
+     * more memory, so it is important to use only the minimum number necessary.
+     * </p>
+     * <p>
+     * The format specifies the image format of this ImageWriter. The format
+     * from the {@code surface} will be overridden with this format. For example,
+     * if the surface is obtained from a {@link android.graphics.SurfaceTexture}, the default
+     * format may be {@link android.graphics.PixelFormat#RGBA_8888}. If the application creates an
+     * ImageWriter with this surface and {@link android.graphics.ImageFormat#PRIVATE}, this
+     * ImageWriter will be able to operate with {@link android.graphics.ImageFormat#PRIVATE} Images.
+     * </p>
+     * <p>
+     * Note that the consumer end-point may or may not be able to support Images with different
+     * format, for such case, the application should only use this method if the consumer is able
+     * to consume such images.
+     * </p>
+     * <p>
+     * The input Image size depends on the Surface that is provided by
+     * the downstream consumer end-point.
+     * </p>
+     *
+     * @param surface The destination Surface this writer produces Image data
+     *            into.
+     * @param maxImages The maximum number of Images the user will want to
+     *            access simultaneously for producing Image data. This should be
+     *            as small as possible to limit memory use. Once maxImages
+     *            Images are dequeued by the user, one of them has to be queued
+     *            back before a new Image can be dequeued for access via
+     *            {@link ImageWriter#dequeueInputImage()}.
+     * @param format The format of this ImageWriter. It can be any valid format specified by
+     *            {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}.
+     *
+     * @return a new ImageWriter instance.
+     */
+    @NonNull
+    public static ImageWriter newInstance(@NonNull Surface surface,
+            @IntRange(from = 1) int maxImages, int format) {
+        if (Build.VERSION.SDK_INT >= 26) {
+            return ImageWriterCompatApi26Impl.newInstance(surface, maxImages, format);
+        } else if (Build.VERSION.SDK_INT >= 29) {
+            return ImageWriterCompatApi29Impl.newInstance(surface, maxImages, format);
+        }
+
+        throw new RuntimeException(
+                "Unable to call newInstance(Surface, int, int) on API " + Build.VERSION.SDK_INT
+                        + ". Version 26 or higher required.");
+    }
+
+    // Class should not be instantiated.
+    private ImageWriterCompat() {
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/ImageWriterCompatApi26Impl.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/ImageWriterCompatApi26Impl.java
new file mode 100644
index 0000000..5fb2f66
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/ImageWriterCompatApi26Impl.java
@@ -0,0 +1,68 @@
+/*
+ * 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.core.internal.compat;
+
+import android.media.ImageWriter;
+import android.os.Build;
+import android.util.Log;
+import android.view.Surface;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.core.util.Preconditions;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+@RequiresApi(26)
+final class ImageWriterCompatApi26Impl {
+    private static final String TAG = "ImageWriterCompatApi26";
+
+    private static Method sNewInstanceMethod;
+
+    static {
+        try {
+            sNewInstanceMethod = ImageWriter.class.getMethod("newInstance", Surface.class,
+                    int.class, int.class);
+        } catch (NoSuchMethodException e) {
+            Log.i(TAG, "Unable to initialize via reflection.", e);
+        }
+    }
+
+    @NonNull
+    static ImageWriter newInstance(@NonNull Surface surface, @IntRange(from = 1) int maxImages,
+            int format) {
+        Throwable t = null;
+        if (Build.VERSION.SDK_INT >= 26) {
+            try {
+                return (ImageWriter) Preconditions.checkNotNull(
+                        sNewInstanceMethod.invoke(null, surface, maxImages, format));
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                t = e;
+            }
+        }
+
+        throw new RuntimeException("Unable to invoke newInstance(Surface, int, int) via "
+                + "reflection.", t);
+    }
+
+    // Class should not be instantiated.
+    private ImageWriterCompatApi26Impl() {
+    }
+}
+
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/ImageWriterCompatApi29Impl.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/ImageWriterCompatApi29Impl.java
new file mode 100644
index 0000000..a2ea19d
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/ImageWriterCompatApi29Impl.java
@@ -0,0 +1,39 @@
+/*
+ * 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.core.internal.compat;
+
+import android.media.ImageWriter;
+import android.view.Surface;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+@RequiresApi(29)
+final class ImageWriterCompatApi29Impl {
+
+    @NonNull
+    static ImageWriter newInstance(@NonNull Surface surface, @IntRange(from = 1) int maxImages,
+            int format) {
+        return ImageWriter.newInstance(surface, maxImages, format);
+    }
+
+    // Class should not be instantiated.
+    private ImageWriterCompatApi29Impl() {
+    }
+}
+
diff --git a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/package-info.java
similarity index 80%
copy from compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt
copy to camera/camera-core/src/main/java/androidx/camera/core/internal/compat/package-info.java
index 3de9f0d..439a386 100644
--- a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/package-info.java
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.util
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+package androidx.camera.core.internal.compat;
 
-actual fun Float.toStringAsFixed(digits: Int): String = String.format("%.${digits}f", this)
+import androidx.annotation.RestrictTo;
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/SoftwareJpegEncodingPreferredQuirk.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/SoftwareJpegEncodingPreferredQuirk.java
new file mode 100644
index 0000000..de15939
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/SoftwareJpegEncodingPreferredQuirk.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.internal.compat.quirk;
+
+
+import androidx.camera.core.impl.Quirk;
+
+/**
+ * A Quirk interface which denotes CameraX should prefer producing JPEGs itself from other
+ * formats rather than the camera producing JPEGs directly.
+ *
+ * <p>Subclasses of this quirk may prefer CameraX produces JPEGs itself (likely from a YUV
+ * format) for compatibility or performance reasons.
+ */
+public interface SoftwareJpegEncodingPreferredQuirk extends Quirk {
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/utils/ImageUtil.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/utils/ImageUtil.java
index c7d011a..e5404ef 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/utils/ImageUtil.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/utils/ImageUtil.java
@@ -89,6 +89,61 @@
         return data;
     }
 
+    /** {@link android.media.Image} to NV21 byte array. */
+    @NonNull
+    public static byte[] yuv_420_888toNv21(@NonNull ImageProxy image) {
+        ImageProxy.PlaneProxy yPlane = image.getPlanes()[0];
+        ImageProxy.PlaneProxy uPlane = image.getPlanes()[1];
+        ImageProxy.PlaneProxy vPlane = image.getPlanes()[2];
+
+        ByteBuffer yBuffer = yPlane.getBuffer();
+        ByteBuffer uBuffer = uPlane.getBuffer();
+        ByteBuffer vBuffer = vPlane.getBuffer();
+        yBuffer.rewind();
+        uBuffer.rewind();
+        vBuffer.rewind();
+
+        int ySize = yBuffer.remaining();
+
+        int position = 0;
+        // TODO(b/115743986): Pull these bytes from a pool instead of allocating for every image.
+        byte[] nv21 = new byte[ySize + (image.getWidth() * image.getHeight() / 2)];
+
+        // Add the full y buffer to the array. If rowStride > 1, some padding may be skipped.
+        for (int row = 0; row < image.getHeight(); row++) {
+            yBuffer.get(nv21, position, image.getWidth());
+            position += image.getWidth();
+            yBuffer.position(
+                    Math.min(ySize, yBuffer.position() - image.getWidth() + yPlane.getRowStride()));
+        }
+
+        int chromaHeight = image.getHeight() / 2;
+        int chromaWidth = image.getWidth() / 2;
+        int vRowStride = vPlane.getRowStride();
+        int uRowStride = uPlane.getRowStride();
+        int vPixelStride = vPlane.getPixelStride();
+        int uPixelStride = uPlane.getPixelStride();
+
+        // Interleave the u and v frames, filling up the rest of the buffer. Use two line buffers to
+        // perform faster bulk gets from the byte buffers.
+        byte[] vLineBuffer = new byte[vRowStride];
+        byte[] uLineBuffer = new byte[uRowStride];
+        for (int row = 0; row < chromaHeight; row++) {
+            vBuffer.get(vLineBuffer, 0, Math.min(vRowStride, vBuffer.remaining()));
+            uBuffer.get(uLineBuffer, 0, Math.min(uRowStride, uBuffer.remaining()));
+            int vLineBufferPosition = 0;
+            int uLineBufferPosition = 0;
+            for (int col = 0; col < chromaWidth; col++) {
+                nv21[position++] = vLineBuffer[vLineBufferPosition];
+                nv21[position++] = uLineBuffer[uLineBufferPosition];
+                vLineBufferPosition += vPixelStride;
+                uLineBufferPosition += uPixelStride;
+            }
+        }
+
+        return nv21;
+    }
+
     /** Crops byte array with given {@link android.graphics.Rect}. */
     @NonNull
     public static byte[] cropByteArray(@NonNull byte[] data, @Nullable Rect cropRect)
@@ -188,59 +243,6 @@
         return out.toByteArray();
     }
 
-    private static byte[] yuv_420_888toNv21(ImageProxy image) {
-        ImageProxy.PlaneProxy yPlane = image.getPlanes()[0];
-        ImageProxy.PlaneProxy uPlane = image.getPlanes()[1];
-        ImageProxy.PlaneProxy vPlane = image.getPlanes()[2];
-
-        ByteBuffer yBuffer = yPlane.getBuffer();
-        ByteBuffer uBuffer = uPlane.getBuffer();
-        ByteBuffer vBuffer = vPlane.getBuffer();
-        yBuffer.rewind();
-        uBuffer.rewind();
-        vBuffer.rewind();
-
-        int ySize = yBuffer.remaining();
-
-        int position = 0;
-        // TODO(b/115743986): Pull these bytes from a pool instead of allocating for every image.
-        byte[] nv21 = new byte[ySize + (image.getWidth() * image.getHeight() / 2)];
-
-        // Add the full y buffer to the array. If rowStride > 1, some padding may be skipped.
-        for (int row = 0; row < image.getHeight(); row++) {
-            yBuffer.get(nv21, position, image.getWidth());
-            position += image.getWidth();
-            yBuffer.position(
-                    Math.min(ySize, yBuffer.position() - image.getWidth() + yPlane.getRowStride()));
-        }
-
-        int chromaHeight = image.getHeight() / 2;
-        int chromaWidth = image.getWidth() / 2;
-        int vRowStride = vPlane.getRowStride();
-        int uRowStride = uPlane.getRowStride();
-        int vPixelStride = vPlane.getPixelStride();
-        int uPixelStride = uPlane.getPixelStride();
-
-        // Interleave the u and v frames, filling up the rest of the buffer. Use two line buffers to
-        // perform faster bulk gets from the byte buffers.
-        byte[] vLineBuffer = new byte[vRowStride];
-        byte[] uLineBuffer = new byte[uRowStride];
-        for (int row = 0; row < chromaHeight; row++) {
-            vBuffer.get(vLineBuffer, 0, Math.min(vRowStride, vBuffer.remaining()));
-            uBuffer.get(uLineBuffer, 0, Math.min(uRowStride, uBuffer.remaining()));
-            int vLineBufferPosition = 0;
-            int uLineBufferPosition = 0;
-            for (int col = 0; col < chromaWidth; col++) {
-                nv21[position++] = vLineBuffer[vLineBufferPosition];
-                nv21[position++] = uLineBuffer[uLineBufferPosition];
-                vLineBufferPosition += vPixelStride;
-                uLineBufferPosition += uPixelStride;
-            }
-        }
-
-        return nv21;
-    }
-
     private static boolean isCropAspectRatioHasEffect(Size sourceSize, Rational aspectRatio) {
         int sourceWidth = sourceSize.getWidth();
         int sourceHeight = sourceSize.getHeight();
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/CameraXConfigTest.java b/camera/camera-core/src/test/java/androidx/camera/core/CameraXConfigTest.java
index d82f3f7..656a101 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/CameraXConfigTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/CameraXConfigTest.java
@@ -96,4 +96,13 @@
         final Integer minLoggingLevel = cameraXConfig.getMinimumLoggingLevel();
         assertThat(minLoggingLevel).isEqualTo(Logger.DEFAULT_MIN_LOG_LEVEL);
     }
+
+    @Test
+    public void canGetAvailableCamerasSelector() {
+        CameraSelector cameraSelector = new CameraSelector.Builder().build();
+        CameraXConfig cameraXConfig = new CameraXConfig.Builder()
+                .setAvailableCamerasLimiter(cameraSelector)
+                .build();
+        assertThat(cameraXConfig.getAvailableCamerasLimiter(null)).isEqualTo(cameraSelector);
+    }
 }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ProcessingImageReaderTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ProcessingImageReaderTest.java
index 9d751a1..d1dd263 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ProcessingImageReaderTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ProcessingImageReaderTest.java
@@ -26,7 +26,6 @@
 import static org.robolectric.Shadows.shadowOf;
 
 import android.graphics.ImageFormat;
-import android.os.AsyncTask;
 import android.os.Build;
 import android.util.Pair;
 import android.util.Size;
@@ -63,7 +62,8 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-@SuppressWarnings("UnstableApiUsage") // Needed because PausedExecutorService is marked @Beta
+// UnstableApiUsage is needed because PausedExecutorService is marked @Beta
+@SuppressWarnings({"UnstableApiUsage", "deprecation"})
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
@@ -194,7 +194,7 @@
         // Sets the callback from ProcessingImageReader to start processing
         WaitingCaptureProcessor waitingCaptureProcessor = new WaitingCaptureProcessor();
         ProcessingImageReader processingImageReader = new ProcessingImageReader(
-                mMetadataImageReader, AsyncTask.THREAD_POOL_EXECUTOR, mCaptureBundle,
+                mMetadataImageReader, android.os.AsyncTask.THREAD_POOL_EXECUTOR, mCaptureBundle,
                 waitingCaptureProcessor);
         processingImageReader.setOnImageAvailableListener(mock(
                 ImageReaderProxy.OnImageAvailableListener.class),
@@ -263,7 +263,7 @@
         MetadataImageReader metadataImageReader = new MetadataImageReader(imageReaderProxy);
 
         // Expects to throw exception when creating ProcessingImageReader.
-        new ProcessingImageReader(metadataImageReader, AsyncTask.THREAD_POOL_EXECUTOR,
+        new ProcessingImageReader(metadataImageReader, android.os.AsyncTask.THREAD_POOL_EXECUTOR,
                 mCaptureBundle,
                 NOOP_PROCESSOR);
     }
@@ -272,8 +272,8 @@
     public void captureStageExceedMaxCaptureStage_setCaptureBundleThrowsException() {
         // Creates a ProcessingImageReader with maximum Image number.
         ProcessingImageReader processingImageReader = new ProcessingImageReader(100, 100,
-                ImageFormat.YUV_420_888, 2, AsyncTask.THREAD_POOL_EXECUTOR, mCaptureBundle,
-                mock(CaptureProcessor.class));
+                ImageFormat.YUV_420_888, 2, android.os.AsyncTask.THREAD_POOL_EXECUTOR,
+                mCaptureBundle, mock(CaptureProcessor.class));
 
         // Expects to throw exception when invoke the setCaptureBundle method with a
         // CaptureBundle size greater than maximum image number.
@@ -281,6 +281,16 @@
                 CaptureBundles.createCaptureBundle(mCaptureStage1, mCaptureStage2, mCaptureStage3));
     }
 
+    @Test
+    public void imageReaderFormatIsOutputFormat() {
+        // Creates a ProcessingImageReader with input format YUV_420_888 and output JPEG
+        ProcessingImageReader processingImageReader = new ProcessingImageReader(100, 100,
+                ImageFormat.YUV_420_888, 2, android.os.AsyncTask.THREAD_POOL_EXECUTOR,
+                mCaptureBundle, mock(CaptureProcessor.class), ImageFormat.JPEG);
+
+        assertThat(processingImageReader.getImageFormat()).isEqualTo(ImageFormat.JPEG);
+    }
+
     private void triggerImageAvailable(int captureId, long timestamp) throws InterruptedException {
         TagBundle tagBundle = TagBundle.create(new Pair<>(mTagBundleKey, captureId));
         mImageReaderProxy.triggerImageAvailable(tagBundle, timestamp);
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/impl/DeferrableSurfaceTest.java b/camera/camera-core/src/test/java/androidx/camera/core/impl/DeferrableSurfaceTest.java
index d7839cc..30ff85a 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/impl/DeferrableSurfaceTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/impl/DeferrableSurfaceTest.java
@@ -139,6 +139,7 @@
         mDeferrableSurface.decrementUseCount();
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void closedSurfaceContainsSurfaceClosedException() {
         mDeferrableSurface.close();
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/impl/QuirksTest.java b/camera/camera-core/src/test/java/androidx/camera/core/impl/QuirksTest.java
index d880ad5..9b780d9 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/impl/QuirksTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/impl/QuirksTest.java
@@ -21,6 +21,7 @@
 import org.junit.Test;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 public class QuirksTest {
@@ -41,7 +42,7 @@
     }
 
     @Test
-    public void returnNullForInexistentQuirk() {
+    public void returnNullForNonexistentQuirk() {
         final Quirk1 quirk1 = new Quirk1();
         final Quirk2 quirk2 = new Quirk2();
 
@@ -54,6 +55,52 @@
         assertThat(quirks.get(Quirk3.class)).isNull();
     }
 
+    @Test
+    public void containsReturnsTrueForExistentQuirk() {
+        final Quirk1 quirk1 = new Quirk1();
+
+        final List<Quirk> allQuirks = Collections.singletonList(quirk1);
+
+        final Quirks quirks = new Quirks(allQuirks);
+
+        assertThat(quirks.contains(Quirk1.class)).isTrue();
+    }
+
+    @Test
+    public void containsReturnsFalseForNonexistentQuirk() {
+        final Quirk1 quirk1 = new Quirk1();
+
+        final List<Quirk> allQuirks = Collections.singletonList(quirk1);
+
+        final Quirks quirks = new Quirks(allQuirks);
+
+        assertThat(quirks.contains(Quirk2.class)).isFalse();
+    }
+
+    @Test
+    public void containsReturnsTrueForExistentSuperInterfaceQuirk() {
+        final SubIQuirk subIQuirk = new SubIQuirk();
+
+        final List<Quirk> allQuirks = Collections.singletonList(subIQuirk);
+
+        final Quirks quirks = new Quirks(allQuirks);
+
+        assertThat(quirks.contains(SubIQuirk.class)).isTrue();
+        assertThat(quirks.contains(ISuperQuirk.class)).isTrue();
+    }
+
+    @Test
+    public void containsReturnsTrueForExistentSuperClassQuirk() {
+        final SubQuirk subQuirk = new SubQuirk();
+
+        final List<Quirk> allQuirks = Collections.singletonList(subQuirk);
+
+        final Quirks quirks = new Quirks(allQuirks);
+
+        assertThat(quirks.contains(SubQuirk.class)).isTrue();
+        assertThat(quirks.contains(SuperQuirk.class)).isTrue();
+    }
+
     static class Quirk1 implements Quirk {
     }
 
@@ -62,4 +109,17 @@
 
     static class Quirk3 implements Quirk {
     }
+
+    interface ISuperQuirk extends Quirk {
+    }
+
+    static class SuperQuirk implements Quirk {
+    }
+
+    static class SubQuirk extends SuperQuirk {
+    }
+
+    static class SubIQuirk implements ISuperQuirk {
+    }
+
 }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/impl/utils/ExifDataTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/impl/utils/ExifDataTest.kt
new file mode 100644
index 0000000..ac6541c
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/impl/utils/ExifDataTest.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.core.impl.utils
+
+import android.os.Build
+import androidx.camera.core.impl.CameraCaptureMetaData
+import androidx.exifinterface.media.ExifInterface
+import androidx.exifinterface.media.ExifInterface.FLAG_FLASH_FIRED
+import androidx.exifinterface.media.ExifInterface.FLAG_FLASH_NO_FLASH_FUNCTION
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import java.util.concurrent.TimeUnit
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+public class ExifDataTest {
+
+    @Test
+    public fun canSetImageWidth() {
+        val exifData = ExifData.builderForDevice().setImageWidth(100).build()
+        assertThat(exifData.getAttribute(ExifInterface.TAG_IMAGE_WIDTH)).isEqualTo("100")
+    }
+
+    @Test
+    public fun canSetImageHeight() {
+        val exifData = ExifData.builderForDevice().setImageHeight(200).build()
+        assertThat(exifData.getAttribute(ExifInterface.TAG_IMAGE_LENGTH)).isEqualTo("200")
+    }
+
+    @Test
+    public fun canSetOrientationDegrees() {
+        val exifData0 = ExifData.builderForDevice().setOrientationDegrees(0).build()
+        val exifData90 = ExifData.builderForDevice().setOrientationDegrees(90).build()
+        val exifData180 = ExifData.builderForDevice().setOrientationDegrees(180).build()
+        val exifData270 = ExifData.builderForDevice().setOrientationDegrees(270).build()
+
+        assertThat(exifData0.getAttribute(ExifInterface.TAG_ORIENTATION))
+            .isEqualTo("${ExifInterface.ORIENTATION_NORMAL}")
+        assertThat(exifData90.getAttribute(ExifInterface.TAG_ORIENTATION))
+            .isEqualTo("${ExifInterface.ORIENTATION_ROTATE_90}")
+        assertThat(exifData180.getAttribute(ExifInterface.TAG_ORIENTATION))
+            .isEqualTo("${ExifInterface.ORIENTATION_ROTATE_180}")
+        assertThat(exifData270.getAttribute(ExifInterface.TAG_ORIENTATION))
+            .isEqualTo("${ExifInterface.ORIENTATION_ROTATE_270}")
+    }
+
+    @Test
+    public fun settingInvalidOrientationIsUndefined() {
+        // Only 0, 90, 180 and 270 are valid orientations. Use an invalid orientation.
+        val exifData = ExifData.builderForDevice().setOrientationDegrees(42).build()
+
+        assertThat(exifData.getAttribute(ExifInterface.TAG_ORIENTATION))
+            .isEqualTo("${ExifInterface.ORIENTATION_UNDEFINED}")
+    }
+
+    @Test
+    public fun canSetFlashState() {
+        val exifDataFired = ExifData.builderForDevice()
+            .setFlashState(CameraCaptureMetaData.FlashState.FIRED)
+            .build()
+        val exifDataReady = ExifData.builderForDevice()
+            .setFlashState(CameraCaptureMetaData.FlashState.READY)
+            .build()
+        val exifDataNone = ExifData.builderForDevice()
+            .setFlashState(CameraCaptureMetaData.FlashState.NONE)
+            .build()
+
+        // Unknown should not set the attribute
+        val exifDataUnknown = ExifData.builderForDevice()
+            .setFlashState(CameraCaptureMetaData.FlashState.UNKNOWN)
+            .build()
+
+        // Flash fired.
+        assertThat(exifDataFired.getAttribute(ExifInterface.TAG_FLASH)?.toShort())
+            .isEqualTo(FLAG_FLASH_FIRED)
+
+        // Has flash but not fired.
+        assertThat(exifDataReady.getAttribute(ExifInterface.TAG_FLASH)?.toShort())
+            .isEqualTo(0)
+
+        // No flash function.
+        assertThat(exifDataNone.getAttribute(ExifInterface.TAG_FLASH)?.toShort())
+            .isEqualTo(FLAG_FLASH_NO_FLASH_FUNCTION)
+
+        assertThat(exifDataUnknown.getAttribute(ExifInterface.TAG_FLASH)).isNull()
+    }
+
+    @Test
+    public fun canSetExposureTime() {
+        val exifData = ExifData.builderForDevice()
+            .setExposureTimeNanos(TimeUnit.SECONDS.toNanos(5))
+            .build()
+        assertThat(exifData.getAttribute(ExifInterface.TAG_EXPOSURE_TIME)?.toFloat()?.toInt())
+            .isEqualTo(5)
+    }
+
+    @Test
+    public fun canSetLensFNumber() {
+        val exifData = ExifData.builderForDevice()
+            .setLensFNumber(1.2f)
+            .build()
+        assertThat(exifData.getAttribute(ExifInterface.TAG_F_NUMBER)).isEqualTo("1.2")
+    }
+
+    @Test
+    public fun canSetIso() {
+        val exifData = ExifData.builderForDevice()
+            .setIso(800)
+            .build()
+        assertThat(exifData.getAttribute(ExifInterface.TAG_SENSITIVITY_TYPE))
+            .isEqualTo("${ExifInterface.SENSITIVITY_TYPE_ISO_SPEED}")
+        assertThat(exifData.getAttribute(ExifInterface.TAG_PHOTOGRAPHIC_SENSITIVITY))
+            .isEqualTo("800")
+    }
+
+    @Test
+    public fun canSetFocalLength() {
+        val exifData = ExifData.builderForDevice()
+            .setFocalLength(5400f /*millimeters*/)
+            .build()
+        assertThat(
+            exifData.getAttribute(ExifInterface.TAG_FOCAL_LENGTH)
+                ?.split("/")
+                ?.map(String::toLong)
+                ?.reduce { numerator: Long, denominator: Long -> numerator / denominator }
+        ).isEqualTo(5400)
+    }
+
+    @Test
+    public fun canSetWhiteBalanceMode() {
+        val exifDataAuto = ExifData.builderForDevice()
+            .setWhiteBalanceMode(ExifData.WhiteBalanceMode.AUTO)
+            .build()
+        val exifDataManual = ExifData.builderForDevice()
+            .setWhiteBalanceMode(ExifData.WhiteBalanceMode.MANUAL)
+            .build()
+
+        assertThat(exifDataAuto.getAttribute(ExifInterface.TAG_WHITE_BALANCE)?.toShort())
+            .isEqualTo(ExifInterface.WHITE_BALANCE_AUTO)
+        assertThat(exifDataManual.getAttribute(ExifInterface.TAG_WHITE_BALANCE)?.toShort())
+            .isEqualTo(ExifInterface.WHITE_BALANCE_MANUAL)
+    }
+
+    @Test
+    public fun makeAndModelSetByDefaultBuilder() {
+        val exifDataDefault = ExifData.builderForDevice().build()
+
+        assertThat(exifDataDefault.getAttribute(ExifInterface.TAG_MAKE))
+            .isEqualTo(Build.MANUFACTURER)
+        assertThat(exifDataDefault.getAttribute(ExifInterface.TAG_MODEL))
+            .isEqualTo(Build.MODEL)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/ImageWriterCompatTest.java b/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/ImageWriterCompatTest.java
new file mode 100644
index 0000000..e739bda
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/ImageWriterCompatTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.internal.compat;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.media.ImageWriter;
+import android.os.Build;
+import android.view.Surface;
+
+import org.junit.After;
+import org.junit.Before;
+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.O)
+public final class ImageWriterCompatTest {
+
+    private static final int TEST_MAX_IMAGES = 4;
+    private static final int TEST_IMAGE_FORMAT = ImageFormat.YUV_420_888;
+    private Surface mTestSurface;
+    private SurfaceTexture mTestSurfaceTexture;
+
+    @Before
+    public void setUp() {
+        mTestSurfaceTexture = new SurfaceTexture(/* singleBufferMode= */ false);
+        mTestSurface = new Surface(mTestSurfaceTexture);
+    }
+
+    @After
+    public void tearDown() {
+        mTestSurface.release();
+        mTestSurfaceTexture.release();
+    }
+
+    @Test
+    public void canCreateNewInstance() {
+        ImageWriter imageWriter = ImageWriterCompat.newInstance(mTestSurface,
+                TEST_MAX_IMAGES, TEST_IMAGE_FORMAT);
+
+        assertThat(imageWriter).isNotNull();
+    }
+}
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
index 062dd05..f848d32 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
@@ -198,6 +198,7 @@
         verifyNoMoreInteractions(mockPreviewExtenderImpl);
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     @MediumTest
     public void getCaptureStagesTest_shouldSetToRepeatingRequest() {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
index 70d700c..6e27cd7 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
@@ -67,6 +67,7 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
 
 /** Utility functions for obtaining instances of camera2 classes. */
 public final class CameraUtil {
@@ -321,7 +322,7 @@
             try {
                 cameraUseCaseAdapter.addUseCases(Arrays.asList(useCases));
             } catch (CameraUseCaseAdapter.CameraException e) {
-                throw new IllegalArgumentException("Unable to attach use cases to camera.");
+                throw new IllegalArgumentException("Unable to attach use cases to camera.", e);
             }
         });
 
@@ -569,33 +570,28 @@
      * unavailable if PRETEST_CAMERA_TAG is loggable at the debug level (see Log#isLoggable).
      */
     @NonNull
-    public static RuleChain grantCameraPermissionAndPreTest() {
-        RuleChain rule =
-                RuleChain.outerRule(GrantPermissionRule.grant(Manifest.permission.CAMERA)).around(
-                (base, description) -> new Statement() {
-                    @Override
-                    public void evaluate() throws Throwable {
-                        dumpCameraLensFacingInfo();
-                        assumeTrue(deviceHasCamera());
-                        base.evaluate();
-                    }
-                });
-        if (shouldRunPreTest()) {
-            rule = rule.around(
-                    new CameraUtil.PreTestCamera(Log.isLoggable(PRETEST_CAMERA_TAG, Log.DEBUG)));
-        }
-        return rule;
+    public static TestRule grantCameraPermissionAndPreTest() {
+        return grantCameraPermissionAndPreTest(new PreTestCamera());
     }
 
-    private static boolean shouldRunPreTest() {
-        if (Build.MODEL.contains("pixel 2") && Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
-            // TODO(b/170070248) Pixel 2 HAL died easily if the pretest and CameraX open the
-            //  camera device at the same time. Remove the code after the pixel 2 issue fixed.
-            Logger.d(LOG_TAG, "Skip camera pretest (b/170070248)");
-            return false;
-        }
-
-        return true;
+    /**
+     * Grant the camera permission and test the camera.
+     *
+     * @param cameraTestRule the PreTestCamera rule to execute the camera test.
+     */
+    @NonNull
+    public static TestRule grantCameraPermissionAndPreTest(@NonNull PreTestCamera cameraTestRule) {
+        RuleChain rule =
+                RuleChain.outerRule(GrantPermissionRule.grant(Manifest.permission.CAMERA)).around(
+                        (base, description) -> new Statement() {
+                            @Override
+                            public void evaluate() throws Throwable {
+                                dumpCameraLensFacingInfo();
+                                assumeTrue(deviceHasCamera());
+                                base.evaluate();
+                            }
+                        }).around(cameraTestRule);
+        return rule;
     }
 
     /**
@@ -609,6 +605,11 @@
      */
     public static class PreTestCamera implements TestRule {
         final boolean mThrowOnError;
+        final AtomicReference<Boolean> mCanOpenCamera = new AtomicReference<>();
+
+        public PreTestCamera() {
+            mThrowOnError = Log.isLoggable(PRETEST_CAMERA_TAG, Log.DEBUG);
+        }
 
         public PreTestCamera(boolean throwOnError) {
             mThrowOnError = throwOnError;
@@ -620,26 +621,28 @@
             return new Statement() {
                 @Override
                 public void evaluate() throws Throwable {
-                    boolean backStatus = true;
-                    if (hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK)) {
-                        RetryCameraOpener opener =
-                                new RetryCameraOpener(CameraSelector.LENS_FACING_BACK);
-                        backStatus = opener.openWithRetry(5, 5000);
-                        opener.shutdown();
+                    if (mCanOpenCamera.get() == null) {
+                        boolean backStatus = true;
+                        if (hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK)) {
+                            RetryCameraOpener opener =
+                                    new RetryCameraOpener(CameraSelector.LENS_FACING_BACK);
+                            backStatus = opener.openWithRetry(5, 5000);
+                            opener.shutdown();
+                        }
+
+                        boolean frontStatus = true;
+                        if (hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT)) {
+                            RetryCameraOpener opener =
+                                    new RetryCameraOpener(CameraSelector.LENS_FACING_FRONT);
+                            frontStatus = opener.openWithRetry(5, 5000);
+                            opener.shutdown();
+                        }
+                        Logger.d(LOG_TAG,
+                                "PreTest Open camera result " + backStatus + " " + frontStatus);
+                        mCanOpenCamera.set(backStatus && frontStatus);
                     }
 
-                    boolean frontStatus = true;
-                    if (hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT)) {
-                        RetryCameraOpener opener =
-                                new RetryCameraOpener(CameraSelector.LENS_FACING_FRONT);
-                        frontStatus = opener.openWithRetry(5, 5000);
-                        opener.shutdown();
-                    }
-                    boolean canOpenCamera = backStatus && frontStatus;
-                    Logger.d(LOG_TAG,
-                            "PreTest Open camera result " + backStatus + " " + frontStatus);
-
-                    if (canOpenCamera) {
+                    if (mCanOpenCamera.get()) {
                         base.evaluate();
                     } else {
                         if (mThrowOnError) {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/Configs.java b/camera/camera-testing/src/main/java/androidx/camera/testing/Configs.java
index 951bd8f..23cffbc7 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/Configs.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/Configs.java
@@ -18,6 +18,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.camera.core.UseCase;
+import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.UseCaseConfig;
 import androidx.camera.core.impl.UseCaseConfigFactory;
 
@@ -32,12 +33,13 @@
     /** Return a map that associates UseCases to UseCaseConfigs with default settings. */
     @NonNull
     public static Map<UseCase, UseCaseConfig<?>> useCaseConfigMapWithDefaultSettingsFromUseCaseList(
-            @NonNull List<UseCase> useCases, @NonNull UseCaseConfigFactory useCaseConfigFactory) {
+            @NonNull CameraInfoInternal cameraInfo, @NonNull List<UseCase> useCases,
+            @NonNull UseCaseConfigFactory useCaseConfigFactory) {
         Map<UseCase, UseCaseConfig<?>> useCaseToConfigMap = new HashMap<>();
 
         for (UseCase useCase : useCases) {
             // Combine with default configuration.
-            UseCaseConfig<?> combinedUseCaseConfig = useCase.mergeConfigs(null,
+            UseCaseConfig<?> combinedUseCaseConfig = useCase.mergeConfigs(cameraInfo, null,
                     useCase.getDefaultConfig(true, useCaseConfigFactory));
             useCaseToConfigMap.put(useCase, combinedUseCaseConfig);
         }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
index f020ad2..3a6ef21 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
@@ -31,8 +31,6 @@
 import androidx.camera.core.impl.DeferrableSurface;
 import androidx.camera.core.impl.LiveDataObservable;
 import androidx.camera.core.impl.Observable;
-import androidx.camera.core.impl.Quirk;
-import androidx.camera.core.impl.Quirks;
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.UseCaseAttachState;
 import androidx.camera.core.impl.utils.futures.Futures;
@@ -72,9 +70,6 @@
 
     private List<DeferrableSurface> mConfiguredDeferrableSurfaces = Collections.emptyList();
 
-    @NonNull
-    private final List<Quirk> mCameraQuirks = Collections.emptyList();
-
     public FakeCamera() {
         this(DEFAULT_CAMERA_ID, /*cameraControl=*/null,
                 new FakeCameraInfoInternal(DEFAULT_CAMERA_ID));
@@ -300,17 +295,6 @@
         return mCameraInfoInternal;
     }
 
-    @NonNull
-    @Override
-    public Quirks getCameraQuirks() {
-        return new Quirks(mCameraQuirks);
-    }
-
-    /** Adds a quirk to the list of this camera's quirks. */
-    public void addCameraQuirk(@NonNull final Quirk quirk) {
-        mCameraQuirks.add(quirk);
-    }
-
     private void checkNotReleased() {
         if (mState == State.RELEASED) {
             throw new IllegalStateException("Camera has been released.");
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
index 19d3ab7..78aab0c 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
@@ -30,11 +30,15 @@
 import androidx.camera.core.impl.CameraCaptureCallback;
 import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.ImageOutputConfig.RotationValue;
+import androidx.camera.core.impl.Quirk;
+import androidx.camera.core.impl.Quirks;
 import androidx.camera.core.impl.utils.CameraOrientationUtil;
 import androidx.camera.core.internal.ImmutableZoomState;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -53,6 +57,9 @@
     private final MutableLiveData<ZoomState> mZoomLiveData;
     private String mImplementationType = IMPLEMENTATION_TYPE_FAKE;
 
+    @NonNull
+    private final List<Quirk> mCameraQuirks = new ArrayList<>();
+
     public FakeCameraInfoInternal() {
         this(/*sensorRotation=*/ 0, /*lensFacing=*/ CameraSelector.LENS_FACING_BACK);
     }
@@ -167,6 +174,17 @@
         throw new UnsupportedOperationException("Not Implemented");
     }
 
+    @NonNull
+    @Override
+    public Quirks getCameraQuirks() {
+        return new Quirks(mCameraQuirks);
+    }
+
+    /** Adds a quirk to the list of this camera's quirks. */
+    public void addCameraQuirk(@NonNull final Quirk quirk) {
+        mCameraQuirks.add(quirk);
+    }
+
     /**
      * Set the implementation type for testing
      */
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageInfo.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageInfo.java
index 6e97478..df8a0da 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageInfo.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageInfo.java
@@ -20,6 +20,7 @@
 import androidx.camera.core.ImageInfo;
 import androidx.camera.core.impl.MutableTagBundle;
 import androidx.camera.core.impl.TagBundle;
+import androidx.camera.core.impl.utils.ExifData;
 
 /**
  * A fake implementation of {@link ImageInfo} where the values are settable.
@@ -62,4 +63,9 @@
     public int getRotationDegrees() {
         return mRotationDegrees;
     }
+
+    @Override
+    public void populateExifData(@NonNull ExifData.Builder exifBuilder) {
+        exifBuilder.setOrientationDegrees(mRotationDegrees);
+    }
 }
diff --git a/camera/camera-video/src/androidTest/AndroidManifest.xml b/camera/camera-video/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..9f27ec7
--- /dev/null
+++ b/camera/camera-video/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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.camera.video.test">
+
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+</manifest>
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/AudioSourceTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/AudioSourceTest.kt
new file mode 100644
index 0000000..db52f2a
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/AudioSourceTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.camera.video.internal
+
+import android.Manifest
+import android.media.AudioFormat
+import android.media.MediaRecorder
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.video.internal.encoder.FakeInputBuffer
+import androidx.camera.video.internal.encoder.noInvocation
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.rule.GrantPermissionRule
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.verify
+import java.util.concurrent.Callable
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class AudioSourceTest {
+
+    companion object {
+        private const val SAMPLE_RATE = 8000
+        private const val DEFAULT_MIN_BUFFER_SIZE = 1024
+    }
+
+    @get:Rule
+    var mAudioPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
+        Manifest.permission.RECORD_AUDIO
+    )
+    private lateinit var audioSource: AudioSource
+    private lateinit var fakeBufferProvider: FakeBufferProvider
+    private val bufferFactoryInvocations = mock(Callable::class.java)
+
+    @Before
+    fun setUp() {
+        fakeBufferProvider = FakeBufferProvider {
+            bufferFactoryInvocations.call()
+            FakeInputBuffer()
+        }
+        fakeBufferProvider.setActive(true)
+
+        audioSource = AudioSource.Builder()
+            .setExecutor(CameraXExecutors.ioExecutor())
+            .setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
+            .setSampleRate(SAMPLE_RATE)
+            .setChannelConfig(AudioFormat.CHANNEL_IN_MONO)
+            .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT)
+            .setDefaultBufferSize(DEFAULT_MIN_BUFFER_SIZE)
+            .setBufferProvider(fakeBufferProvider)
+            .build()
+    }
+
+    @After
+    fun tearDown() {
+        if (this::audioSource.isInitialized) {
+            audioSource.release()
+        }
+    }
+
+    @Test
+    fun canRestartAudioSource() {
+        for (i in 0..2) {
+            // Act.
+            audioSource.start()
+
+            // Assert.
+            // It should continuously send audio data by invoking BufferProvider#acquireBuffer
+            verify(bufferFactoryInvocations, timeout(10000L).atLeast(3)).call()
+
+            // Act.
+            audioSource.stop()
+
+            // Assert.
+            verify(bufferFactoryInvocations, noInvocation(3000L, 6000L)).call()
+        }
+    }
+
+    @Test
+    fun bufferProviderStateChange_acquireBufferOrNot() {
+        // Arrange.
+        audioSource.start()
+
+        for (i in 0..2) {
+            // Act.
+            fakeBufferProvider.setActive(true)
+
+            // Assert.
+            // It should continuously send audio data by invoking BufferProvider#acquireBuffer
+            verify(bufferFactoryInvocations, timeout(10000L).atLeast(3)).call()
+
+            // Act.
+            fakeBufferProvider.setActive(false)
+
+            // Assert.
+            verify(bufferFactoryInvocations, noInvocation(3000L, 6000L)).call()
+        }
+    }
+}
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/FakeBufferProvider.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/FakeBufferProvider.kt
new file mode 100644
index 0000000..db8e4be
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/FakeBufferProvider.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.video.internal
+
+import androidx.annotation.GuardedBy
+import androidx.camera.core.impl.Observable
+import androidx.camera.core.impl.utils.futures.Futures
+import androidx.camera.video.internal.encoder.InputBuffer
+import com.google.common.util.concurrent.ListenableFuture
+import java.lang.IllegalStateException
+import java.util.concurrent.Callable
+import java.util.concurrent.Executor
+
+class FakeBufferProvider(private val bufferFactory: Callable<InputBuffer>) :
+    BufferProvider<InputBuffer> {
+
+    private val lock = Object()
+    @GuardedBy("lock")
+    private val observers = mutableMapOf<Observable.Observer<BufferProvider.State>, Executor>()
+    @GuardedBy("lock")
+    private var state = BufferProvider.State.ACTIVE
+
+    override fun acquireBuffer(): ListenableFuture<InputBuffer> {
+        synchronized(lock) {
+            return if (state == BufferProvider.State.ACTIVE) {
+                Futures.immediateFuture(bufferFactory.call())
+            } else {
+                Futures.immediateFailedFuture(IllegalStateException("Not in ACTIVE state"))
+            }
+        }
+    }
+
+    override fun fetchData(): ListenableFuture<BufferProvider.State> {
+        synchronized(lock) {
+            return Futures.immediateFuture(state)
+        }
+    }
+
+    override fun addObserver(
+        executor: Executor,
+        observer: Observable.Observer<BufferProvider.State>
+    ) {
+        synchronized(observers) {
+            observers[observer] = executor
+        }
+        executor.execute { observer.onNewData(state) }
+    }
+
+    override fun removeObserver(observer: Observable.Observer<BufferProvider.State>) {
+        synchronized(lock) {
+            observers.remove(observer)
+        }
+    }
+
+    fun setActive(active: Boolean) {
+        val newState = if (active) BufferProvider.State.ACTIVE else BufferProvider.State.INACTIVE
+        val localObservers: Map<Observable.Observer<BufferProvider.State>, Executor>
+        synchronized(lock) {
+            if (state == newState) {
+                return
+            }
+            state = newState
+            localObservers = observers
+        }
+        for ((observer, executor) in localObservers) {
+            executor.execute { observer.onNewData(newState) }
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/AudioEncoderTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/AudioEncoderTest.kt
index 64d1e7d..ffba5b9 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/AudioEncoderTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/AudioEncoderTest.kt
@@ -16,12 +16,17 @@
 package androidx.camera.video.internal.encoder
 
 import android.media.AudioFormat
+import androidx.camera.core.impl.Observable.Observer
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.video.internal.BufferProvider
+import androidx.camera.video.internal.BufferProvider.State
+import androidx.concurrent.futures.await
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
-import kotlinx.coroutines.Dispatchers
+import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.asCoroutineDispatcher
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import org.junit.After
@@ -37,6 +42,10 @@
 import org.mockito.Mockito.verify
 import org.mockito.invocation.InvocationOnMock
 import java.nio.ByteBuffer
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicReference
 
 @LargeTest
 @RunWith(AndroidJUnit4::class)
@@ -51,7 +60,7 @@
 
     private lateinit var encoder: Encoder
     private lateinit var encoderCallback: EncoderCallback
-    private lateinit var byteBufferProviderJob: Job
+    private lateinit var fakeAudioLoop: FakeAudioLoop
 
     @Before
     fun setup() {
@@ -74,25 +83,25 @@
         )
         encoder.setEncoderCallback(encoderCallback, CameraXExecutors.directExecutor())
 
-        // Prepare a fake audio source
-        val byteBuffer = ByteBuffer.allocateDirect(1024)
-        byteBufferProviderJob = GlobalScope.launch(Dispatchers.Default) {
-            while (true) {
-                byteBuffer.rewind()
-                (encoder.input as Encoder.ByteBufferInput).putByteBuffer(byteBuffer)
-                delay(200)
-            }
-        }
+        @Suppress("UNCHECKED_CAST")
+        fakeAudioLoop = FakeAudioLoop(encoder.input as BufferProvider<InputBuffer>)
     }
 
     @After
     fun tearDown() {
-        encoder.release()
-        byteBufferProviderJob.cancel(null)
+        if (this::encoder.isInitialized) {
+            encoder.release()
+        }
+        if (this::fakeAudioLoop.isInitialized) {
+            fakeAudioLoop.stop()
+        }
     }
 
     @Test
     fun discardInputBufferBeforeStart() {
+        // Arrange.
+        fakeAudioLoop.start()
+
         // Act.
         // Wait a second to receive data
         Thread.sleep(3000L)
@@ -103,6 +112,9 @@
 
     @Test
     fun canRestartEncoder() {
+        // Arrange.
+        fakeAudioLoop.start()
+
         for (i in 0..3) {
             // Arrange.
             clearInvocations(encoderCallback)
@@ -125,6 +137,9 @@
 
     @Test
     fun canRestartEncoderImmediately() {
+        // Arrange.
+        fakeAudioLoop.start()
+
         // Act.
         encoder.start()
         encoder.stop()
@@ -136,6 +151,9 @@
 
     @Test
     fun canPauseResumeEncoder() {
+        // Arrange.
+        fakeAudioLoop.start()
+
         // Act.
         encoder.start()
 
@@ -162,6 +180,9 @@
 
     @Test
     fun canPauseStopStartEncoder() {
+        // Arrange.
+        fakeAudioLoop.start()
+
         // Act.
         encoder.start()
 
@@ -191,4 +212,128 @@
         // Assert.
         verify(encoderCallback, timeout(15000L).atLeast(5)).onEncodedData(any())
     }
+
+    @Test
+    fun bufferProvider_canAcquireBuffer() {
+        // Arrange.
+        encoder.start()
+
+        for (i in 0..8) {
+            // Act.
+            val inputBuffer = (encoder.input as Encoder.ByteBufferInput)
+                .acquireBuffer()
+                .get(3, TimeUnit.SECONDS)
+
+            // Assert.
+            assertThat(inputBuffer).isNotNull()
+            inputBuffer.cancel()
+        }
+    }
+
+    @Test
+    fun bufferProvider_canReceiveBufferProviderStateChange() {
+        // Arrange.
+        val stateRef = AtomicReference<State>()
+        val lock = Semaphore(0)
+        (encoder.input as Encoder.ByteBufferInput).addObserver(
+            CameraXExecutors.directExecutor(),
+            object : Observer<State> {
+                override fun onNewData(state: State?) {
+                    stateRef.set(state)
+                    lock.release()
+                }
+
+                override fun onError(t: Throwable) {
+                    stateRef.set(null)
+                    lock.release()
+                }
+            }
+        )
+
+        // Assert.
+        assertThat(lock.tryAcquire(3, TimeUnit.SECONDS)).isTrue()
+        assertThat(stateRef.get()).isEqualTo(State.INACTIVE)
+
+        // Act.
+        encoder.start()
+
+        // Assert.
+        assertThat(lock.tryAcquire(3, TimeUnit.SECONDS)).isTrue()
+        assertThat(stateRef.get()).isEqualTo(State.ACTIVE)
+
+        // Act.
+        encoder.pause()
+
+        // Assert
+        assertThat(lock.tryAcquire(3, TimeUnit.SECONDS)).isTrue()
+        assertThat(stateRef.get()).isEqualTo(State.INACTIVE)
+
+        // Act.
+        encoder.start()
+
+        // Assert.
+        assertThat(lock.tryAcquire(3, TimeUnit.SECONDS)).isTrue()
+        assertThat(stateRef.get()).isEqualTo(State.ACTIVE)
+
+        // Act.
+        encoder.stop()
+
+        // Assert.
+        assertThat(lock.tryAcquire(3, TimeUnit.SECONDS)).isTrue()
+        assertThat(stateRef.get()).isEqualTo(State.INACTIVE)
+    }
+
+    private class FakeAudioLoop(private val bufferProvider: BufferProvider<InputBuffer>) {
+        private val inputByteBuffer = ByteBuffer.allocateDirect(1024)
+        private val started = AtomicBoolean(false)
+        private var job: Job? = null
+
+        fun start() {
+            if (started.getAndSet(true)) {
+                return
+            }
+            job = GlobalScope.launch(
+                CameraXExecutors.ioExecutor().asCoroutineDispatcher(),
+            ) {
+                while (true) {
+                    try {
+                        val inputBuffer = bufferProvider.acquireBuffer().await()
+                        inputBuffer.apply {
+                            byteBuffer.apply {
+                                put(
+                                    inputByteBuffer.apply {
+                                        clear()
+                                        limit(limit().coerceAtMost(byteBuffer.capacity()))
+                                    }
+                                )
+                                flip()
+                            }
+                            setPresentationTimeUs(System.nanoTime() / 1000L)
+                            submit()
+                        }
+                    } catch (e: IllegalStateException) {
+                        // For simplicity, AudioLoop doesn't monitor the encoder's state.
+                        // When an IllegalStateException is thrown by encoder which is not started,
+                        // AudioLoop should retry with a delay to avoid busy loop.
+                        // CancellationException is a subclass of IllegalStateException and is
+                        // ambiguous since the cancellation could be caused by ListenableFuture
+                        // was cancelled or coroutine Job was cancelled. For the
+                        // ListenableFuture case, AudioLoop will need to retry with a delay as
+                        // IllegalStateException. For the coroutine Job case, the loop should
+                        // be stopped. The goal can be simply achieved by calling delay() method
+                        // because the method will also get CancellationException if it is
+                        // coroutine Job cancellation, and eventually leave the audio loop.
+                        delay(300L)
+                    }
+                }
+            }
+        }
+
+        fun stop() {
+            if (!started.getAndSet(false)) {
+                return
+            }
+            job!!.cancel()
+        }
+    }
 }
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/FakeInputBuffer.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/FakeInputBuffer.kt
new file mode 100644
index 0000000..72fc1c4
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/FakeInputBuffer.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.video.internal.encoder
+
+import androidx.concurrent.futures.ResolvableFuture
+import com.google.common.util.concurrent.ListenableFuture
+import java.nio.ByteBuffer
+
+class FakeInputBuffer : InputBuffer {
+    private val byteBuffer = ByteBuffer.allocateDirect(1024)
+    private val terminationFuture = ResolvableFuture.create<Void>()
+
+    override fun getByteBuffer(): ByteBuffer {
+        throwIfTerminated()
+        return byteBuffer
+    }
+
+    override fun setPresentationTimeUs(presentationTimeUs: Long) {
+        throwIfTerminated()
+    }
+
+    override fun setEndOfStream(isEndOfStream: Boolean) {
+        throwIfTerminated()
+    }
+
+    override fun submit(): Boolean {
+        return terminationFuture.set(null)
+    }
+
+    override fun cancel(): Boolean {
+        return terminationFuture.set(null)
+    }
+
+    override fun getTerminationFuture(): ListenableFuture<Void> {
+        return terminationFuture
+    }
+
+    private fun throwIfTerminated() {
+        check(!terminationFuture.isDone)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java
new file mode 100644
index 0000000..5a08d74
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java
@@ -0,0 +1,422 @@
+/*
+ * 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.video.internal;
+
+import static androidx.camera.video.internal.AudioSource.InternalState.CONFIGURED;
+import static androidx.camera.video.internal.AudioSource.InternalState.RELEASED;
+import static androidx.camera.video.internal.AudioSource.InternalState.STARTED;
+
+import android.annotation.SuppressLint;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.AudioTimestamp;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.camera.core.Logger;
+import androidx.camera.core.impl.Observable;
+import androidx.camera.core.impl.annotation.ExecutedBy;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.camera.core.impl.utils.futures.FutureCallback;
+import androidx.camera.core.impl.utils.futures.Futures;
+import androidx.camera.video.internal.encoder.InputBuffer;
+import androidx.core.util.Preconditions;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.Executor;
+
+/**
+ * AudioSource is used to obtain audio raw data and write to the buffer from {@link BufferProvider}.
+ *
+ * <p>The audio raw data could be one of sources from the device. The target source can be
+ * specified with {@link Builder#setAudioSource(int)}.
+ *
+ * <p>Calling {@link #start} will start reading audio data from the target source and then write
+ * the data into the buffer from {@link BufferProvider}. Calling {@link #stop} will stop sending
+ * audio data. However, to really read/write data to buffer, the {@link BufferProvider}'s state
+ * must be {@link BufferProvider.State#ACTIVE}. So recording may temporarily pause when the
+ * {@link BufferProvider}'s state is {@link BufferProvider.State#INACTIVE}.
+ *
+ * @see BufferProvider
+ * @see AudioRecord
+ */
+public final class AudioSource {
+    private static final String TAG = "AudioSource";
+
+    enum InternalState {
+        /** The initial state or when {@link #stop} is called after started. */
+        CONFIGURED,
+
+        /** The state is when it is in {@link #CONFIGURED} state and {@link #start} is called. */
+        STARTED,
+
+        /** The state is when {@link #release} is called. */
+        RELEASED,
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    final Executor mExecutor;
+
+    private final BufferProvider<InputBuffer> mBufferProvider;
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    final AudioRecord mAudioRecord;
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    final int mBufferSize;
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    InternalState mState = CONFIGURED;
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    BufferProvider.State mBufferProviderState = BufferProvider.State.INACTIVE;
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    boolean mIsSendingAudio;
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    AudioSource(@NonNull Executor executor,
+            @NonNull BufferProvider<InputBuffer> bufferProvider,
+            int audioSource,
+            int sampleRate,
+            int channelConfig,
+            int audioFormat,
+            int defaultBufferSize)
+            throws AudioSourceAccessException {
+        mExecutor = CameraXExecutors.newSequentialExecutor(Preconditions.checkNotNull(executor));
+        mBufferProvider = Preconditions.checkNotNull(bufferProvider);
+
+        int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
+        if (bufferSize <= 0) {
+            bufferSize = defaultBufferSize;
+        }
+        mBufferSize = bufferSize * 2;
+        try {
+            mAudioRecord = new AudioRecord(audioSource,
+                    sampleRate,
+                    channelConfig,
+                    audioFormat,
+                    mBufferSize);
+        } catch (IllegalArgumentException e) {
+            throw new AudioSourceAccessException("Unable to create AudioRecord", e);
+        }
+
+        if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
+            mAudioRecord.release();
+            throw new AudioSourceAccessException("Unable to initialize AudioRecord");
+        }
+
+        mBufferProvider.addObserver(mExecutor, mStateObserver);
+    }
+
+    /**
+     * Starts the AudioSource.
+     *
+     * <p>Audio data will start being sent to the {@link BufferProvider} when
+     * {@link BufferProvider}'s state is {@link BufferProvider.State#ACTIVE}.
+     *
+     * @throws IllegalStateException if the AudioSource is released.
+     */
+    public void start() {
+        mExecutor.execute(() -> {
+            switch (mState) {
+                case CONFIGURED:
+                    setState(STARTED);
+                    updateSendingAudio();
+                    break;
+                case STARTED:
+                    // Do nothing
+                    break;
+                case RELEASED:
+                    throw new IllegalStateException("AudioRecorder is released");
+            }
+        });
+    }
+
+    /**
+     * Stops the AudioSource.
+     *
+     * <p>Audio data will stop being sent to the {@link BufferProvider}.
+     *
+     * @throws IllegalStateException if it is released.
+     */
+    public void stop() {
+        mExecutor.execute(() -> {
+            switch (mState) {
+                case STARTED:
+                    setState(CONFIGURED);
+                    updateSendingAudio();
+                    break;
+                case CONFIGURED:
+                    // Do nothing
+                    break;
+                case RELEASED:
+                    throw new IllegalStateException("AudioRecorder is released");
+            }
+        });
+    }
+
+    /**
+     * Releases the AudioSource.
+     *
+     * <p>Once the AudioSource is released, it can not be used any more.
+     */
+    public void release() {
+        mExecutor.execute(() -> {
+            switch (mState) {
+                case STARTED:
+                case CONFIGURED:
+                    mBufferProvider.removeObserver(mStateObserver);
+                    mAudioRecord.release();
+                    stopSendingAudio();
+                    setState(RELEASED);
+                    break;
+                case RELEASED:
+                    // Do nothing
+                    break;
+            }
+        });
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @ExecutedBy("mExecutor")
+    void updateSendingAudio() {
+        if (mState == STARTED && mBufferProviderState == BufferProvider.State.ACTIVE) {
+            startSendingAudio();
+        } else {
+            stopSendingAudio();
+        }
+    }
+
+    @ExecutedBy("mExecutor")
+    private void startSendingAudio() {
+        if (mIsSendingAudio) {
+            // Already started, ignore
+            return;
+        }
+        try {
+            Logger.d(TAG, "startSendingAudio");
+            mAudioRecord.startRecording();
+            if (mAudioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
+                throw new IllegalStateException("Unable to start AudioRecord with state: "
+                                + mAudioRecord.getRecordingState());
+            }
+        } catch (IllegalStateException e) {
+            Logger.w(TAG, "Failed to start AudioRecord", e);
+            return;
+        }
+        mIsSendingAudio = true;
+        sendNextAudio();
+    }
+
+    @ExecutedBy("mExecutor")
+    private void stopSendingAudio() {
+        if (!mIsSendingAudio) {
+            // Already stopped, ignore.
+            return;
+        }
+        mIsSendingAudio = false;
+        try {
+            Logger.d(TAG, "stopSendingAudio");
+            mAudioRecord.stop();
+            if (mAudioRecord.getRecordingState() != AudioRecord.RECORDSTATE_STOPPED) {
+                throw new IllegalStateException("Unable to stop AudioRecord with state: "
+                        + mAudioRecord.getRecordingState());
+            }
+        } catch (IllegalStateException e) {
+            Logger.w(TAG, "Failed to stop AudioRecord", e);
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @ExecutedBy("mExecutor")
+    void sendNextAudio() {
+        Futures.addCallback(mBufferProvider.acquireBuffer(), mAcquireBufferCallback, mExecutor);
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @ExecutedBy("mExecutor")
+    void setState(InternalState state) {
+        Logger.d(TAG, "Transitioning internal state: " + mState + " --> " + state);
+        mState = state;
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @SuppressLint("UnsafeNewApiCall")
+    long generatePresentationTimeUs() {
+        long presentationTimeUs = -1;
+        if (Build.VERSION.SDK_INT >= 24) {
+            AudioTimestamp audioTimestamp = new AudioTimestamp();
+            if (mAudioRecord.getTimestamp(audioTimestamp, AudioTimestamp.TIMEBASE_MONOTONIC)
+                    == AudioRecord.SUCCESS) {
+                presentationTimeUs = audioTimestamp.nanoTime / 1000L;
+            } else {
+                Logger.w(TAG, "Unable to get audio timestamp");
+            }
+        }
+        if (presentationTimeUs == -1) {
+            presentationTimeUs = System.nanoTime() / 1000L;
+        }
+        return presentationTimeUs;
+    }
+
+    private final FutureCallback<InputBuffer> mAcquireBufferCallback =
+            new FutureCallback<InputBuffer>() {
+                @ExecutedBy("mExecutor")
+                @Override
+                public void onSuccess(InputBuffer inputBuffer) {
+                    if (!mIsSendingAudio) {
+                        inputBuffer.cancel();
+                        return;
+                    }
+                    ByteBuffer byteBuffer = inputBuffer.getByteBuffer();
+
+                    int length = mAudioRecord.read(byteBuffer, mBufferSize);
+                    if (length > 0) {
+                        byteBuffer.limit(length);
+                        inputBuffer.setPresentationTimeUs(generatePresentationTimeUs());
+                        inputBuffer.submit();
+                    } else {
+                        Logger.w(TAG, "Unable to read data from AudioRecord.");
+                        inputBuffer.cancel();
+                    }
+                    sendNextAudio();
+                }
+
+                @ExecutedBy("mExecutor")
+                @Override
+                public void onFailure(Throwable throwable) {
+                    Logger.d(TAG, "Unable to get input buffer, the BufferProvider "
+                            + "could be transitioning to INACTIVE state.");
+                }
+            };
+
+    private final Observable.Observer<BufferProvider.State> mStateObserver =
+            new Observable.Observer<BufferProvider.State>() {
+                @ExecutedBy("mExecutor")
+                @Override
+                public void onNewData(@Nullable BufferProvider.State state) {
+                    Logger.d(TAG, "Receive BufferProvider state change: "
+                            + mBufferProviderState + " to " + state);
+                    mBufferProviderState = state;
+                    updateSendingAudio();
+                }
+
+                @ExecutedBy("mExecutor")
+                @Override
+                public void onError(@NonNull Throwable t) {
+                    // Not define, should not be possible.
+                }
+            };
+
+    /**
+     * The builder of the AudioSource.
+     */
+    public static class Builder {
+        private Executor mExecutor;
+        private int mAudioSource;
+        private int mSampleRate;
+        private int mChannelConfig;
+        private int mAudioFormat;
+        private int mDefaultBufferSize;
+        private BufferProvider<InputBuffer> mBufferProvider;
+
+        /** Sets the executor to run the background task. */
+        @NonNull
+        public Builder setExecutor(@NonNull Executor executor) {
+            mExecutor = executor;
+            return this;
+        }
+
+        /**
+         * Sets the device audio source.
+         *
+         * @see android.media.MediaRecorder.AudioSource#MIC
+         * @see android.media.MediaRecorder.AudioSource#CAMCORDER
+         */
+        @NonNull
+        public Builder setAudioSource(int audioSource) {
+            mAudioSource = audioSource;
+            return this;
+        }
+
+        /** Sets the audio sample rate. */
+        @NonNull
+        public Builder setSampleRate(int sampleRate) {
+            mSampleRate = sampleRate;
+            return this;
+        }
+
+        /**
+         * Sets the channel config.
+         *
+         * @see AudioFormat#CHANNEL_IN_MONO
+         * @see AudioFormat#CHANNEL_IN_STEREO
+         */
+        @NonNull
+        public Builder setChannelConfig(int channelConfig) {
+            mChannelConfig = channelConfig;
+            return this;
+        }
+
+        /**
+         * Sets the audio format.
+         *
+         * @see AudioFormat#ENCODING_PCM_8BIT
+         * @see AudioFormat#ENCODING_PCM_16BIT
+         * @see AudioFormat#ENCODING_PCM_FLOAT
+         */
+        @NonNull
+        public Builder setAudioFormat(int audioFormat) {
+            mAudioFormat = audioFormat;
+            return this;
+        }
+
+        /**
+         * Sets the default buffer size.
+         *
+         * <p>AudioSource will try to generate a buffer size. But if it is unable to get one,
+         * it will apply this default buffer size.
+         */
+        @NonNull
+        public Builder setDefaultBufferSize(int bufferSize) {
+            mDefaultBufferSize = bufferSize;
+            return this;
+        }
+
+        /** Sets the {@link BufferProvider}. */
+        @NonNull
+        public Builder setBufferProvider(@NonNull BufferProvider<InputBuffer> bufferProvider) {
+            mBufferProvider = bufferProvider;
+            return this;
+        }
+
+        /** Build the AudioSource. */
+        @NonNull
+        public AudioSource build() throws AudioSourceAccessException {
+            return new AudioSource(mExecutor,
+                    mBufferProvider,
+                    mAudioSource,
+                    mSampleRate,
+                    mChannelConfig,
+                    mAudioFormat,
+                    mDefaultBufferSize
+            );
+        }
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/MalformedVersionException.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSourceAccessException.java
similarity index 63%
rename from car/app/app/src/main/java/androidx/car/app/MalformedVersionException.java
rename to camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSourceAccessException.java
index b685ecb..204ba20 100644
--- a/car/app/app/src/main/java/androidx/car/app/MalformedVersionException.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSourceAccessException.java
@@ -14,24 +14,22 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.camera.video.internal;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-/**
- * An exception for malformed {@link CarAppVersion} strings.
- */
-public class MalformedVersionException extends Exception {
-    public MalformedVersionException(@Nullable String message) {
+/** An exception thrown to indicate an error has occurred during configuring an audio source. */
+public class AudioSourceAccessException extends Exception {
+
+    public AudioSourceAccessException(@Nullable String message) {
         super(message);
     }
 
-    public MalformedVersionException(@NonNull String message, @NonNull Throwable cause) {
+    public AudioSourceAccessException(@Nullable String message, @Nullable Throwable cause) {
         super(message, cause);
     }
 
-    public MalformedVersionException(@Nullable Throwable cause) {
+    public AudioSourceAccessException(@Nullable Throwable cause) {
         super(cause);
     }
 }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/BufferProvider.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/BufferProvider.java
new file mode 100644
index 0000000..1dfd4c1
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/BufferProvider.java
@@ -0,0 +1,74 @@
+/*
+ * 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.video.internal;
+
+import androidx.annotation.NonNull;
+import androidx.camera.core.impl.Observable;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * BufferProvider provides buffers for writing data.
+ *
+ * <p>BufferProvider has {@link State}, it could be either {@link State#ACTIVE} or
+ * {@link State#INACTIVE}. The state can be fetched directly through {@link #fetchData()} or use
+ * {@link #addObserver} to receive state changes.
+ *
+ * <p>A buffer for writing data can be acquired with {@link #acquireBuffer()}". The buffer can
+ * only be obtained when the state is {@link State#ACTIVE}. If the state is
+ * {@link State#INACTIVE}, the {@link #acquireBuffer()} will return a failed
+ * {@link ListenableFuture} with {@link IllegalStateException}. If the state is transitioned from
+ * {@link State#ACTIVE} to {@link State#INACTIVE}, the incomplete {@link ListenableFuture} will
+ * get {@link java.util.concurrent.CancellationException}. Buffer acquisition can be cancelled
+ * with {@link ListenableFuture#cancel} if acquisition is not yet complete.
+ *
+ * @param <T> the buffer data type
+ */
+public interface BufferProvider<T> extends Observable<BufferProvider.State> {
+
+    /**
+     * Acquires a buffer.
+     *
+     * <p>A buffer can only be obtained when the state is {@link State#ACTIVE}. If the state is
+     * {@link State#INACTIVE}, the {@link #acquireBuffer()} will return an failed
+     * {@link ListenableFuture} with {@link IllegalStateException}. If the state is transitioned
+     * from {@link State#ACTIVE} to {@link State#INACTIVE}, the incomplete
+     * {@link ListenableFuture} will get {@link java.util.concurrent.CancellationException}.
+     * Buffer acquisition can be cancelled with {@link ListenableFuture#cancel} if acquisition
+     * is not yet complete.
+     *
+     * @return a {@link ListenableFuture} to represent the acquisition.
+     */
+    @NonNull
+    ListenableFuture<T> acquireBuffer();
+
+    /** The state of the BufferProvider. */
+    enum State {
+
+        /** The state means it is able to acquire a buffer. */
+        ACTIVE,
+
+        /**
+         * The state means it is not able to acquire buffer.
+         *
+         * <p>The acquisition via {@link #acquireBuffer()} will get a result with
+         * {@link IllegalStateException}.
+         */
+        INACTIVE,
+    }
+}
+
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncodedDataImpl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncodedDataImpl.java
index 723edce..1cef3f9 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncodedDataImpl.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncodedDataImpl.java
@@ -88,7 +88,7 @@
         }
         try {
             mMediaCodec.releaseOutputBuffer(mBufferIndex, false);
-        } catch (MediaCodec.CodecException e) {
+        } catch (IllegalStateException e) {
             mClosedCompleter.setException(e);
             return;
         }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/Encoder.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/Encoder.java
index da6ebce..d6fa8a5 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/Encoder.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/Encoder.java
@@ -19,8 +19,8 @@
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
+import androidx.camera.video.internal.BufferProvider;
 
-import java.nio.ByteBuffer;
 import java.util.concurrent.Executor;
 
 /**
@@ -108,18 +108,13 @@
         }
     }
 
-    /** A ByteBufferInput provides {@link #putByteBuffer} method to send raw data. */
-    interface ByteBufferInput extends EncoderInput {
-
-        /**
-         * Puts an input raw {@link ByteBuffer} to the encoder.
-         *
-         * <p>The input {@code ByteBuffer} must be put when encoder is in started and not paused
-         * state, otherwise the {@code ByteBuffer} will be dropped directly. Then the encoded data
-         * will be sent via {@link EncoderCallback#onEncodedData} callback.
-         *
-         * @param byteBuffer the input byte buffer
-         */
-        void putByteBuffer(@NonNull ByteBuffer byteBuffer);
+    /**
+     * A ByteBufferInput is a {@link BufferProvider} implementation and provides
+     * {@link InputBuffer} to write input data to the encoder.
+     *
+     * @see BufferProvider
+     * @see InputBuffer
+     */
+    interface ByteBufferInput extends EncoderInput, BufferProvider<InputBuffer> {
     }
 }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java
index 00280df..acd7535 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java
@@ -17,6 +17,7 @@
 package androidx.camera.video.internal.encoder;
 
 import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.CONFIGURED;
+import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.ERROR;
 import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.PAUSED;
 import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.PENDING_RELEASE;
 import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.PENDING_START;
@@ -36,24 +37,29 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.camera.core.Logger;
+import androidx.camera.core.impl.Observable;
+import androidx.camera.core.impl.annotation.ExecutedBy;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.impl.utils.futures.FutureCallback;
 import androidx.camera.core.impl.utils.futures.Futures;
-import androidx.core.util.Consumer;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
 import androidx.core.util.Preconditions;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
 import java.io.IOException;
-import java.nio.ByteBuffer;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * The encoder implementation.
@@ -106,20 +112,34 @@
          */
         PENDING_RELEASE,
 
+        /**
+         * Then state is when the encoder encounter error. Error state is a transitional state
+         * where encoder user is supposed to wait for {@link EncoderCallback#onEncodeStop} or
+         * {@link EncoderCallback#onEncodeError}. Any method call during this state should be
+         * ignore except {@link #release}.
+         */
+        ERROR,
+
         /** The state is when the encoder is released. */
         RELEASED,
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     final Object mLock = new Object();
+    private final InternalStateObservable mStateObservable = new InternalStateObservable();
     private final MediaFormat mMediaFormat;
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    @GuardedBy("mLock")
     final MediaCodec mMediaCodec;
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     final EncoderInput mEncoderInput;
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    final Executor mExecutor;
+    final Executor mEncoderExecutor;
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    final Queue<Integer> mFreeInputBufferIndexQueue = new ArrayDeque<>();
+    private final Queue<Completer<InputBuffer>> mAcquisitionQueue = new ArrayDeque<>();
+    private final Set<InputBuffer> mInputBufferSet = new HashSet<>();
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    final Set<EncodedDataImpl> mEncodedDataSet = new HashSet<>();
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     @GuardedBy("mLock")
@@ -128,7 +148,6 @@
     @GuardedBy("mLock")
     Executor mEncoderCallbackExecutor = CameraXExecutors.mainThreadExecutor();
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    @GuardedBy("mLock")
     InternalState mState;
 
     /**
@@ -143,10 +162,26 @@
         Preconditions.checkNotNull(executor);
         Preconditions.checkNotNull(encoderConfig);
 
-        mExecutor = CameraXExecutors.newSequentialExecutor(executor);
+        mEncoderExecutor = CameraXExecutors.newSequentialExecutor(executor);
 
         if (encoderConfig instanceof AudioEncoderConfig) {
-            mEncoderInput = new ByteBufferInput();
+            ByteBufferInput byteBufferInput = new ByteBufferInput();
+            // State change will run on mEncoderExecutor, use direct executor to avoid delay.
+            mStateObservable.addObserver(CameraXExecutors.directExecutor(),
+                    new Observable.Observer<InternalState>() {
+                        @ExecutedBy("mEncoderExecutor")
+                        @Override
+                        public void onNewData(@Nullable InternalState value) {
+                            byteBufferInput.setActive(value == STARTED);
+                        }
+
+                        @ExecutedBy("mEncoderExecutor")
+                        @Override
+                        public void onError(@NonNull Throwable t) {
+                            // No defined. Ignore.
+                        }
+                    });
+            mEncoderInput = byteBufferInput;
         } else if (encoderConfig instanceof VideoEncoderConfig) {
             mEncoderInput = new SurfaceInput();
         } else {
@@ -171,18 +206,22 @@
         setState(CONFIGURED);
     }
 
-    @SuppressWarnings("GuardedBy")
-    // It complains SurfaceInput#resetSurface and ByteBufferInput#clearFreeBuffers don't hold mLock
-    @GuardedBy("mLock")
+    @ExecutedBy("mEncoderExecutor")
     private void reset() {
+        mFreeInputBufferIndexQueue.clear();
+
+        // Cancel incomplete acquisitions if exists.
+        for (Completer<InputBuffer> completer : mAcquisitionQueue) {
+            completer.setCancelled();
+        }
+        mAcquisitionQueue.clear();
+
         mMediaCodec.reset();
         mMediaCodec.setCallback(new MediaCodecCallback());
         mMediaCodec.configure(mMediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
 
         if (mEncoderInput instanceof SurfaceInput) {
             ((SurfaceInput) mEncoderInput).resetSurface();
-        } else if (mEncoderInput instanceof ByteBufferInput) {
-            ((ByteBufferInput) mEncoderInput).clearFreeBuffers();
         }
     }
 
@@ -204,7 +243,7 @@
      */
     @Override
     public void start() {
-        synchronized (mLock) {
+        mEncoderExecutor.execute(() -> {
             switch (mState) {
                 case CONFIGURED:
                     try {
@@ -222,6 +261,7 @@
                     setState(STARTED);
                     break;
                 case STARTED:
+                case ERROR:
                 case PENDING_START:
                     // Do nothing
                     break;
@@ -235,7 +275,7 @@
                 default:
                     throw new IllegalStateException("Unknown state: " + mState);
             }
-        }
+        });
     }
 
     /**
@@ -244,21 +284,26 @@
      * <p>It will trigger {@link EncoderCallback#onEncodeStop} after the last encoded data. It can
      * call {@link #start} to start again.
      */
-    @SuppressWarnings("GuardedBy")
-    // It complains ByteBufferInput#signalEndOfInputStream doesn't hold mLock
     @Override
     public void stop() {
-        synchronized (mLock) {
+        mEncoderExecutor.execute(() -> {
             switch (mState) {
                 case CONFIGURED:
                 case STOPPING:
+                case ERROR:
                     // Do nothing
                     break;
                 case STARTED:
                 case PAUSED:
                     setState(STOPPING);
                     if (mEncoderInput instanceof ByteBufferInput) {
-                        ((ByteBufferInput) mEncoderInput).signalEndOfInputStream();
+                        // Wait for all issued input buffer done to avoid input loss.
+                        List<ListenableFuture<Void>> futures = new ArrayList<>();
+                        for (InputBuffer inputBuffer : mInputBufferSet) {
+                            futures.add(inputBuffer.getTerminationFuture());
+                        }
+                        Futures.successfulAsList(futures).addListener(this::signalEndOfInputStream,
+                                mEncoderExecutor);
                     } else if (mEncoderInput instanceof SurfaceInput) {
                         try {
                             mMediaCodec.signalEndOfInputStream();
@@ -277,7 +322,7 @@
                 default:
                     throw new IllegalStateException("Unknown state: " + mState);
             }
-        }
+        });
     }
 
     /**
@@ -288,10 +333,11 @@
      */
     @Override
     public void pause() {
-        synchronized (mLock) {
+        mEncoderExecutor.execute(() -> {
             switch (mState) {
                 case CONFIGURED:
                 case PAUSED:
+                case ERROR:
                 case STOPPING:
                 case PENDING_START_PAUSED:
                     // Do nothing
@@ -311,7 +357,7 @@
                 default:
                     throw new IllegalStateException("Unknown state: " + mState);
             }
-        }
+        });
     }
 
     /**
@@ -324,11 +370,12 @@
      */
     @Override
     public void release() {
-        synchronized (mLock) {
+        mEncoderExecutor.execute(() -> {
             switch (mState) {
                 case CONFIGURED:
                 case STARTED:
                 case PAUSED:
+                case ERROR:
                     mMediaCodec.release();
                     setState(RELEASED);
                     break;
@@ -344,7 +391,7 @@
                 default:
                     throw new IllegalStateException("Unknown state: " + mState);
             }
-        }
+        });
     }
 
     /**
@@ -363,42 +410,142 @@
         }
     }
 
-    @GuardedBy("mLock")
+    @ExecutedBy("mEncoderExecutor")
     private void setState(InternalState state) {
+        if (mState == state) {
+            return;
+        }
         Logger.d(TAG, "Transitioning encoder internal state: " + mState + " --> " + state);
         mState = state;
+        mStateObservable.notifyState();
     }
 
-    @GuardedBy("mLock")
+    @ExecutedBy("mEncoderExecutor")
     private void updatePauseToMediaCodec(boolean paused) {
         Bundle bundle = new Bundle();
         bundle.putBoolean(MediaCodec.PARAMETER_KEY_SUSPEND, paused);
         mMediaCodec.setParameters(bundle);
     }
 
+    @ExecutedBy("mEncoderExecutor")
+    private void signalEndOfInputStream() {
+        Futures.addCallback(acquireInputBuffer(),
+                new FutureCallback<InputBuffer>() {
+                    @Override
+                    public void onSuccess(InputBuffer inputBuffer) {
+                        inputBuffer.setPresentationTimeUs(generatePresentationTimeUs());
+                        inputBuffer.setEndOfStream(true);
+                        inputBuffer.submit();
+
+                        Futures.addCallback(inputBuffer.getTerminationFuture(),
+                                new FutureCallback<Void>() {
+                                    @ExecutedBy("mEncoderExecutor")
+                                    @Override
+                                    public void onSuccess(@Nullable Void result) {
+                                        // Do nothing.
+                                    }
+
+                                    @ExecutedBy("mEncoderExecutor")
+                                    @Override
+                                    public void onFailure(Throwable t) {
+                                        if (t instanceof MediaCodec.CodecException) {
+                                            handleEncodeError(
+                                                    (MediaCodec.CodecException) t);
+                                        } else {
+                                            handleEncodeError(EncodeException.ERROR_UNKNOWN,
+                                                    t.getMessage(), t);
+                                        }
+                                    }
+                                }, mEncoderExecutor);
+                    }
+
+                    @Override
+                    public void onFailure(Throwable t) {
+                        handleEncodeError(EncodeException.ERROR_UNKNOWN,
+                                "Unable to acquire InputBuffer.", t);
+                    }
+                }, mEncoderExecutor);
+    }
+
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    @GuardedBy("mLock")
+    @ExecutedBy("mEncoderExecutor")
     void handleEncodeError(@NonNull MediaCodec.CodecException e) {
         handleEncodeError(EncodeException.ERROR_CODEC, e.getMessage(), e);
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    @GuardedBy("mLock")
+    @ExecutedBy("mEncoderExecutor")
     void handleEncodeError(@EncodeException.ErrorType int error, @Nullable String message,
             @Nullable Throwable throwable) {
-        EncoderCallback encoderCallback = mEncoderCallback;
-        try {
-            mEncoderCallbackExecutor.execute(() -> encoderCallback.onEncodeError(
-                    new EncodeException(error, message, throwable)));
-        } catch (RejectedExecutionException re) {
-            Logger.e(TAG, "Unable to post to the supplied executor.", re);
+        switch (mState) {
+            case CONFIGURED:
+                // Unable to start MediaCodec. This is a fatal error. Try to reset the encoder.
+                notifyError(error, message, throwable);
+                reset();
+                break;
+            case STARTED:
+            case PAUSED:
+            case STOPPING:
+            case PENDING_START_PAUSED:
+            case PENDING_START:
+            case PENDING_RELEASE:
+                setState(ERROR);
+                stopMediaCodec(() -> notifyError(error, message, throwable));
+                break;
+            case ERROR:
+                Logger.w(TAG, "Get more than one error: " + message + "(" + error + ")",
+                        throwable);
+                break;
+            case RELEASED:
+                // Do nothing
+                break;
         }
-        mMediaCodec.stop();
-        handleStopped();
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    @GuardedBy("mLock")
+    void notifyError(@EncodeException.ErrorType int error, @Nullable String message,
+            @Nullable Throwable throwable) {
+        EncoderCallback callback;
+        Executor executor;
+        synchronized (mLock) {
+            callback = mEncoderCallback;
+            executor = mEncoderCallbackExecutor;
+        }
+        try {
+            executor.execute(
+                    () -> callback.onEncodeError(new EncodeException(error, message, throwable)));
+        } catch (RejectedExecutionException e) {
+            Logger.e(TAG, "Unable to post to the supplied executor.", e);
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @ExecutedBy("mEncoderExecutor")
+    void stopMediaCodec(@Nullable Runnable afterStop) {
+        /*
+         * MediaCodec#close will free all its input/output ByteBuffers. Therefore, before calling
+         * MediaCodec#close, it must ensure all dispatched EncodedData(output ByteBuffers) and
+         * InputBuffer(input ByteBuffers) are complete. Otherwise, the ByteBuffer receiver will
+         * get buffer overflow when accessing the ByteBuffers.
+         */
+        List<ListenableFuture<Void>> futures = new ArrayList<>();
+        for (EncodedDataImpl dataToClose : mEncodedDataSet) {
+            futures.add(dataToClose.getClosedFuture());
+        }
+        for (InputBuffer inputBuffer : mInputBufferSet) {
+            futures.add(inputBuffer.getTerminationFuture());
+        }
+        Futures.successfulAsList(futures).addListener(() -> {
+            mMediaCodec.stop();
+            if (afterStop != null) {
+                afterStop.run();
+            }
+            handleStopped();
+        }, mEncoderExecutor);
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @ExecutedBy("mEncoderExecutor")
     void handleStopped() {
         if (mState == PENDING_RELEASE) {
             mMediaCodec.release();
@@ -417,25 +564,115 @@
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @ExecutedBy("mEncoderExecutor")
+    @NonNull
+    ListenableFuture<InputBuffer> acquireInputBuffer() {
+        switch (mState) {
+            case CONFIGURED:
+                return Futures.immediateFailedFuture(new IllegalStateException(
+                        "Encoder is not started yet."));
+            case STARTED:
+            case PAUSED:
+            case STOPPING:
+            case PENDING_START:
+            case PENDING_START_PAUSED:
+            case PENDING_RELEASE:
+                AtomicReference<Completer<InputBuffer>> ref = new AtomicReference<>();
+                ListenableFuture<InputBuffer> future = CallbackToFutureAdapter.getFuture(
+                        completer -> {
+                            ref.set(completer);
+                            return "acquireInputBuffer";
+                        });
+                Completer<InputBuffer> completer = Preconditions.checkNotNull(ref.get());
+                mAcquisitionQueue.offer(completer);
+                completer.addCancellationListener(() -> mAcquisitionQueue.remove(completer),
+                        mEncoderExecutor);
+                matchAcquisitionsAndFreeBufferIndexes();
+                return future;
+            case ERROR:
+                return Futures.immediateFailedFuture(new IllegalStateException(
+                        "Encoder is in error state."));
+            case RELEASED:
+                return Futures.immediateFailedFuture(new IllegalStateException(
+                        "Encoder is released."));
+            default:
+                throw new IllegalStateException("Unknown state: " + mState);
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @ExecutedBy("mEncoderExecutor")
+    void matchAcquisitionsAndFreeBufferIndexes() {
+        while (!mAcquisitionQueue.isEmpty() && !mFreeInputBufferIndexQueue.isEmpty()) {
+            Completer<InputBuffer> completer = mAcquisitionQueue.poll();
+            int bufferIndex = mFreeInputBufferIndexQueue.poll();
+
+            InputBufferImpl inputBuffer;
+            try {
+                inputBuffer = new InputBufferImpl(mMediaCodec, bufferIndex);
+            } catch (MediaCodec.CodecException e) {
+                handleEncodeError(e);
+                return;
+            }
+            if (completer.set(inputBuffer)) {
+                mInputBufferSet.add(inputBuffer);
+                inputBuffer.getTerminationFuture().addListener(
+                        () -> mInputBufferSet.remove(inputBuffer), mEncoderExecutor);
+            } else {
+                inputBuffer.cancel();
+            }
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     static long generatePresentationTimeUs() {
         return System.nanoTime() / 1000L;
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    class InternalStateObservable implements Observable<InternalState> {
+
+        private final Map<Observer<InternalState>, Executor> mObservers = new LinkedHashMap<>();
+
+        @ExecutedBy("mEncoderExecutor")
+        void notifyState() {
+            final InternalState state = mState;
+            for (Map.Entry<Observer<InternalState>, Executor> entry : mObservers.entrySet()) {
+                entry.getValue().execute(() -> entry.getKey().onNewData(state));
+            }
+        }
+
+        @ExecutedBy("mEncoderExecutor")
+        @NonNull
+        @Override
+        public ListenableFuture<InternalState> fetchData() {
+            return Futures.immediateFuture(mState);
+        }
+
+        @ExecutedBy("mEncoderExecutor")
+        @Override
+        public void addObserver(@NonNull Executor executor,
+                @NonNull Observer<InternalState> observer) {
+            final InternalState state = mState;
+            mObservers.put(observer, executor);
+            executor.execute(() -> observer.onNewData(state));
+        }
+
+        @ExecutedBy("mEncoderExecutor")
+        @Override
+        public void removeObserver(@NonNull Observer<InternalState> observer) {
+            mObservers.remove(observer);
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     class MediaCodecCallback extends MediaCodec.Callback {
 
-        @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-        @GuardedBy("mLock")
-        final Set<EncodedDataImpl> mEncodedDataSet = new HashSet<>();
-
-        @GuardedBy("mLock")
         private boolean mHasFirstData = false;
 
-        @SuppressWarnings("GuardedBy")
-        // It complains ByteBufferInput#putFreeBufferIndex doesn't hold mLock
         @Override
         public void onInputBufferAvailable(MediaCodec mediaCodec, int index) {
-            synchronized (mLock) {
+            mEncoderExecutor.execute(() -> {
                 switch (mState) {
                     case STARTED:
                     case PAUSED:
@@ -443,24 +680,24 @@
                     case PENDING_START:
                     case PENDING_START_PAUSED:
                     case PENDING_RELEASE:
-                        if (mEncoderInput instanceof ByteBufferInput) {
-                            ((ByteBufferInput) mEncoderInput).putFreeBufferIndex(index);
-                        }
+                        mFreeInputBufferIndexQueue.offer(index);
+                        matchAcquisitionsAndFreeBufferIndexes();
                         break;
                     case CONFIGURED:
+                    case ERROR:
                     case RELEASED:
                         // Do nothing
                         break;
                     default:
                         throw new IllegalStateException("Unknown state: " + mState);
                 }
-            }
+            });
         }
 
         @Override
         public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec, int index,
                 @NonNull MediaCodec.BufferInfo bufferInfo) {
-            synchronized (mLock) {
+            mEncoderExecutor.execute(() -> {
                 switch (mState) {
                     case STARTED:
                     case PAUSED:
@@ -468,8 +705,12 @@
                     case PENDING_START:
                     case PENDING_START_PAUSED:
                     case PENDING_RELEASE:
-                        final EncoderCallback encoderCallback = mEncoderCallback;
-                        final Executor executor = mEncoderCallbackExecutor;
+                        final EncoderCallback encoderCallback;
+                        final Executor executor;
+                        synchronized (mLock) {
+                            encoderCallback = mEncoderCallback;
+                            executor = mEncoderCallbackExecutor;
+                        }
 
                         // Handle start of stream
                         if (!mHasFirstData) {
@@ -507,25 +748,21 @@
                                     new FutureCallback<Void>() {
                                         @Override
                                         public void onSuccess(@Nullable Void result) {
-                                            synchronized (mLock) {
-                                                mEncodedDataSet.remove(encodedData);
-                                            }
+                                            mEncodedDataSet.remove(encodedData);
                                         }
 
                                         @Override
                                         public void onFailure(Throwable t) {
-                                            synchronized (mLock) {
-                                                mEncodedDataSet.remove(encodedData);
-                                                if (t instanceof MediaCodec.CodecException) {
-                                                    handleEncodeError(
-                                                            (MediaCodec.CodecException) t);
-                                                } else {
-                                                    handleEncodeError(EncodeException.ERROR_UNKNOWN,
-                                                            t.getMessage(), t);
-                                                }
+                                            mEncodedDataSet.remove(encodedData);
+                                            if (t instanceof MediaCodec.CodecException) {
+                                                handleEncodeError(
+                                                        (MediaCodec.CodecException) t);
+                                            } else {
+                                                handleEncodeError(EncodeException.ERROR_UNKNOWN,
+                                                        t.getMessage(), t);
                                             }
                                         }
-                                    }, CameraXExecutors.directExecutor());
+                                    }, mEncoderExecutor);
                             try {
                                 executor.execute(() -> encoderCallback.onEncodedData(encodedData));
                             } catch (RejectedExecutionException e) {
@@ -543,56 +780,29 @@
 
                         // Handle end of stream
                         if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                            // Wait for all data closed
-                            List<ListenableFuture<Void>> waitForCloseFutures = new ArrayList<>();
-                            for (EncodedDataImpl dataToClose : mEncodedDataSet) {
-                                waitForCloseFutures.add(dataToClose.getClosedFuture());
-                            }
-                            Futures.addCallback(Futures.allAsList(waitForCloseFutures),
-                                    new FutureCallback<List<Void>>() {
-                                        @Override
-                                        public void onSuccess(@Nullable List<Void> result) {
-                                            synchronized (mLock) {
-                                                mMediaCodec.stop();
-                                                try {
-                                                    executor.execute(encoderCallback::onEncodeStop);
-                                                } catch (RejectedExecutionException e) {
-                                                    Logger.e(TAG,
-                                                            "Unable to post to the supplied "
-                                                                    + "executor.", e);
-                                                }
-                                                handleStopped();
-                                            }
-                                        }
-
-                                        @Override
-                                        public void onFailure(Throwable t) {
-                                            synchronized (mLock) {
-                                                if (t instanceof MediaCodec.CodecException) {
-                                                    handleEncodeError(
-                                                            (MediaCodec.CodecException) t);
-                                                } else {
-                                                    handleEncodeError(EncodeException.ERROR_UNKNOWN,
-                                                            t.getMessage(), t);
-                                                }
-                                            }
-                                        }
-                                    }, CameraXExecutors.directExecutor());
+                            stopMediaCodec(() -> {
+                                try {
+                                    executor.execute(encoderCallback::onEncodeStop);
+                                } catch (RejectedExecutionException e) {
+                                    Logger.e(TAG, "Unable to post to the supplied executor.", e);
+                                }
+                            });
                         }
                         break;
                     case CONFIGURED:
+                    case ERROR:
                     case RELEASED:
                         // Do nothing
                         break;
                     default:
                         throw new IllegalStateException("Unknown state: " + mState);
                 }
-            }
+            });
         }
 
         @Override
         public void onError(@NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
-            synchronized (mLock) {
+            mEncoderExecutor.execute(() -> {
                 switch (mState) {
                     case STARTED:
                     case PAUSED:
@@ -603,19 +813,20 @@
                         handleEncodeError(e);
                         break;
                     case CONFIGURED:
+                    case ERROR:
                     case RELEASED:
                         // Do nothing
                         break;
                     default:
                         throw new IllegalStateException("Unknown state: " + mState);
                 }
-            }
+            });
         }
 
         @Override
         public void onOutputFormatChanged(@NonNull MediaCodec mediaCodec,
                 @NonNull MediaFormat mediaFormat) {
-            synchronized (mLock) {
+            mEncoderExecutor.execute(() -> {
                 switch (mState) {
                     case STARTED:
                     case PAUSED:
@@ -623,28 +834,36 @@
                     case PENDING_START:
                     case PENDING_START_PAUSED:
                     case PENDING_RELEASE:
-                        EncoderCallback encoderCallback = mEncoderCallback;
+                        EncoderCallback encoderCallback;
+                        Executor executor;
+                        synchronized (mLock) {
+                            encoderCallback = mEncoderCallback;
+                            executor = mEncoderCallbackExecutor;
+                        }
                         try {
-                            mEncoderCallbackExecutor.execute(
+                            executor.execute(
                                     () -> encoderCallback.onOutputConfigUpdate(() -> mediaFormat));
                         } catch (RejectedExecutionException e) {
                             Logger.e(TAG, "Unable to post to the supplied executor.", e);
                         }
                         break;
                     case CONFIGURED:
+                    case ERROR:
                     case RELEASED:
                         // Do nothing
                         break;
                     default:
                         throw new IllegalStateException("Unknown state: " + mState);
                 }
-            }
+            });
         }
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     class SurfaceInput implements Encoder.SurfaceInput {
 
+        private final Object mLock = new Object();
+
         @GuardedBy("mLock")
         private Surface mSurface;
 
@@ -663,42 +882,50 @@
         @Override
         public void setOnSurfaceUpdateListener(@NonNull Executor executor,
                 @NonNull OnSurfaceUpdateListener listener) {
+            Surface surface;
             synchronized (mLock) {
                 mSurfaceUpdateListener = Preconditions.checkNotNull(listener);
                 mSurfaceUpdateExecutor = Preconditions.checkNotNull(executor);
-
-                if (mSurface != null) {
-                    notifySurfaceUpdate(mSurface);
-                }
-
+                surface = mSurface;
+            }
+            if (surface != null) {
+                notifySurfaceUpdate(executor, listener, surface);
             }
         }
 
-        @GuardedBy("mLock")
         @SuppressLint("UnsafeNewApiCall")
         void resetSurface() {
-            if (Build.VERSION.SDK_INT >= 23) {
-                if (mSurface == null) {
-                    mSurface = MediaCodec.createPersistentInputSurface();
-                    notifySurfaceUpdate(mSurface);
+            Surface surface;
+            Executor executor;
+            OnSurfaceUpdateListener listener;
+            synchronized (mLock) {
+                if (Build.VERSION.SDK_INT >= 23) {
+                    if (mSurface == null) {
+                        mSurface = MediaCodec.createPersistentInputSurface();
+                        surface = mSurface;
+                    } else {
+                        surface = null;
+                    }
+                    mMediaCodec.setInputSurface(mSurface);
+                } else {
+                    mSurface = mMediaCodec.createInputSurface();
+                    surface = mSurface;
                 }
-                mMediaCodec.setInputSurface(mSurface);
-            } else {
-                mSurface = mMediaCodec.createInputSurface();
-                notifySurfaceUpdate(mSurface);
+                listener = mSurfaceUpdateListener;
+                executor = mSurfaceUpdateExecutor;
+            }
+            if (surface != null && listener != null && executor != null) {
+                notifySurfaceUpdate(executor, listener, surface);
             }
         }
 
-        @GuardedBy("mLock")
-        private void notifySurfaceUpdate(@NonNull Surface surface) {
-            if (mSurfaceUpdateListener != null && mSurfaceUpdateExecutor != null) {
-                OnSurfaceUpdateListener listener = mSurfaceUpdateListener;
-                try {
-                    mSurfaceUpdateExecutor.execute(() -> listener.onSurfaceUpdate(surface));
-                } catch (RejectedExecutionException e) {
-                    Logger.e(TAG, "Unable to post to the supplied executor.", e);
-                    surface.release();
-                }
+        private void notifySurfaceUpdate(@NonNull Executor executor,
+                @NonNull OnSurfaceUpdateListener listener, @NonNull Surface surface) {
+            try {
+                executor.execute(() -> listener.onSurfaceUpdate(surface));
+            } catch (RejectedExecutionException e) {
+                Logger.e(TAG, "Unable to post to the supplied executor.", e);
+                surface.release();
             }
         }
     }
@@ -706,139 +933,91 @@
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     class ByteBufferInput implements Encoder.ByteBufferInput {
 
-        @GuardedBy("mLock")
-        private final Queue<Consumer<Integer>> mListenerQueue = new ArrayDeque<>();
+        private final Map<Observer<State>, Executor> mStateObservers = new LinkedHashMap<>();
 
-        @GuardedBy("mLock")
-        private final Queue<Integer> mFreeBufferIndexQueue = new ArrayDeque<>();
+        private State mBufferProviderState = State.INACTIVE;
+
+        private final List<ListenableFuture<InputBuffer>> mAcquisitionList = new ArrayList<>();
 
         /** {@inheritDoc} */
+        @NonNull
         @Override
-        public void putByteBuffer(@NonNull ByteBuffer byteBuffer) {
-            synchronized (mLock) {
-                switch (mState) {
-                    case STARTED:
-                        // Here it means the byteBuffer should definitely be queued into codec.
-                        acquireFreeBufferIndex(freeBufferIndex -> {
-                            ByteBuffer inputBuffer = null;
-                            synchronized (mLock) {
-                                if (mState == STARTED
-                                        || mState == PAUSED
-                                        || mState == STOPPING
-                                        || mState == PENDING_START
-                                        || mState == PENDING_START_PAUSED
-                                        || mState == PENDING_RELEASE) {
-                                    try {
-                                        inputBuffer = mMediaCodec.getInputBuffer(freeBufferIndex);
-                                    } catch (MediaCodec.CodecException e) {
-                                        handleEncodeError(e);
-                                        return;
-                                    }
-                                }
-                            }
-
-                            if (inputBuffer == null) {
-                                return;
-                            }
-                            inputBuffer.put(byteBuffer);
-
-                            synchronized (mLock) {
-                                if (mState == STARTED
-                                        || mState == PAUSED
-                                        || mState == STOPPING
-                                        || mState == PENDING_START
-                                        || mState == PENDING_START_PAUSED
-                                        || mState == PENDING_RELEASE) {
-                                    try {
-                                        mMediaCodec.queueInputBuffer(freeBufferIndex, 0,
-                                                inputBuffer.position(),
-                                                generatePresentationTimeUs(),
-                                                0);
-                                    } catch (MediaCodec.CodecException e) {
-                                        handleEncodeError(e);
-                                        return;
-                                    }
-                                }
-                            }
-                        });
-                        break;
-                    case PAUSED:
-                        // Drop the data
-                        break;
-                    case CONFIGURED:
-                    case STOPPING:
-                    case PENDING_START:
-                    case PENDING_RELEASE:
-                    case RELEASED:
-                        // Do nothing
-                        break;
-                    default:
-                        throw new IllegalStateException("Unknown state: " + mState);
-                }
-            }
-        }
-
-        @GuardedBy("mLock")
-        void signalEndOfInputStream() {
-            acquireFreeBufferIndex(freeBufferIndex -> {
-                synchronized (mLock) {
-                    switch (mState) {
-                        case STARTED:
-                        case PAUSED:
-                        case STOPPING:
-                        case PENDING_START:
-                        case PENDING_START_PAUSED:
-                        case PENDING_RELEASE:
-                            try {
-                                mMediaCodec.queueInputBuffer(freeBufferIndex, 0, 0,
-                                        generatePresentationTimeUs(),
-                                        MediaCodec.BUFFER_FLAG_END_OF_STREAM);
-                            } catch (MediaCodec.CodecException e) {
-                                handleEncodeError(e);
-                            }
-                            break;
-                        case CONFIGURED:
-                        case RELEASED:
-                            // Do nothing
-                            break;
-                        default:
-                            throw new IllegalStateException("Unknown state: " + mState);
-                    }
-                }
+        public ListenableFuture<State> fetchData() {
+            return CallbackToFutureAdapter.getFuture(completer -> {
+                mEncoderExecutor.execute(() -> completer.set(mBufferProviderState));
+                return "fetchData";
             });
         }
 
-        @GuardedBy("mLock")
-        void putFreeBufferIndex(int index) {
-            mFreeBufferIndexQueue.offer(index);
-            match();
+        /** {@inheritDoc} */
+        @NonNull
+        @Override
+        public ListenableFuture<InputBuffer> acquireBuffer() {
+            return CallbackToFutureAdapter.getFuture(completer -> {
+                mEncoderExecutor.execute(() -> {
+                    if (mBufferProviderState == State.ACTIVE) {
+                        ListenableFuture<InputBuffer> future = acquireInputBuffer();
+                        Futures.propagate(future, completer);
+                        // Cancel by outer, also cancel internal future.
+                        completer.addCancellationListener(() -> future.cancel(true),
+                                CameraXExecutors.directExecutor());
+
+                        // Keep tracking the acquisition by internal future. Once the provider state
+                        // transition to inactive, cancel the internal future can also send signal
+                        // to outer future since we propagate the internal result to the completer.
+                        mAcquisitionList.add(future);
+                        future.addListener(() -> mAcquisitionList.remove(future), mEncoderExecutor);
+                    } else if (mBufferProviderState == State.INACTIVE) {
+                        completer.setException(
+                                new IllegalStateException("BufferProvider is not active."));
+                    } else {
+                        completer.setException(
+                                new IllegalStateException(
+                                        "Unknown state: " + mBufferProviderState));
+                    }
+                });
+                return "acquireBuffer";
+            });
         }
 
-        @GuardedBy("mLock")
-        void clearFreeBuffers() {
-            mListenerQueue.clear();
-            mFreeBufferIndexQueue.clear();
+        /** {@inheritDoc} */
+        @Override
+        public void addObserver(@NonNull Executor executor, @NonNull Observer<State> observer) {
+            mEncoderExecutor.execute(() -> {
+                mStateObservers.put(Preconditions.checkNotNull(observer),
+                        Preconditions.checkNotNull(executor));
+                final State state = mBufferProviderState;
+                executor.execute(() -> observer.onNewData(state));
+            });
         }
 
-        @GuardedBy("mLock")
-        private void acquireFreeBufferIndex(
-                @NonNull Consumer<Integer> onFreeBufferIndexListener) {
-            synchronized (mLock) {
-                mListenerQueue.offer(onFreeBufferIndexListener);
-                match();
+        /** {@inheritDoc} */
+        @Override
+        public void removeObserver(@NonNull Observer<State> observer) {
+            mEncoderExecutor.execute(
+                    () -> mStateObservers.remove(Preconditions.checkNotNull(observer)));
+        }
+
+        @ExecutedBy("mEncoderExecutor")
+        void setActive(boolean isActive) {
+            final State newState = isActive ? State.ACTIVE : State.INACTIVE;
+            if (mBufferProviderState == newState) {
+                return;
             }
-        }
+            mBufferProviderState = newState;
 
-        @GuardedBy("mLock")
-        private void match() {
-            if (!mListenerQueue.isEmpty() && !mFreeBufferIndexQueue.isEmpty()) {
-                Consumer<Integer> listener = mListenerQueue.poll();
-                Integer index = mFreeBufferIndexQueue.poll();
+            if (newState == State.INACTIVE) {
+                for (ListenableFuture<InputBuffer> future : mAcquisitionList) {
+                    future.cancel(true);
+                }
+                mAcquisitionList.clear();
+            }
+
+            for (Map.Entry<Observer<State>, Executor> entry : mStateObservers.entrySet()) {
                 try {
-                    mExecutor.execute(() -> listener.accept(index));
+                    entry.getValue().execute(() -> entry.getKey().onNewData(newState));
                 } catch (RejectedExecutionException e) {
                     Logger.e(TAG, "Unable to post to the supplied executor.", e);
-                    putFreeBufferIndex(index);
                 }
             }
         }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/InputBuffer.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/InputBuffer.java
new file mode 100644
index 0000000..fba76c7
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/InputBuffer.java
@@ -0,0 +1,87 @@
+/*
+ * 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.video.internal.encoder;
+
+import androidx.annotation.NonNull;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.nio.ByteBuffer;
+
+/**
+ * InputBuffer is provided by the {@link Encoder} and used to feed data into the {@link Encoder}.
+ *
+ * <p>Once {@link InputBuffer} is complete or no longer needed, {@link #submit} or
+ * {@link #cancel} must be called to return the request to the encoder, otherwise, it will cause
+ * leakage or failure.
+ */
+public interface InputBuffer {
+
+    /**
+     * Gets the {@link ByteBuffer} of the input buffer.
+     *
+     * <p>Before submitting the InputBuffer, the internal position of the ByteBuffer must be set
+     * to prepare it for reading, e.g. the {@link ByteBuffer#position} is the beginning of the
+     * data, usually 0; the {@link ByteBuffer#limit} is the end of the data. Usually
+     * {@link ByteBuffer#flip} is used after writing data.
+     *
+     * <p>Getting ByteBuffer multiple times won't reset its internal position and data.
+     *
+     * @throws {@link IllegalStateException} if InputBuffer is submitted or canceled.
+     */
+    @NonNull
+    ByteBuffer getByteBuffer();
+
+    /**
+     * Sets the timestamp of the input buffer in microseconds.
+     *
+     * @throws {@link IllegalStateException} if InputBuffer is submitted or canceled.
+     */
+    void setPresentationTimeUs(long presentationTimeUs);
+
+    /**
+     * Denotes the input buffer is the end of the data stream.
+     *
+     * @throws {@link IllegalStateException} if InputBuffer is submitted or canceled.
+     */
+    void setEndOfStream(boolean isEndOfStream);
+
+    /**
+     * Submits the input buffer.
+     *
+     * <p>The data will be written to encoder only when {@link #submit} is called.
+     *
+     * @return {@code true} if submit successfully; {@code false} if already submitted, failed or
+     * has been canceled.
+     */
+    boolean submit();
+
+    /**
+     * Returns the request to encoder without taking any effect.
+     *
+     * @return {@code true} if cancel successfully; {@code false} if already submitted, failed or
+     * has been canceled.
+     */
+    boolean cancel();
+
+    /**
+     * The {@link ListenableFuture} that is complete when {@link #submit} or {@link #cancel} is
+     * called.
+     */
+    @NonNull
+    ListenableFuture<Void> getTerminationFuture();
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/InputBufferImpl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/InputBufferImpl.java
new file mode 100644
index 0000000..3f88d73
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/InputBufferImpl.java
@@ -0,0 +1,128 @@
+/*
+ * 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.video.internal.encoder;
+
+import android.media.MediaCodec;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.camera.core.impl.utils.futures.Futures;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
+import androidx.core.util.Preconditions;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+class InputBufferImpl implements InputBuffer {
+    private final MediaCodec mMediaCodec;
+    private final int mBufferIndex;
+    private final ByteBuffer mByteBuffer;
+    private final ListenableFuture<Void> mTerminationFuture;
+    private final CallbackToFutureAdapter.Completer<Void> mTerminationCompleter;
+    private final AtomicBoolean mTerminated = new AtomicBoolean(false);
+    private long mPresentationTimeUs = 0L;
+    private boolean mIsEndOfStream = false;
+
+    InputBufferImpl(@NonNull MediaCodec mediaCodec, @IntRange(from = 0) int bufferIndex)
+            throws MediaCodec.CodecException {
+        mMediaCodec = Preconditions.checkNotNull(mediaCodec);
+        mBufferIndex = Preconditions.checkArgumentNonnegative(bufferIndex);
+        mByteBuffer = mediaCodec.getInputBuffer(bufferIndex);
+        AtomicReference<Completer<Void>> ref = new AtomicReference<>();
+        mTerminationFuture = CallbackToFutureAdapter.getFuture(
+                completer -> {
+                    ref.set(completer);
+                    return "Terminate InputBuffer";
+                });
+        mTerminationCompleter = Preconditions.checkNotNull(ref.get());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @NonNull
+    public ByteBuffer getByteBuffer() {
+        throwIfTerminated();
+        return mByteBuffer;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setPresentationTimeUs(long presentationTimeUs) {
+        throwIfTerminated();
+        Preconditions.checkArgument(presentationTimeUs >= 0L);
+        mPresentationTimeUs = presentationTimeUs;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setEndOfStream(boolean endOfStream) {
+        throwIfTerminated();
+        mIsEndOfStream = endOfStream;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean submit() {
+        if (mTerminated.getAndSet(true)) {
+            return false;
+        }
+        try {
+            mMediaCodec.queueInputBuffer(mBufferIndex,
+                    mByteBuffer.position(),
+                    mByteBuffer.limit(),
+                    mPresentationTimeUs,
+                    mIsEndOfStream ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+            mTerminationCompleter.set(null);
+            return true;
+        } catch (IllegalStateException e) {
+            mTerminationCompleter.setException(e);
+            return false;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean cancel() {
+        if (mTerminated.getAndSet(true)) {
+            return false;
+        }
+        try {
+            mMediaCodec.queueInputBuffer(mBufferIndex, 0, 0, 0, 0);
+            mTerminationCompleter.set(null);
+        } catch (IllegalStateException e) {
+            mTerminationCompleter.setException(e);
+        }
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @NonNull
+    public ListenableFuture<Void> getTerminationFuture() {
+        return Futures.nonCancellationPropagating(mTerminationFuture);
+    }
+
+    private void throwIfTerminated() {
+        if (mTerminated.get()) {
+            throw new IllegalStateException("The buffer is submitted or canceled.");
+        }
+    }
+}
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewTransformationDeviceTest.kt b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewTransformationDeviceTest.kt
index cf225ff..1af8754 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewTransformationDeviceTest.kt
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewTransformationDeviceTest.kt
@@ -36,9 +36,9 @@
  */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class PreviewTransformationDeviceTest {
+public class PreviewTransformationDeviceTest {
 
-    companion object {
+    public companion object {
         // Size of the PreviewView. Aspect ratio 2:1.
         private val PREVIEW_VIEW_SIZE = Size(400, 200)
         private val PIVOTED_PREVIEW_VIEW_SIZE =
@@ -71,13 +71,13 @@
     private lateinit var mView: View
 
     @Before
-    fun setUp() {
+    public fun setUp() {
         mPreviewTransform = PreviewTransformation()
         mView = View(ApplicationProvider.getApplicationContext())
     }
 
     @Test
-    fun cropRectWidthOffByOnePixel_match() {
+    public fun cropRectWidthOffByOnePixel_match() {
         assertThat(
             isCropRectAspectRatioMatchPreviewView(
                 Rect(
@@ -91,7 +91,7 @@
     }
 
     @Test
-    fun cropRectWidthOffByTwoPixels_mismatch() {
+    public fun cropRectWidthOffByTwoPixels_mismatch() {
         assertThat(
             isCropRectAspectRatioMatchPreviewView(
                 Rect(
@@ -115,7 +115,7 @@
     }
 
     @Test
-    fun correctTextureViewWith0Rotation() {
+    public fun correctTextureViewWith0Rotation() {
         assertThat(getTextureViewCorrection(Surface.ROTATION_0)).isEqualTo(
             floatArrayOf(
                 0f,
@@ -131,7 +131,7 @@
     }
 
     @Test
-    fun correctTextureViewWith90Rotation() {
+    public fun correctTextureViewWith90Rotation() {
         assertThat(getTextureViewCorrection(Surface.ROTATION_90)).isEqualTo(
             floatArrayOf(
                 0f,
@@ -147,7 +147,7 @@
     }
 
     @Test
-    fun correctTextureViewWith180Rotation() {
+    public fun correctTextureViewWith180Rotation() {
         assertThat(getTextureViewCorrection(Surface.ROTATION_180)).isEqualTo(
             floatArrayOf(
                 SURFACE_SIZE.width.toFloat(),
@@ -163,7 +163,7 @@
     }
 
     @Test
-    fun correctTextureViewWith270Rotation() {
+    public fun correctTextureViewWith270Rotation() {
         assertThat(getTextureViewCorrection(Surface.ROTATION_270)).isEqualTo(
             floatArrayOf(
                 SURFACE_SIZE.width.toFloat(),
@@ -196,7 +196,7 @@
     }
 
     @Test
-    fun ratioMatch_surfaceIsScaledToFillPreviewView() {
+    public fun ratioMatch_surfaceIsScaledToFillPreviewView() {
         // Arrange.
         mPreviewTransform.setTransformationInfo(
             SurfaceRequest.TransformationInfo.of(
@@ -225,7 +225,7 @@
     }
 
     @Test
-    fun mismatchedCropRect_fitStart() {
+    public fun mismatchedCropRect_fitStart() {
         assertForMismatchedCropRect(
             PreviewView.ScaleType.FIT_START,
             LayoutDirection.LTR,
@@ -237,7 +237,7 @@
     }
 
     @Test
-    fun mismatchedCropRect_fitCenter() {
+    public fun mismatchedCropRect_fitCenter() {
         assertForMismatchedCropRect(
             PreviewView.ScaleType.FIT_CENTER,
             LayoutDirection.LTR,
@@ -249,7 +249,7 @@
     }
 
     @Test
-    fun mismatchedCropRect_fitEnd() {
+    public fun mismatchedCropRect_fitEnd() {
         assertForMismatchedCropRect(
             PreviewView.ScaleType.FIT_END,
             LayoutDirection.LTR,
@@ -261,7 +261,7 @@
     }
 
     @Test
-    fun mismatchedCropRectFrontCamera_fitStart() {
+    public fun mismatchedCropRectFrontCamera_fitStart() {
         assertForMismatchedCropRect(
             PreviewView.ScaleType.FIT_START,
             LayoutDirection.LTR,
@@ -273,7 +273,7 @@
     }
 
     @Test
-    fun mismatchedCropRect_fillStart() {
+    public fun mismatchedCropRect_fillStart() {
         assertForMismatchedCropRect(
             PreviewView.ScaleType.FILL_START,
             LayoutDirection.LTR,
@@ -285,7 +285,7 @@
     }
 
     @Test
-    fun mismatchedCropRect_fillCenter() {
+    public fun mismatchedCropRect_fillCenter() {
         assertForMismatchedCropRect(
             PreviewView.ScaleType.FILL_CENTER,
             LayoutDirection.LTR,
@@ -297,7 +297,7 @@
     }
 
     @Test
-    fun mismatchedCropRect_fillEnd() {
+    public fun mismatchedCropRect_fillEnd() {
         assertForMismatchedCropRect(
             PreviewView.ScaleType.FILL_END,
             LayoutDirection.LTR,
@@ -309,7 +309,7 @@
     }
 
     @Test
-    fun mismatchedCropRect_fitStartWithRtl_actsLikeFitEnd() {
+    public fun mismatchedCropRect_fitStartWithRtl_actsLikeFitEnd() {
         assertForMismatchedCropRect(
             PreviewView.ScaleType.FIT_START,
             LayoutDirection.RTL,
@@ -347,7 +347,7 @@
     }
 
     @Test
-    fun frontCamera0_transformationIsMirrored() {
+    public fun frontCamera0_transformationIsMirrored() {
         testOffCenterCropRectMirroring(FRONT_CAMERA, CROP_RECT_0, PREVIEW_VIEW_SIZE, 0)
 
         // Assert:
@@ -358,7 +358,7 @@
     }
 
     @Test
-    fun backCamera0_transformationIsNotMirrored() {
+    public fun backCamera0_transformationIsNotMirrored() {
         testOffCenterCropRectMirroring(BACK_CAMERA, CROP_RECT_0, PREVIEW_VIEW_SIZE, 0)
 
         // Assert:
@@ -369,7 +369,7 @@
     }
 
     @Test
-    fun frontCameraRotated90_transformationIsMirrored() {
+    public fun frontCameraRotated90_transformationIsMirrored() {
         testOffCenterCropRectMirroring(
             FRONT_CAMERA, CROP_RECT_90, PIVOTED_PREVIEW_VIEW_SIZE, 90
         )
@@ -382,7 +382,7 @@
     }
 
     @Test
-    fun backCameraRotated90_transformationIsNotMirrored() {
+    public fun backCameraRotated90_transformationIsNotMirrored() {
         testOffCenterCropRectMirroring(BACK_CAMERA, CROP_RECT_90, PIVOTED_PREVIEW_VIEW_SIZE, 90)
 
         // Assert:
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewBitmapTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewBitmapTest.java
index b8a1f6e..1d086ff 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewBitmapTest.java
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewBitmapTest.java
@@ -35,10 +35,10 @@
 import androidx.camera.view.PreviewView.ImplementationMode;
 import androidx.lifecycle.Observer;
 import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.After;
 import org.junit.Assume;
@@ -59,8 +59,9 @@
 public class PreviewViewBitmapTest {
 
     @Rule
-    public final ActivityTestRule<FakeActivity> mActivityRule =
-            new ActivityTestRule<>(FakeActivity.class);
+    public final ActivityScenarioRule<FakeActivity> mActivityRule = new ActivityScenarioRule<>(
+            FakeActivity.class);
+
     @Rule
     public final TestRule mUseCamera = CameraUtil.grantCameraPermissionAndPreTest();
 
@@ -87,6 +88,7 @@
         if (mCameraProvider != null) {
             runOnMainThread(() -> mCameraProvider.unbindAll());
             mCameraProvider.shutdown().get();
+            mCameraProvider = null;
         }
     }
 
@@ -109,7 +111,6 @@
         final FakeLifecycleOwner lifecycleOwner = new FakeLifecycleOwner();
         lifecycleOwner.startAndResume();
 
-
         runOnMainThread(() -> {
             // Act.
             preview.setSurfaceProvider(previewView.getSurfaceProvider());
@@ -277,7 +278,8 @@
             PreviewView previewView = new PreviewView(ApplicationProvider.getApplicationContext());
             previewView.setImplementationMode(mode);
             previewView.setScaleType(scaleType);
-            mActivityRule.getActivity().setContentView(previewView);
+            mActivityRule.getScenario().onActivity(
+                    activity -> activity.setContentView(previewView));
             previewViewAtomicReference.set(previewView);
         });
         return previewViewAtomicReference.get();
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewMeteringPointFactoryDeviceTest.kt b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewMeteringPointFactoryDeviceTest.kt
index 0e9e196..ef4277f 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewMeteringPointFactoryDeviceTest.kt
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewMeteringPointFactoryDeviceTest.kt
@@ -33,7 +33,7 @@
  */
 @SmallTest
 @RunWith(Parameterized::class)
-class PreviewViewMeteringPointFactoryDeviceTest(
+public class PreviewViewMeteringPointFactoryDeviceTest(
     private val cropRect: Rect,
     private val rotationDegrees: Int,
     private val surfaceSize: Size,
@@ -45,7 +45,7 @@
     private val expectedMeteringPoint: PointF
 ) {
 
-    companion object {
+    public companion object {
 
         private const val FRONT_CAMERA = true
         private const val BACK_CAMERA = false
@@ -73,7 +73,7 @@
 
         @JvmStatic
         @Parameterized.Parameters
-        fun data(): Collection<Array<Any>> {
+        public fun data(): Collection<Array<Any>> {
 
             return listOf(
                 // Device in sensor orientation without crop rect.
@@ -195,7 +195,7 @@
     private val instrumentation = InstrumentationRegistry.getInstrumentation()
 
     @Test
-    fun verifyMeteringPoint() {
+    public fun verifyMeteringPoint() {
         // Arrange.
         val previewTransformation = PreviewTransformation()
         previewTransformation.scaleType = scaleType
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewStreamStateTest.kt b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewStreamStateTest.kt
index cccc54e..62990f0 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewStreamStateTest.kt
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewStreamStateTest.kt
@@ -16,9 +16,7 @@
 
 package androidx.camera.view
 
-import android.app.Activity
 import android.content.Context
-import android.view.View
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.CameraX
@@ -32,6 +30,7 @@
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.Observer
 import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.filters.LargeTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.google.common.truth.Truth.assertThat
@@ -49,18 +48,19 @@
 
 @LargeTest
 @RunWith(Parameterized::class)
-class PreviewViewStreamStateTest(private val implMode: PreviewView.ImplementationMode) {
-    companion object {
+public class PreviewViewStreamStateTest(private val implMode: PreviewView.ImplementationMode) {
+
+    public companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
-        fun data() = arrayOf(
+        public fun data(): Array<PreviewView.ImplementationMode> = arrayOf(
             PreviewView.ImplementationMode.COMPATIBLE,
             PreviewView.ImplementationMode.PERFORMANCE
         )
 
         @BeforeClass
         @JvmStatic
-        fun classSetUp() {
+        public fun classSetUp() {
             CoreAppTestUtil.prepareDeviceUI(InstrumentationRegistry.getInstrumentation())
         }
     }
@@ -72,49 +72,37 @@
     private lateinit var mCameraProvider: ProcessCameraProvider
 
     @get:Rule
-    val mUseCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+    public val mUseCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
 
-    @Suppress("DEPRECATION")
     @get:Rule
-    var mActivityRule = androidx.test.rule.ActivityTestRule(
-        FakeActivity::class.java
-    )
-
-    @Throws(Throwable::class)
-    private fun setContentView(view: View) {
-        val activity: Activity = mActivityRule.activity
-        mActivityRule.runOnUiThread { activity.setContentView(view) }
-    }
+    public val mActivityRule: ActivityScenarioRule<FakeActivity> =
+        ActivityScenarioRule(FakeActivity::class.java)
 
     @Before
-    fun setUp() {
+    public fun setUp() {
         Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
         CoreAppTestUtil.assumeCompatibleDevice()
 
-        mIsSetup = true
-
         val context = ApplicationProvider.getApplicationContext<Context>()
         val config = Camera2Config.defaultConfig()
         CameraX.initialize(context, config)
-        mLifecycle = FakeLifecycleOwner()
-        mInstrumentation.runOnMainSync {
-            mPreviewView = PreviewView(context)
-        }
-        setContentView(mPreviewView)
-        mInstrumentation.runOnMainSync {
-            mPreviewView.implementationMode = implMode
-        }
 
+        mLifecycle = FakeLifecycleOwner()
+        mActivityRule.scenario.onActivity { activity ->
+            mPreviewView = PreviewView(context)
+            mPreviewView.implementationMode = implMode
+            activity.setContentView(mPreviewView)
+        }
         mCameraProvider = ProcessCameraProvider.getInstance(context).get()
+        mIsSetup = true
     }
 
     @After
-    fun tearDown() {
+    public fun tearDown() {
         if (mIsSetup) {
-            mInstrumentation.runOnMainSync {
-                mCameraProvider.unbindAll()
-            }
+            mInstrumentation.runOnMainSync { mCameraProvider.unbindAll() }
             mCameraProvider.shutdown().get()
+            mIsSetup = false
         }
     }
 
@@ -134,7 +122,7 @@
     }
 
     @Test
-    fun streamState_IDLE_TO_STREAMING_startPreview() {
+    public fun streamState_IDLE_TO_STREAMING_startPreview() {
         assertStreamState(PreviewView.StreamState.IDLE)
 
         startPreview(mLifecycle, mPreviewView, CameraSelector.DEFAULT_BACK_CAMERA)
@@ -144,7 +132,7 @@
     }
 
     @Test
-    fun streamState_STREAMING_TO_IDLE_TO_STREAMING_lifecycleStopAndStart() {
+    public fun streamState_STREAMING_TO_IDLE_TO_STREAMING_lifecycleStopAndStart() {
         startPreview(mLifecycle, mPreviewView, CameraSelector.DEFAULT_BACK_CAMERA)
         mLifecycle.startAndResume()
         assertStreamState(PreviewView.StreamState.STREAMING)
@@ -157,7 +145,7 @@
     }
 
     @Test
-    fun streamState_STREAMING_TO_IDLE_unbindAll() {
+    public fun streamState_STREAMING_TO_IDLE_unbindAll() {
         startPreview(mLifecycle, mPreviewView, CameraSelector.DEFAULT_BACK_CAMERA)
         mLifecycle.startAndResume()
         assertStreamState(PreviewView.StreamState.STREAMING)
@@ -167,7 +155,7 @@
     }
 
     @Test
-    fun streamState_STREAMING_TO_IDLE_unbindPreviewOnly() {
+    public fun streamState_STREAMING_TO_IDLE_unbindPreviewOnly() {
         val preview = startPreview(mLifecycle, mPreviewView, CameraSelector.DEFAULT_BACK_CAMERA)
 
         mLifecycle.startAndResume()
@@ -178,7 +166,7 @@
     }
 
     @Test
-    fun streamState_STREAMING_TO_IDLE_TO_STREAMING_switchCamera() {
+    public fun streamState_STREAMING_TO_IDLE_TO_STREAMING_switchCamera() {
         Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT))
 
         startPreview(mLifecycle, mPreviewView, CameraSelector.DEFAULT_BACK_CAMERA)
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java
index 8e8b65a..41a8c1c 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java
@@ -38,6 +38,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.os.Build;
 import android.util.Size;
+import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.Surface;
 import android.view.SurfaceView;
@@ -54,6 +55,7 @@
 import androidx.camera.core.MeteringPointFactory;
 import androidx.camera.core.Preview;
 import androidx.camera.core.SurfaceRequest;
+import androidx.camera.core.ViewPort;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.impl.utils.futures.FutureCallback;
 import androidx.camera.core.impl.utils.futures.Futures;
@@ -66,11 +68,11 @@
 import androidx.camera.view.test.R;
 import androidx.core.content.ContextCompat;
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.core.app.ActivityScenario;
 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.ActivityTestRule;
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.UiObjectNotFoundException;
 import androidx.test.uiautomator.UiSelector;
@@ -107,37 +109,16 @@
     public TestRule mUseCamera = CameraUtil.grantCameraPermissionAndPreTest();
 
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
-    @Rule
-    public final ActivityTestRule<FakeActivity> mActivityRule = new ActivityTestRule<>(
-            FakeActivity.class, false, false);
+    private ActivityScenario<FakeActivity> mActivityScenario;
     private final Context mContext = ApplicationProvider.getApplicationContext();
-    private List<SurfaceRequest> mSurfaceRequestList = new ArrayList<>();
+    private final List<SurfaceRequest> mSurfaceRequestList = new ArrayList<>();
     private PreviewView mPreviewView;
     private MeteringPointFactory mMeteringPointFactory;
 
-    private SurfaceRequest createSurfaceRequest(CameraInfo cameraInfo,
-            boolean isRGBA8888Required) {
-        return createSurfaceRequest(DEFAULT_SURFACE_SIZE, cameraInfo, isRGBA8888Required);
-    }
-
-    private SurfaceRequest createSurfaceRequest(CameraInfo cameraInfo) {
-        return createSurfaceRequest(DEFAULT_SURFACE_SIZE, cameraInfo, false);
-    }
-
-    private SurfaceRequest createSurfaceRequest(Size size, CameraInfo cameraInfo,
-            boolean isRGBA8888Required) {
-        FakeCamera fakeCamera = spy(new FakeCamera());
-        when(fakeCamera.getCameraInfo()).thenReturn(cameraInfo);
-
-        SurfaceRequest surfaceRequest = new SurfaceRequest(size, fakeCamera, isRGBA8888Required);
-        mSurfaceRequestList.add(surfaceRequest);
-        return surfaceRequest;
-    }
-
     @Before
     public void setUp() throws CoreAppTestUtil.ForegroundOccupiedError {
         CoreAppTestUtil.prepareDeviceUI(mInstrumentation);
-        mActivityRule.launchActivity(null);
+        mActivityScenario = ActivityScenario.launch(FakeActivity.class);
     }
 
     @After
@@ -149,18 +130,46 @@
         }
     }
 
-    private CameraInfo createCameraInfo(String implementationType) {
-        FakeCameraInfoInternal cameraInfoInternal = new FakeCameraInfoInternal();
-        cameraInfoInternal.setImplementationType(implementationType);
-        return cameraInfoInternal;
-    }
+    @Test
+    public void previewViewSetScaleType_controllerRebinds() throws InterruptedException {
+        // Arrange.
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        Semaphore fitTypeSemaphore = new Semaphore(0);
+        CameraController fakeController = new CameraController(mContext) {
 
-    private CameraInfo createCameraInfo(int rotationDegrees, String implementationType,
-            @CameraSelector.LensFacing int lensFacing) {
-        FakeCameraInfoInternal cameraInfoInternal = new FakeCameraInfoInternal(rotationDegrees,
-                lensFacing);
-        cameraInfoInternal.setImplementationType(implementationType);
-        return cameraInfoInternal;
+            @Override
+            void attachPreviewSurface(@NonNull Preview.SurfaceProvider surfaceProvider,
+                    @NonNull ViewPort viewPort, @NonNull Display display) {
+                if (viewPort.getScaleType() == ViewPort.FIT) {
+                    fitTypeSemaphore.release();
+                }
+            }
+
+            @Nullable
+            @Override
+            Camera startCamera() {
+                return null;
+            }
+        };
+        AtomicReference<PreviewView> previewViewAtomicReference = new AtomicReference<>();
+        mInstrumentation.runOnMainSync(() -> {
+            PreviewView previewView = new PreviewView(mContext);
+            previewViewAtomicReference.set(previewView);
+            previewView.setImplementationMode(COMPATIBLE);
+            notifyLatchWhenLayoutReady(previewView, countDownLatch);
+            setContentView(previewView);
+        });
+        // Wait for layout ready
+        assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+
+        // Act: set controller then change the scale type.
+        mInstrumentation.runOnMainSync(() -> {
+            previewViewAtomicReference.get().setController(fakeController);
+            previewViewAtomicReference.get().setScaleType(PreviewView.ScaleType.FIT_CENTER);
+        });
+
+        // Assert: cameraController receives a fit type ViewPort.
+        assertThat(fitTypeSemaphore.tryAcquire(1, TimeUnit.SECONDS)).isTrue();
     }
 
     @Test
@@ -173,7 +182,7 @@
         // Arrange.
         CountDownLatch countDownLatch = new CountDownLatch(1);
         Semaphore semaphore = new Semaphore(0);
-        CameraController fakeController = new CameraController(mInstrumentation.getContext()) {
+        CameraController fakeController = new CameraController(mContext) {
             @Override
             void onPinchToZoom(float pinchToZoomScale) {
                 semaphore.release();
@@ -209,7 +218,7 @@
         // Arrange.
         CountDownLatch countDownLatch = new CountDownLatch(1);
         Semaphore semaphore = new Semaphore(0);
-        CameraController fakeController = new CameraController(mInstrumentation.getContext()) {
+        CameraController fakeController = new CameraController(mContext) {
             @Override
             void onTapToFocus(MeteringPointFactory meteringPointFactory, float x, float y) {
                 semaphore.release();
@@ -368,20 +377,16 @@
     @Test
     public void correctSurfacePixelFormat_whenRGBA8888IsRequired() throws Throwable {
         final CameraInfo cameraInfo = createCameraInfo(CameraInfo.IMPLEMENTATION_TYPE_CAMERA2);
-        SurfaceRequest surfaceRequest = createSurfaceRequest(cameraInfo, true);
+        SurfaceRequest surfaceRequest = createRgb8888SurfaceRequest(cameraInfo);
         ListenableFuture<Surface> future = surfaceRequest.getDeferrableSurface().getSurface();
 
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
+        mActivityScenario.onActivity(activity -> {
+            final PreviewView previewView = new PreviewView(mContext);
+            setContentView(previewView);
 
-                final PreviewView previewView = new PreviewView(mContext);
-                setContentView(previewView);
-
-                previewView.setImplementationMode(PERFORMANCE);
-                Preview.SurfaceProvider surfaceProvider = previewView.getSurfaceProvider();
-                surfaceProvider.onSurfaceRequested(surfaceRequest);
-            }
+            previewView.setImplementationMode(PERFORMANCE);
+            Preview.SurfaceProvider surfaceProvider = previewView.getSurfaceProvider();
+            surfaceProvider.onSurfaceRequested(surfaceRequest);
         });
         final Surface[] surface = new Surface[1];
         CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -657,12 +662,12 @@
     }
 
     @Test
-    public void redrawsPreview_whenLayoutResized() throws Throwable {
+    public void redrawsPreview_whenLayoutResized() {
         final AtomicReference<PreviewView> previewView = new AtomicReference<>();
         final AtomicReference<FrameLayout> container = new AtomicReference<>();
         final PreviewViewImplementation implementation = mock(TestPreviewViewImplementation.class);
 
-        mActivityRule.runOnUiThread(() -> {
+        mActivityScenario.onActivity(activity -> {
             previewView.set(new PreviewView(mContext));
             previewView.get().mImplementation = implementation;
 
@@ -681,12 +686,12 @@
     }
 
     @Test
-    public void doesNotRedrawPreview_whenDetachedFromWindow() throws Throwable {
+    public void doesNotRedrawPreview_whenDetachedFromWindow() {
         final AtomicReference<PreviewView> previewView = new AtomicReference<>();
         final AtomicReference<FrameLayout> container = new AtomicReference<>();
         final PreviewViewImplementation implementation = mock(TestPreviewViewImplementation.class);
 
-        mActivityRule.runOnUiThread(() -> {
+        mActivityScenario.onActivity(activity -> {
             previewView.set(new PreviewView(mContext));
             previewView.get().mImplementation = implementation;
 
@@ -707,12 +712,12 @@
     }
 
     @Test
-    public void redrawsPreview_whenReattachedToWindow() throws Throwable {
+    public void redrawsPreview_whenReattachedToWindow() {
         final AtomicReference<PreviewView> previewView = new AtomicReference<>();
         final AtomicReference<FrameLayout> container = new AtomicReference<>();
         final PreviewViewImplementation implementation = mock(TestPreviewViewImplementation.class);
 
-        mActivityRule.runOnUiThread(() -> {
+        mActivityScenario.onActivity(activity -> {
             previewView.set(new PreviewView(mContext));
             previewView.get().mImplementation = implementation;
 
@@ -793,7 +798,39 @@
     }
 
     private void setContentView(View view) {
-        mActivityRule.getActivity().setContentView(view);
+        mActivityScenario.onActivity(activity -> activity.setContentView(view));
+    }
+
+    private SurfaceRequest createRgb8888SurfaceRequest(CameraInfo cameraInfo) {
+        return createSurfaceRequest(cameraInfo, true);
+    }
+
+    private SurfaceRequest createSurfaceRequest(CameraInfo cameraInfo) {
+        return createSurfaceRequest(cameraInfo, false);
+    }
+
+    private SurfaceRequest createSurfaceRequest(CameraInfo cameraInfo, boolean isRGBA8888Required) {
+        final FakeCamera fakeCamera = spy(new FakeCamera());
+        when(fakeCamera.getCameraInfo()).thenReturn(cameraInfo);
+
+        final SurfaceRequest surfaceRequest = new SurfaceRequest(DEFAULT_SURFACE_SIZE, fakeCamera,
+                isRGBA8888Required);
+        mSurfaceRequestList.add(surfaceRequest);
+        return surfaceRequest;
+    }
+
+    private CameraInfo createCameraInfo(String implementationType) {
+        FakeCameraInfoInternal cameraInfoInternal = new FakeCameraInfoInternal();
+        cameraInfoInternal.setImplementationType(implementationType);
+        return cameraInfoInternal;
+    }
+
+    private CameraInfo createCameraInfo(int rotationDegrees, String implementationType,
+            @CameraSelector.LensFacing int lensFacing) {
+        FakeCameraInfoInternal cameraInfoInternal = new FakeCameraInfoInternal(rotationDegrees,
+                lensFacing);
+        cameraInfoInternal.setImplementationType(implementationType);
+        return cameraInfoInternal;
     }
 
     private void updateCropRectAndWaitForIdle(Rect cropRect) {
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/SurfaceViewImplementationTest.kt b/camera/camera-view/src/androidTest/java/androidx/camera/view/SurfaceViewImplementationTest.kt
index 4356d85..cc960aa 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/SurfaceViewImplementationTest.kt
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/SurfaceViewImplementationTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.camera.view
 
-import android.app.Activity
 import android.content.Context
 import android.util.Size
 import android.view.View
@@ -25,6 +24,7 @@
 import androidx.camera.testing.CoreAppTestUtil
 import androidx.camera.testing.fakes.FakeActivity
 import androidx.camera.testing.fakes.FakeCamera
+import androidx.test.core.app.ActivityScenario
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -32,7 +32,6 @@
 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
 import java.util.concurrent.CountDownLatch
@@ -40,37 +39,29 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-class SurfaceViewImplementationTest {
+public class SurfaceViewImplementationTest {
 
-    private val ANY_WIDTH = 640
-    private val ANY_HEIGHT = 480
-    private val ANY_SIZE = Size(ANY_WIDTH, ANY_HEIGHT)
+    public companion object {
+        private const val ANY_WIDTH = 640
+        private const val ANY_HEIGHT = 480
+        private val ANY_SIZE = Size(ANY_WIDTH, ANY_HEIGHT)
+    }
 
     private lateinit var mParent: FrameLayout
     private lateinit var mImplementation: SurfaceViewImplementation
-    private val mInstrumentation =
-        InstrumentationRegistry.getInstrumentation()
+    private val mInstrumentation = InstrumentationRegistry.getInstrumentation()
     private lateinit var mSurfaceRequest: SurfaceRequest
     private lateinit var mContext: Context
 
     // Shows the view in activity so that SurfaceView can work normally
-    @Suppress("DEPRECATION")
-    @get:Rule
-    var mActivityRule = androidx.test.rule.ActivityTestRule(
-        FakeActivity::class.java, false, false
-    )
-
-    @Throws(Throwable::class)
-    private fun setContentView(view: View) {
-        val activity: Activity = mActivityRule.activity
-        mActivityRule.runOnUiThread { activity.setContentView(view) }
-    }
+    private lateinit var mActivityScenario: ActivityScenario<FakeActivity>
 
     @Before
-    fun setUp() {
+    public fun setUp() {
         CoreAppTestUtil.prepareDeviceUI(mInstrumentation)
-        mActivityRule.launchActivity(null)
-        mContext = ApplicationProvider.getApplicationContext<Context>()
+
+        mActivityScenario = ActivityScenario.launch(FakeActivity::class.java)
+        mContext = ApplicationProvider.getApplicationContext()
         mParent = FrameLayout(mContext)
         setContentView(mParent)
 
@@ -79,12 +70,12 @@
     }
 
     @After
-    fun tearDown() {
+    public fun tearDown() {
         mSurfaceRequest.deferrableSurface.close()
     }
 
     @Test
-    fun surfaceProvidedSuccessfully() {
+    public fun surfaceProvidedSuccessfully() {
         CoreAppTestUtil.checkKeyguard(mContext)
 
         mInstrumentation.runOnMainSync {
@@ -96,10 +87,10 @@
     }
 
     @Test
-    fun onSurfaceNotInUseListener_isCalledWhenSurfaceIsNotUsedAnyMore() {
+    public fun onSurfaceNotInUseListener_isCalledWhenSurfaceIsNotUsedAnyMore() {
         CoreAppTestUtil.checkKeyguard(mContext)
 
-        var listenerLatch = CountDownLatch(1)
+        val listenerLatch = CountDownLatch(1)
         val onSurfaceNotInUseListener = {
             listenerLatch.countDown()
         }
@@ -114,14 +105,14 @@
     }
 
     @Test
-    fun onSurfaceNotInUseListener_isCalledWhenSurfaceRequestIsCancelled() {
-        var listenerLatch = CountDownLatch(1)
+    public fun onSurfaceNotInUseListener_isCalledWhenSurfaceRequestIsCancelled() {
+        val listenerLatch = CountDownLatch(1)
         val onSurfaceNotInUseListener = {
             listenerLatch.countDown()
         }
 
         // Not attach the mParent to the window so that the Surface cannot be created.
-        setContentView(View(ApplicationProvider.getApplicationContext()))
+        setContentView(View(mContext))
 
         mInstrumentation.runOnMainSync {
             mImplementation.onSurfaceRequested(mSurfaceRequest, onSurfaceNotInUseListener)
@@ -135,8 +126,13 @@
     }
 
     @Test
-    fun waitForNextFrame_futureCompletesImmediately() {
+    public fun waitForNextFrame_futureCompletesImmediately() {
         val future = mImplementation.waitForNextFrame()
         future.get(20, TimeUnit.MILLISECONDS)
     }
+
+    @Throws(Throwable::class)
+    private fun setContentView(view: View) {
+        mActivityScenario.onActivity { activity -> activity.setContentView(view) }
+    }
 }
\ No newline at end of file
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewImplementationTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewImplementationTest.java
index 1074d8d..961cab6 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewImplementationTest.java
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewImplementationTest.java
@@ -149,7 +149,7 @@
         SurfaceRequest surfaceRequest = getSurfaceRequest();
         CountDownLatch latchForSurfaceNotInUse = new CountDownLatch(1);
         PreviewViewImplementation.OnSurfaceNotInUseListener onSurfaceNotInUseListener =
-                () -> latchForSurfaceNotInUse.countDown();
+                latchForSurfaceNotInUse::countDown;
 
         mImplementation.onSurfaceRequested(surfaceRequest, onSurfaceNotInUseListener);
         DeferrableSurface deferrableSurface = surfaceRequest.getDeferrableSurface();
@@ -172,7 +172,7 @@
         SurfaceRequest surfaceRequest = getSurfaceRequest();
         CountDownLatch latchForSurfaceNotInUse = new CountDownLatch(1);
         PreviewViewImplementation.OnSurfaceNotInUseListener onSurfaceNotInUseListener =
-                () -> latchForSurfaceNotInUse.countDown();
+                latchForSurfaceNotInUse::countDown;
 
         mImplementation.onSurfaceRequested(surfaceRequest, onSurfaceNotInUseListener);
         DeferrableSurface deferrableSurface = surfaceRequest.getDeferrableSurface();
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java b/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
index 87fe126..90ed0e6 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
@@ -365,8 +365,13 @@
     /**
      * Applies a {@link ScaleType} to the preview.
      *
-     * <p> Once applied, the transformation will take immediate effect. This value can also be set
-     * in the layout XML file via the {@code app:scaleType} attribute.
+     * <p> If a {@link CameraController} is attached to {@link PreviewView}, the change will take
+     * immediate effect. It also takes immediate effect if {@link #getViewPort()} is not set in
+     * the bound {@link UseCaseGroup}. Otherwise, the {@link UseCase}s need to be bound again
+     * with the latest value of {@link #getViewPort()}.
+     *
+     * <p> This value can also be set in the layout XML file via the {@code app:scaleType}
+     * attribute.
      *
      * <p> The default value is {@link ScaleType#FILL_CENTER}.
      *
@@ -378,6 +383,8 @@
         Threads.checkMainThread();
         mPreviewTransform.setScaleType(scaleType);
         redrawPreview();
+        // Notify controller to re-calculate the crop rect.
+        attachToControllerIfReady(false);
     }
 
     /**
@@ -807,7 +814,7 @@
             mCameraController.clearPreviewSurface();
         }
         mCameraController = cameraController;
-        attachToControllerIfReady(false);
+        attachToControllerIfReady(/*shouldFailSilently=*/false);
     }
 
     /**
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisBaseTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisBaseTest.kt
index e9673f0..f4ea58e 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisBaseTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisBaseTest.kt
@@ -28,6 +28,7 @@
 import androidx.testutils.withActivity
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Assume
+import org.junit.BeforeClass
 import org.junit.Rule
 import org.junit.rules.TestRule
 import java.util.concurrent.TimeUnit
@@ -45,7 +46,7 @@
 abstract class ImageAnalysisBaseTest<A : CameraActivity> {
 
     @get:Rule
-    val mUseCameraRule: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+    val mUseCameraRule: TestRule = CameraUtil.grantCameraPermissionAndPreTest(testCameraRule)
 
     @get:Rule
     val mCameraActivityRules: GrantPermissionRule =
@@ -127,5 +128,14 @@
     companion object {
         protected const val IMAGES_COUNT = 30
         protected const val TIMEOUT = 5L
+
+        @JvmStatic
+        lateinit var testCameraRule: CameraUtil.PreTestCamera
+
+        @BeforeClass
+        @JvmStatic
+        fun classSetup() {
+            testCameraRule = CameraUtil.PreTestCamera()
+        }
     }
 }
\ No newline at end of file
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt
index a3eca5b..b38d2c7 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt
@@ -35,6 +35,7 @@
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Assume.assumeFalse
 import org.junit.Assume.assumeTrue
+import org.junit.BeforeClass
 import org.junit.Rule
 import org.junit.rules.TestRule
 import java.util.concurrent.TimeUnit
@@ -54,7 +55,7 @@
 abstract class ImageCaptureBaseTest<A : CameraActivity> {
 
     @get:Rule
-    val mUseCameraRule: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+    val mUseCameraRule: TestRule = CameraUtil.grantCameraPermissionAndPreTest(testCameraRule)
 
     @get:Rule
     val mCameraActivityRules: GrantPermissionRule =
@@ -196,5 +197,14 @@
         @JvmStatic
         protected val lensFacing =
             arrayOf(CameraSelector.LENS_FACING_BACK, CameraSelector.LENS_FACING_FRONT)
+
+        @JvmStatic
+        lateinit var testCameraRule: CameraUtil.PreTestCamera
+
+        @BeforeClass
+        @JvmStatic
+        fun classSetup() {
+            testCameraRule = CameraUtil.PreTestCamera()
+        }
     }
 }
\ No newline at end of file
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt
index 6ce2c1b..5475a0e 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt
@@ -64,10 +64,13 @@
             CameraSelector.LENS_FACING_FRONT,
             CameraSelector.LENS_FACING_BACK
         )
+
+        @JvmField
+        val testCameraRule = CameraUtil.PreTestCamera()
     }
 
     @get:Rule
-    val mUseCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+    val mUseCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest(testCameraRule)
 
     private val mDevice =
         UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPagerActivityTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPagerActivityTest.kt
index 590a29c..eeb4afe 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPagerActivityTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPagerActivityTest.kt
@@ -64,10 +64,13 @@
             CameraSelector.LENS_FACING_FRONT,
             CameraSelector.LENS_FACING_BACK
         )
+
+        @JvmField
+        val testCameraRule = CameraUtil.PreTestCamera()
     }
 
     @get:Rule
-    val mUseCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+    val mUseCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest(testCameraRule)
 
     private val mDevice =
         UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
index 16f8e7a..5afd62c 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
@@ -77,13 +77,16 @@
 
         // The minimum luminance for comparing pictures. Arbitrarily chosen.
         private const val MIN_LUMINANCE = 50F
+
+        @JvmField
+        val testCameraRule = CameraUtil.PreTestCamera()
     }
 
     @get:Rule
     val thrown: ExpectedException = ExpectedException.none()
 
     @get:Rule
-    val useCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+    val useCamera: TestRule = CameraUtil.grantCameraPermissionAndPreTest(testCameraRule)
 
     @get:Rule
     val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
index 173165f..e833abd 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
@@ -115,11 +115,6 @@
         image.close();
     };
 
-    @Override
-    public void onAttach(@NonNull Context context) {
-        super.onAttach(context);
-    }
-
     @NonNull
     @Override
     @UseExperimental(markerClass = ExperimentalVideo.class)
@@ -241,7 +236,7 @@
         view.findViewById(R.id.video_record).setOnClickListener(v -> {
             try {
                 String videoFileName = "video_" + System.currentTimeMillis();
-                ContentResolver resolver = getContext().getContentResolver();
+                ContentResolver resolver = requireContext().getContentResolver();
                 ContentValues contentValues = new ContentValues();
                 contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4");
                 contentValues.put(MediaStore.Video.Media.TITLE, videoFileName);
@@ -326,7 +321,9 @@
         if (mExecutorService != null) {
             mExecutorService.shutdown();
         }
-        mSensorRotationListener.disable();
+        if (mSensorRotationListener != null) {
+            mSensorRotationListener.disable();
+        }
     }
 
     void checkFailedFuture(ListenableFuture<Void> voidFuture) {
@@ -347,7 +344,7 @@
     // Synthetic access
     @SuppressWarnings("WeakerAccess")
     void toast(String message) {
-        getActivity().runOnUiThread(
+        requireActivity().runOnUiThread(
                 () -> Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show());
     }
 
@@ -373,8 +370,9 @@
     @UseExperimental(markerClass = ExperimentalVideo.class)
     private void updateUiText() {
         mFlashMode.setText(getFlashModeTextResId());
-        mCameraToggle.setChecked(mCameraController.getCameraSelector().getLensFacing()
-                == CameraSelector.LENS_FACING_BACK);
+        final Integer lensFacing = mCameraController.getCameraSelector().getLensFacing();
+        mCameraToggle.setChecked(
+                lensFacing != null && lensFacing == CameraSelector.LENS_FACING_BACK);
         mVideoEnabledToggle.setChecked(mCameraController.isVideoCaptureEnabled());
         mPinchToZoomToggle.setChecked(mCameraController.isPinchToZoomEnabled());
         mTapToFocusToggle.setChecked(mCameraController.isTapToFocusEnabled());
@@ -510,7 +508,7 @@
         contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
         ImageCapture.OutputFileOptions outputFileOptions =
                 new ImageCapture.OutputFileOptions.Builder(
-                        getContext().getContentResolver(),
+                        requireContext().getContentResolver(),
                         MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                         contentValues).build();
         mCameraController.takePicture(outputFileOptions, mExecutorService, callback);
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java
index 74878d8..c23de8b 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java
@@ -17,6 +17,7 @@
 package androidx.camera.integration.view;
 
 import android.Manifest;
+import android.annotation.SuppressLint;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Bundle;
@@ -28,7 +29,9 @@
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
 import androidx.core.content.ContextCompat;
 import androidx.fragment.app.Fragment;
 
@@ -53,7 +56,7 @@
     private Mode mMode = Mode.CAMERA_VIEW;
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         // Get extra option for checking whether it need to be implemented with PreviewView
@@ -77,7 +80,8 @@
                 if (allPermissionsGranted()) {
                     startFragment();
                 } else if (!mCheckedPermissions) {
-                    requestPermissions(REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
+                    ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS,
+                            REQUEST_CODE_PERMISSIONS);
                     mCheckedPermissions = true;
                 }
             } else {
@@ -105,6 +109,7 @@
         return true;
     }
 
+    @SuppressLint("NonConstantResourceId")
     @Override
     public boolean onOptionsItemSelected(@NonNull MenuItem item) {
         switch (item.getItemId()) {
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/PreviewViewFragment.java b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/PreviewViewFragment.java
index 97e5053..9b1a8e2 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/PreviewViewFragment.java
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/PreviewViewFragment.java
@@ -68,7 +68,8 @@
 public class PreviewViewFragment extends Fragment {
 
     /** Scale types of ImageView that map to the PreviewView scale types. */
-    private static final ImageView.ScaleType[] IMAGE_VIEW_SCALE_TYPES =
+    // Synthetic access
+    static final ImageView.ScaleType[] IMAGE_VIEW_SCALE_TYPES =
             {ImageView.ScaleType.FIT_CENTER, ImageView.ScaleType.FIT_CENTER,
                     ImageView.ScaleType.FIT_CENTER, ImageView.ScaleType.FIT_START,
                     ImageView.ScaleType.FIT_CENTER, ImageView.ScaleType.FIT_END};
@@ -182,7 +183,8 @@
         });
     }
 
-    void updateTargetRotationButtonText(Button rotationButton) {
+    @SuppressLint("SetTextI18n")
+    void updateTargetRotationButtonText(final @NonNull Button rotationButton) {
         switch (mPreview.getTargetRotation()) {
             case Surface.ROTATION_0:
                 rotationButton.setText("ROTATION_0");
@@ -225,7 +227,7 @@
             // Get extra option for setting initial camera direction
             boolean isCameraDirectionValid = false;
             String cameraDirectionString = null;
-            Bundle bundle = getActivity().getIntent().getExtras();
+            Bundle bundle = requireActivity().getIntent().getExtras();
             if (bundle != null) {
                 cameraDirectionString = bundle.getString(INTENT_EXTRA_CAMERA_DIRECTION);
                 isCameraDirectionValid =
@@ -330,6 +332,7 @@
         setUpFocusAndMetering(camera);
     }
 
+    @SuppressLint("ClickableViewAccessibility")
     private void setUpFocusAndMetering(@NonNull final Camera camera) {
         mPreviewView.setOnTouchListener((view, motionEvent) -> {
             switch (motionEvent.getAction()) {
@@ -414,7 +417,7 @@
     // like TextureView.SurfaceTextureListener#onSurfaceTextureUpdated but it will require to add
     // API in PreviewView which is not a good idea. And we use OnPreDrawListener instead of
     // OnDrawListener because OnDrawListener is not invoked on some low API level devices.
-    private ViewTreeObserver.OnPreDrawListener mOnPreDrawListener = () -> {
+    private final ViewTreeObserver.OnPreDrawListener mOnPreDrawListener = () -> {
         if (mPreviewUpdatingLatch != null) {
             mPreviewUpdatingLatch.countDown();
         }
@@ -439,5 +442,5 @@
     void setPreviewUpdatingLatch(@NonNull CountDownLatch previewUpdatingLatch) {
         mPreviewUpdatingLatch = previewUpdatingLatch;
     }
-    // end region
+    // endregion
 }
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 60456c9..1607e2f 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -1,9 +1,16 @@
 // Signature format: 4.0
 package androidx.car.app {
 
+  public final class AppInfo {
+    ctor @VisibleForTesting public AppInfo(int, int, String);
+    method public int getLatestCarAppApiLevel();
+    method public String getLibraryDisplayVersion();
+    method public int getMinCarAppApiLevel();
+  }
+
   public class AppManager {
     method public void invalidate();
-    method public void setSurfaceListener(androidx.car.app.SurfaceListener?);
+    method public void setSurfaceCallback(androidx.car.app.SurfaceCallback?);
     method public void showToast(CharSequence, int);
   }
 
@@ -14,38 +21,20 @@
     field public static final String NAVIGATION_TEMPLATES = "androidx.car.app.NAVIGATION_TEMPLATES";
   }
 
-  public abstract class CarAppService extends android.app.Service implements androidx.lifecycle.LifecycleOwner {
+  public abstract class CarAppService extends android.app.Service {
     ctor public CarAppService();
-    method @CallSuper public void dump(java.io.FileDescriptor, java.io.PrintWriter, String![]?);
-    method public void finish();
-    method public final androidx.car.app.CarContext getCarContext();
-    method public androidx.car.app.HostInfo? getHostInfo();
-    method public androidx.lifecycle.Lifecycle getLifecycle();
-    method @CallSuper public android.os.IBinder? onBind(android.content.Intent);
-    method public void onCarAppFinished();
-    method public void onCarConfigurationChanged(android.content.res.Configuration);
-    method public abstract androidx.car.app.Screen onCreateScreen(android.content.Intent);
-    method public final void onDestroy();
-    method public void onNewIntent(android.content.Intent);
+    method @CallSuper public final void dump(java.io.FileDescriptor, java.io.PrintWriter, String![]?);
+    method public final androidx.car.app.Session? getCurrentSession();
+    method public final androidx.car.app.HostInfo? getHostInfo();
+    method @CallSuper public final android.os.IBinder? onBind(android.content.Intent);
+    method public abstract androidx.car.app.Session onCreateSession();
     method public final boolean onUnbind(android.content.Intent);
     field public static final String SERVICE_INTERFACE = "androidx.car.app.CarAppService";
   }
 
-  public class CarAppVersion {
-    method public boolean isGreaterOrEqualTo(androidx.car.app.CarAppVersion);
-    method public static androidx.car.app.CarAppVersion? of(String) throws androidx.car.app.MalformedVersionException;
-    field public static final androidx.car.app.CarAppVersion HANDSHAKE_MIN_VERSION;
-    field public static final androidx.car.app.CarAppVersion INSTANCE;
-  }
-
-  public enum CarAppVersion.ReleaseSuffix {
-    method public static androidx.car.app.CarAppVersion.ReleaseSuffix fromString(String);
-    enum_constant public static final androidx.car.app.CarAppVersion.ReleaseSuffix RELEASE_SUFFIX_BETA;
-    enum_constant public static final androidx.car.app.CarAppVersion.ReleaseSuffix RELEASE_SUFFIX_EAP;
-  }
-
   public class CarContext extends android.content.ContextWrapper {
     method public void finishCarApp();
+    method public int getCarAppApiLevel();
     method public Object getCarService(String);
     method public <T> T getCarService(Class<T!>);
     method public String getCarServiceName(Class<?>);
@@ -54,11 +43,11 @@
     method public void startCarApp(android.content.Intent);
     method public static void startCarApp(android.content.Intent, android.content.Intent);
     field public static final String ACTION_NAVIGATE = "androidx.car.app.action.NAVIGATE";
-    field public static final String APP_SERVICE = "app_manager";
+    field public static final String APP_SERVICE = "app";
     field public static final String CAR_SERVICE = "car";
-    field public static final String NAVIGATION_SERVICE = "navigation_manager";
-    field public static final String SCREEN_MANAGER_SERVICE = "screen_manager";
-    field public static final String START_CAR_APP_BINDER_KEY = "StartCarAppBinderKey";
+    field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+    field public static final String NAVIGATION_SERVICE = "navigation";
+    field public static final String SCREEN_SERVICE = "screen";
   }
 
   public final class CarToast {
@@ -74,7 +63,6 @@
 
   public class FailureResponse {
     ctor public FailureResponse(Throwable);
-    ctor public FailureResponse();
     method public int getErrorType();
     method public String getStackTrace();
     field public static final int BUNDLER_EXCEPTION = 1; // 0x1
@@ -86,7 +74,16 @@
     field public static final int UNKNOWN_ERROR = 0; // 0x0
   }
 
+  public class HandshakeInfo {
+    ctor public HandshakeInfo(String, int);
+    method public int getHostCarAppApiLevel();
+    method public String getHostPackageName();
+  }
+
   public class HostException extends java.lang.RuntimeException {
+    ctor public HostException(String);
+    ctor public HostException(String, Throwable);
+    ctor public HostException(Throwable);
   }
 
   public class HostInfo {
@@ -95,33 +92,15 @@
     method public int getUid();
   }
 
-  public class MalformedVersionException extends java.lang.Exception {
-    ctor public MalformedVersionException(String?);
-    ctor public MalformedVersionException(String, Throwable);
-    ctor public MalformedVersionException(Throwable?);
-  }
-
-  public interface OnCheckedChangeListenerWrapper {
-    method public void onCheckedChange(boolean, androidx.car.app.OnDoneCallback);
-  }
-
   public interface OnDoneCallback {
     method public void onFailure(androidx.car.app.serialization.Bundleable);
     method public void onSuccess(androidx.car.app.serialization.Bundleable?);
   }
 
-  public interface OnItemVisibilityChangedListenerWrapper {
-    method public void onItemVisibilityChanged(int, int, androidx.car.app.OnDoneCallback);
-  }
-
-  public interface OnScreenResultCallback {
+  public interface OnScreenResultListener {
     method public void onScreenResult(Object?);
   }
 
-  public interface OnSelectedListenerWrapper {
-    method public void onSelected(int, androidx.car.app.OnDoneCallback);
-  }
-
   public abstract class Screen implements androidx.lifecycle.LifecycleOwner {
     ctor protected Screen(androidx.car.app.CarContext);
     method public final void finish();
@@ -133,26 +112,32 @@
     method public abstract androidx.car.app.model.Template onGetTemplate();
     method public void setMarker(String?);
     method public void setResult(Object?);
-    field public static final String ROOT = "ROOT";
   }
 
-  public class ScreenManager {
+  @MainThread public class ScreenManager {
     method public androidx.car.app.Screen getTop();
     method public void pop();
     method public void popTo(String);
+    method public void popToRoot();
     method public void push(androidx.car.app.Screen);
-    method public void pushForResult(androidx.car.app.Screen, androidx.car.app.OnScreenResultCallback);
+    method public void pushForResult(androidx.car.app.Screen, androidx.car.app.OnScreenResultListener);
     method public void remove(androidx.car.app.Screen);
   }
 
-  public interface SearchListener {
-    method public void onSearchSubmitted(String);
-    method public void onSearchTextChanged(String);
+  public abstract class Session implements androidx.lifecycle.LifecycleOwner {
+    ctor public Session();
+    method public final androidx.car.app.CarContext getCarContext();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method public void onCarConfigurationChanged(android.content.res.Configuration);
+    method public abstract androidx.car.app.Screen onCreateScreen(android.content.Intent);
+    method public void onNewIntent(android.content.Intent);
   }
 
-  public interface SearchListenerWrapper {
-    method public void onSearchSubmitted(String, androidx.car.app.OnDoneCallback);
-    method public void onSearchTextChanged(String, androidx.car.app.OnDoneCallback);
+  public interface SurfaceCallback {
+    method public void onStableAreaChanged(android.graphics.Rect);
+    method public void onSurfaceAvailable(androidx.car.app.SurfaceContainer);
+    method public void onSurfaceDestroyed(androidx.car.app.SurfaceContainer);
+    method public void onVisibleAreaChanged(android.graphics.Rect);
   }
 
   public class SurfaceContainer {
@@ -163,19 +148,20 @@
     method public int getWidth();
   }
 
-  public interface SurfaceListener {
-    method public void onStableAreaChanged(android.graphics.Rect);
-    method public void onSurfaceAvailable(androidx.car.app.SurfaceContainer);
-    method public void onSurfaceDestroyed(androidx.car.app.SurfaceContainer);
-    method public void onVisibleAreaChanged(android.graphics.Rect);
-  }
-
   public class WrappedRuntimeException extends java.lang.RuntimeException {
     ctor public WrappedRuntimeException(Throwable?);
   }
 
 }
 
+package androidx.car.app.annotations {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface RequiresCarApi {
+    method public abstract int value();
+  }
+
+}
+
 package androidx.car.app.model {
 
   public final class Action {
@@ -341,12 +327,12 @@
 
   public class GridItem implements androidx.car.app.model.Item {
     method public static androidx.car.app.model.GridItem.Builder builder();
-    method public androidx.car.app.model.CarIcon getImage();
+    method public androidx.car.app.model.CarIcon? getImage();
     method public int getImageType();
     method public androidx.car.app.model.OnClickListenerWrapper? getOnClickListener();
     method public androidx.car.app.model.CarText? getText();
-    method public androidx.car.app.model.CarText? getTitle();
-    method public androidx.car.app.model.Toggle? getToggle();
+    method public androidx.car.app.model.CarText getTitle();
+    method public boolean isLoading();
     field public static final int IMAGE_TYPE_ICON = 1; // 0x1
     field public static final int IMAGE_TYPE_LARGE = 2; // 0x2
   }
@@ -355,10 +341,10 @@
     method public androidx.car.app.model.GridItem build();
     method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon);
     method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, int);
+    method public androidx.car.app.model.GridItem.Builder setLoading(boolean);
     method public androidx.car.app.model.GridItem.Builder setOnClickListener(androidx.car.app.model.OnClickListener?);
     method public androidx.car.app.model.GridItem.Builder setText(CharSequence?);
-    method public androidx.car.app.model.GridItem.Builder setTitle(CharSequence?);
-    method public androidx.car.app.model.GridItem.Builder setToggle(androidx.car.app.model.Toggle?);
+    method public androidx.car.app.model.GridItem.Builder setTitle(CharSequence);
   }
 
   public final class GridTemplate implements androidx.car.app.model.Template {
@@ -389,8 +375,8 @@
     method public static androidx.car.app.model.ItemList.Builder builder();
     method public java.util.List<java.lang.Object!> getItems();
     method public androidx.car.app.model.CarText? getNoItemsMessage();
-    method public androidx.car.app.OnItemVisibilityChangedListenerWrapper? getOnItemsVisibilityChangedListener();
-    method public androidx.car.app.OnSelectedListenerWrapper? getOnSelectedListener();
+    method public androidx.car.app.model.OnItemVisibilityChangedListenerWrapper? getOnItemsVisibilityChangedListener();
+    method public androidx.car.app.model.OnSelectedListenerWrapper? getOnSelectedListener();
     method public int getSelectedIndex();
   }
 
@@ -442,7 +428,7 @@
 
   public final class MessageTemplate implements androidx.car.app.model.Template {
     method public static androidx.car.app.model.MessageTemplate.Builder builder(CharSequence);
-    method public androidx.car.app.model.ActionList? getActionList();
+    method public androidx.car.app.model.ActionList? getActions();
     method public androidx.car.app.model.CarText? getDebugMessage();
     method public androidx.car.app.model.Action? getHeaderAction();
     method public androidx.car.app.model.CarIcon? getIcon();
@@ -474,6 +460,10 @@
     method public androidx.car.app.model.Metadata.Builder setPlace(androidx.car.app.model.Place?);
   }
 
+  public interface OnCheckedChangeListenerWrapper {
+    method public void onCheckedChange(boolean, androidx.car.app.OnDoneCallback);
+  }
+
   public interface OnClickListener {
     method public void onClick();
   }
@@ -483,9 +473,17 @@
     method public void onClick(androidx.car.app.OnDoneCallback);
   }
 
+  public interface OnItemVisibilityChangedListenerWrapper {
+    method public void onItemVisibilityChanged(int, int, androidx.car.app.OnDoneCallback);
+  }
+
+  public interface OnSelectedListenerWrapper {
+    method public void onSelected(int, androidx.car.app.OnDoneCallback);
+  }
+
   public final class Pane {
     method public static androidx.car.app.model.Pane.Builder builder();
-    method public androidx.car.app.model.ActionList? getActionList();
+    method public androidx.car.app.model.ActionList? getActions();
     method public java.util.List<java.lang.Object!> getRows();
     method public boolean isLoading();
   }
@@ -603,14 +601,19 @@
     method public androidx.car.app.model.Row.Builder setToggle(androidx.car.app.model.Toggle?);
   }
 
+  public interface SearchCallbackWrapper {
+    method public void onSearchSubmitted(String, androidx.car.app.OnDoneCallback);
+    method public void onSearchTextChanged(String, androidx.car.app.OnDoneCallback);
+  }
+
   public final class SearchTemplate implements androidx.car.app.model.Template {
-    method public static androidx.car.app.model.SearchTemplate.Builder builder(androidx.car.app.SearchListener);
+    method public static androidx.car.app.model.SearchTemplate.Builder builder(androidx.car.app.model.SearchTemplate.SearchCallback);
     method public androidx.car.app.model.ActionStrip? getActionStrip();
     method public androidx.car.app.model.Action? getHeaderAction();
     method public String? getInitialSearchText();
     method public androidx.car.app.model.ItemList? getItemList();
+    method public androidx.car.app.model.SearchCallbackWrapper getSearchCallback();
     method public String? getSearchHint();
-    method public androidx.car.app.SearchListenerWrapper getSearchListener();
     method public boolean isLoading();
     method public boolean isShowKeyboardByDefault();
   }
@@ -626,6 +629,11 @@
     method public androidx.car.app.model.SearchTemplate.Builder setShowKeyboardByDefault(boolean);
   }
 
+  public static interface SearchTemplate.SearchCallback {
+    method public void onSearchSubmitted(String);
+    method public void onSearchTextChanged(String);
+  }
+
   public class SectionedItemList {
     method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, androidx.car.app.model.CarText);
     method public androidx.car.app.model.CarText getHeader();
@@ -659,7 +667,7 @@
 
   public class Toggle {
     method public static androidx.car.app.model.Toggle.Builder builder(androidx.car.app.model.Toggle.OnCheckedChangeListener);
-    method public androidx.car.app.OnCheckedChangeListenerWrapper getOnCheckedChangeListener();
+    method public androidx.car.app.model.OnCheckedChangeListenerWrapper getOnCheckedChangeListener();
     method public boolean isChecked();
   }
 
@@ -675,111 +683,20 @@
 
 }
 
-package androidx.car.app.model.constraints {
-
-  public class ActionsConstraints {
-    method @VisibleForTesting public static androidx.car.app.model.constraints.ActionsConstraints.Builder builder();
-    method public java.util.Set<java.lang.Integer!> getDisallowedActionTypes();
-    method public int getMaxActions();
-    method public int getMaxCustomTitles();
-    method public java.util.Set<java.lang.Integer!> getRequiredActionTypes();
-    method @VisibleForTesting public androidx.car.app.model.constraints.ActionsConstraints.Builder newBuilder();
-    method public void validateOrThrow(java.util.List<java.lang.Object!>);
-    field public static final androidx.car.app.model.constraints.ActionsConstraints ACTIONS_CONSTRAINTS_HEADER;
-    field public static final androidx.car.app.model.constraints.ActionsConstraints ACTIONS_CONSTRAINTS_NAVIGATION;
-    field public static final androidx.car.app.model.constraints.ActionsConstraints ACTIONS_CONSTRAINTS_SIMPLE;
-  }
-
-  @VisibleForTesting public static final class ActionsConstraints.Builder {
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder addDisallowedActionType(int);
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder addRequiredActionType(int);
-    method public androidx.car.app.model.constraints.ActionsConstraints build();
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder setMaxActions(int);
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder setMaxCustomTitles(int);
-  }
-
-  public class CarColorConstraints {
-    method public void validateOrThrow(androidx.car.app.model.CarColor);
-    field public static final androidx.car.app.model.constraints.CarColorConstraints STANDARD_ONLY;
-    field public static final androidx.car.app.model.constraints.CarColorConstraints UNCONSTRAINED;
-  }
-
-  public class CarIconConstraints {
-    method public androidx.core.graphics.drawable.IconCompat checkSupportedIcon(androidx.core.graphics.drawable.IconCompat);
-    method public void validateOrThrow(androidx.car.app.model.CarIcon?);
-    field public static final androidx.car.app.model.constraints.CarIconConstraints DEFAULT;
-    field public static final androidx.car.app.model.constraints.CarIconConstraints UNCONSTRAINED;
-  }
-
-  public class RowConstraints {
-    method public static androidx.car.app.model.constraints.RowConstraints.Builder builder();
-    method public androidx.car.app.model.constraints.CarIconConstraints getCarIconConstraints();
-    method public int getMaxActionsExclusive();
-    method public int getMaxTextLinesPerRow();
-    method public boolean isImageAllowed();
-    method public boolean isOnClickListenerAllowed();
-    method public boolean isToggleAllowed();
-    method public androidx.car.app.model.constraints.RowConstraints.Builder newBuilder();
-    method public void validateOrThrow(Object);
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_CONSERVATIVE;
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_FULL_LIST;
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_PANE;
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_SIMPLE;
-    field public static final androidx.car.app.model.constraints.RowConstraints UNCONSTRAINED;
-  }
-
-  public static final class RowConstraints.Builder {
-    method public androidx.car.app.model.constraints.RowConstraints build();
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setCarIconConstraints(androidx.car.app.model.constraints.CarIconConstraints);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setImageAllowed(boolean);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setMaxActionsExclusive(int);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setMaxTextLinesPerRow(int);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setOnClickListenerAllowed(boolean);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setToggleAllowed(boolean);
-  }
-
-  public class RowListConstraints {
-    method public static androidx.car.app.model.constraints.RowListConstraints.Builder builder();
-    method public int getMaxActions();
-    method public androidx.car.app.model.constraints.RowConstraints getRowConstraints();
-    method public int getRowListType();
-    method public boolean isAllowSelectableLists();
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder newBuilder();
-    method public void validateOrThrow(androidx.car.app.model.ItemList);
-    method public void validateOrThrow(java.util.List<androidx.car.app.model.SectionedItemList!>);
-    method public void validateOrThrow(androidx.car.app.model.Pane);
-    field public static final int DEFAULT_LIST = 0; // 0x0
-    field public static final int PANE = 1; // 0x1
-    field public static final int ROUTE_PREVIEW = 2; // 0x2
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_CONSERVATIVE;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_FULL_LIST;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_PANE;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_ROUTE_PREVIEW;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_SIMPLE;
-  }
-
-  public static final class RowListConstraints.Builder {
-    method public androidx.car.app.model.constraints.RowListConstraints build();
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setAllowSelectableLists(boolean);
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setMaxActions(int);
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setRowConstraints(androidx.car.app.model.constraints.RowConstraints);
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setRowListType(int);
-  }
-
-}
-
 package androidx.car.app.navigation {
 
   public class NavigationManager {
+    method @MainThread public void clearNavigationManagerCallback();
     method @MainThread public void navigationEnded();
     method @MainThread public void navigationStarted();
-    method @MainThread public void setListener(androidx.car.app.navigation.NavigationManagerListener?);
+    method @MainThread public void setNavigationManagerCallback(androidx.car.app.navigation.NavigationManagerCallback);
+    method @MainThread public void setNavigationManagerCallback(java.util.concurrent.Executor, androidx.car.app.navigation.NavigationManagerCallback);
     method @MainThread public void updateTrip(androidx.car.app.navigation.model.Trip);
   }
 
-  public interface NavigationManagerListener {
+  public interface NavigationManagerCallback {
     method public void onAutoDriveEnabled();
-    method public void stopNavigation();
+    method public void onStopNavigation();
   }
 
 }
@@ -942,9 +859,8 @@
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate build();
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip?);
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setHeaderAction(androidx.car.app.model.Action?);
-    method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setIsLoading(boolean);
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setItemList(androidx.car.app.model.ItemList?);
-    method @VisibleForTesting public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setItemListForTesting(androidx.car.app.model.ItemList?);
+    method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setLoading(boolean);
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setTitle(CharSequence?);
   }
 
@@ -963,9 +879,8 @@
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate build();
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip?);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setHeaderAction(androidx.car.app.model.Action?);
-    method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setIsLoading(boolean);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setItemList(androidx.car.app.model.ItemList?);
-    method @VisibleForTesting public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setItemListForTesting(androidx.car.app.model.ItemList?);
+    method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setLoading(boolean);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setNavigateAction(androidx.car.app.model.Action);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setTitle(CharSequence?);
   }
@@ -982,8 +897,8 @@
   public static final class RoutingInfo.Builder {
     method public androidx.car.app.navigation.model.RoutingInfo build();
     method public androidx.car.app.navigation.model.RoutingInfo.Builder setCurrentStep(androidx.car.app.navigation.model.Step, androidx.car.app.model.Distance);
-    method public androidx.car.app.navigation.model.RoutingInfo.Builder setIsLoading(boolean);
     method public androidx.car.app.navigation.model.RoutingInfo.Builder setJunctionImage(androidx.car.app.model.CarIcon?);
+    method public androidx.car.app.navigation.model.RoutingInfo.Builder setLoading(boolean);
     method public androidx.car.app.navigation.model.RoutingInfo.Builder setNextStep(androidx.car.app.navigation.model.Step?);
   }
 
@@ -1050,7 +965,7 @@
     method public androidx.car.app.navigation.model.Trip.Builder clearStepTravelEstimates();
     method public androidx.car.app.navigation.model.Trip.Builder clearSteps();
     method public androidx.car.app.navigation.model.Trip.Builder setCurrentRoad(CharSequence?);
-    method public androidx.car.app.navigation.model.Trip.Builder setIsLoading(boolean);
+    method public androidx.car.app.navigation.model.Trip.Builder setLoading(boolean);
   }
 
 }
@@ -1067,8 +982,8 @@
     method public CharSequence? getContentTitle();
     method public android.app.PendingIntent? getDeleteIntent();
     method public int getImportance();
-    method public android.graphics.Bitmap? getLargeIconBitmap();
-    method public int getSmallIconResId();
+    method public android.graphics.Bitmap? getLargeIcon();
+    method @DrawableRes public int getSmallIcon();
     method public boolean isExtended();
     method public static boolean isExtended(android.app.Notification);
   }
@@ -1114,3 +1029,13 @@
 
 }
 
+package androidx.car.app.versioning {
+
+  public class CarAppApiLevels {
+    method public static int getLatest();
+    method public static int getOldest();
+    field public static final int LEVEL_1 = 1; // 0x1
+  }
+
+}
+
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 60456c9..1607e2f 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -1,9 +1,16 @@
 // Signature format: 4.0
 package androidx.car.app {
 
+  public final class AppInfo {
+    ctor @VisibleForTesting public AppInfo(int, int, String);
+    method public int getLatestCarAppApiLevel();
+    method public String getLibraryDisplayVersion();
+    method public int getMinCarAppApiLevel();
+  }
+
   public class AppManager {
     method public void invalidate();
-    method public void setSurfaceListener(androidx.car.app.SurfaceListener?);
+    method public void setSurfaceCallback(androidx.car.app.SurfaceCallback?);
     method public void showToast(CharSequence, int);
   }
 
@@ -14,38 +21,20 @@
     field public static final String NAVIGATION_TEMPLATES = "androidx.car.app.NAVIGATION_TEMPLATES";
   }
 
-  public abstract class CarAppService extends android.app.Service implements androidx.lifecycle.LifecycleOwner {
+  public abstract class CarAppService extends android.app.Service {
     ctor public CarAppService();
-    method @CallSuper public void dump(java.io.FileDescriptor, java.io.PrintWriter, String![]?);
-    method public void finish();
-    method public final androidx.car.app.CarContext getCarContext();
-    method public androidx.car.app.HostInfo? getHostInfo();
-    method public androidx.lifecycle.Lifecycle getLifecycle();
-    method @CallSuper public android.os.IBinder? onBind(android.content.Intent);
-    method public void onCarAppFinished();
-    method public void onCarConfigurationChanged(android.content.res.Configuration);
-    method public abstract androidx.car.app.Screen onCreateScreen(android.content.Intent);
-    method public final void onDestroy();
-    method public void onNewIntent(android.content.Intent);
+    method @CallSuper public final void dump(java.io.FileDescriptor, java.io.PrintWriter, String![]?);
+    method public final androidx.car.app.Session? getCurrentSession();
+    method public final androidx.car.app.HostInfo? getHostInfo();
+    method @CallSuper public final android.os.IBinder? onBind(android.content.Intent);
+    method public abstract androidx.car.app.Session onCreateSession();
     method public final boolean onUnbind(android.content.Intent);
     field public static final String SERVICE_INTERFACE = "androidx.car.app.CarAppService";
   }
 
-  public class CarAppVersion {
-    method public boolean isGreaterOrEqualTo(androidx.car.app.CarAppVersion);
-    method public static androidx.car.app.CarAppVersion? of(String) throws androidx.car.app.MalformedVersionException;
-    field public static final androidx.car.app.CarAppVersion HANDSHAKE_MIN_VERSION;
-    field public static final androidx.car.app.CarAppVersion INSTANCE;
-  }
-
-  public enum CarAppVersion.ReleaseSuffix {
-    method public static androidx.car.app.CarAppVersion.ReleaseSuffix fromString(String);
-    enum_constant public static final androidx.car.app.CarAppVersion.ReleaseSuffix RELEASE_SUFFIX_BETA;
-    enum_constant public static final androidx.car.app.CarAppVersion.ReleaseSuffix RELEASE_SUFFIX_EAP;
-  }
-
   public class CarContext extends android.content.ContextWrapper {
     method public void finishCarApp();
+    method public int getCarAppApiLevel();
     method public Object getCarService(String);
     method public <T> T getCarService(Class<T!>);
     method public String getCarServiceName(Class<?>);
@@ -54,11 +43,11 @@
     method public void startCarApp(android.content.Intent);
     method public static void startCarApp(android.content.Intent, android.content.Intent);
     field public static final String ACTION_NAVIGATE = "androidx.car.app.action.NAVIGATE";
-    field public static final String APP_SERVICE = "app_manager";
+    field public static final String APP_SERVICE = "app";
     field public static final String CAR_SERVICE = "car";
-    field public static final String NAVIGATION_SERVICE = "navigation_manager";
-    field public static final String SCREEN_MANAGER_SERVICE = "screen_manager";
-    field public static final String START_CAR_APP_BINDER_KEY = "StartCarAppBinderKey";
+    field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+    field public static final String NAVIGATION_SERVICE = "navigation";
+    field public static final String SCREEN_SERVICE = "screen";
   }
 
   public final class CarToast {
@@ -74,7 +63,6 @@
 
   public class FailureResponse {
     ctor public FailureResponse(Throwable);
-    ctor public FailureResponse();
     method public int getErrorType();
     method public String getStackTrace();
     field public static final int BUNDLER_EXCEPTION = 1; // 0x1
@@ -86,7 +74,16 @@
     field public static final int UNKNOWN_ERROR = 0; // 0x0
   }
 
+  public class HandshakeInfo {
+    ctor public HandshakeInfo(String, int);
+    method public int getHostCarAppApiLevel();
+    method public String getHostPackageName();
+  }
+
   public class HostException extends java.lang.RuntimeException {
+    ctor public HostException(String);
+    ctor public HostException(String, Throwable);
+    ctor public HostException(Throwable);
   }
 
   public class HostInfo {
@@ -95,33 +92,15 @@
     method public int getUid();
   }
 
-  public class MalformedVersionException extends java.lang.Exception {
-    ctor public MalformedVersionException(String?);
-    ctor public MalformedVersionException(String, Throwable);
-    ctor public MalformedVersionException(Throwable?);
-  }
-
-  public interface OnCheckedChangeListenerWrapper {
-    method public void onCheckedChange(boolean, androidx.car.app.OnDoneCallback);
-  }
-
   public interface OnDoneCallback {
     method public void onFailure(androidx.car.app.serialization.Bundleable);
     method public void onSuccess(androidx.car.app.serialization.Bundleable?);
   }
 
-  public interface OnItemVisibilityChangedListenerWrapper {
-    method public void onItemVisibilityChanged(int, int, androidx.car.app.OnDoneCallback);
-  }
-
-  public interface OnScreenResultCallback {
+  public interface OnScreenResultListener {
     method public void onScreenResult(Object?);
   }
 
-  public interface OnSelectedListenerWrapper {
-    method public void onSelected(int, androidx.car.app.OnDoneCallback);
-  }
-
   public abstract class Screen implements androidx.lifecycle.LifecycleOwner {
     ctor protected Screen(androidx.car.app.CarContext);
     method public final void finish();
@@ -133,26 +112,32 @@
     method public abstract androidx.car.app.model.Template onGetTemplate();
     method public void setMarker(String?);
     method public void setResult(Object?);
-    field public static final String ROOT = "ROOT";
   }
 
-  public class ScreenManager {
+  @MainThread public class ScreenManager {
     method public androidx.car.app.Screen getTop();
     method public void pop();
     method public void popTo(String);
+    method public void popToRoot();
     method public void push(androidx.car.app.Screen);
-    method public void pushForResult(androidx.car.app.Screen, androidx.car.app.OnScreenResultCallback);
+    method public void pushForResult(androidx.car.app.Screen, androidx.car.app.OnScreenResultListener);
     method public void remove(androidx.car.app.Screen);
   }
 
-  public interface SearchListener {
-    method public void onSearchSubmitted(String);
-    method public void onSearchTextChanged(String);
+  public abstract class Session implements androidx.lifecycle.LifecycleOwner {
+    ctor public Session();
+    method public final androidx.car.app.CarContext getCarContext();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method public void onCarConfigurationChanged(android.content.res.Configuration);
+    method public abstract androidx.car.app.Screen onCreateScreen(android.content.Intent);
+    method public void onNewIntent(android.content.Intent);
   }
 
-  public interface SearchListenerWrapper {
-    method public void onSearchSubmitted(String, androidx.car.app.OnDoneCallback);
-    method public void onSearchTextChanged(String, androidx.car.app.OnDoneCallback);
+  public interface SurfaceCallback {
+    method public void onStableAreaChanged(android.graphics.Rect);
+    method public void onSurfaceAvailable(androidx.car.app.SurfaceContainer);
+    method public void onSurfaceDestroyed(androidx.car.app.SurfaceContainer);
+    method public void onVisibleAreaChanged(android.graphics.Rect);
   }
 
   public class SurfaceContainer {
@@ -163,19 +148,20 @@
     method public int getWidth();
   }
 
-  public interface SurfaceListener {
-    method public void onStableAreaChanged(android.graphics.Rect);
-    method public void onSurfaceAvailable(androidx.car.app.SurfaceContainer);
-    method public void onSurfaceDestroyed(androidx.car.app.SurfaceContainer);
-    method public void onVisibleAreaChanged(android.graphics.Rect);
-  }
-
   public class WrappedRuntimeException extends java.lang.RuntimeException {
     ctor public WrappedRuntimeException(Throwable?);
   }
 
 }
 
+package androidx.car.app.annotations {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface RequiresCarApi {
+    method public abstract int value();
+  }
+
+}
+
 package androidx.car.app.model {
 
   public final class Action {
@@ -341,12 +327,12 @@
 
   public class GridItem implements androidx.car.app.model.Item {
     method public static androidx.car.app.model.GridItem.Builder builder();
-    method public androidx.car.app.model.CarIcon getImage();
+    method public androidx.car.app.model.CarIcon? getImage();
     method public int getImageType();
     method public androidx.car.app.model.OnClickListenerWrapper? getOnClickListener();
     method public androidx.car.app.model.CarText? getText();
-    method public androidx.car.app.model.CarText? getTitle();
-    method public androidx.car.app.model.Toggle? getToggle();
+    method public androidx.car.app.model.CarText getTitle();
+    method public boolean isLoading();
     field public static final int IMAGE_TYPE_ICON = 1; // 0x1
     field public static final int IMAGE_TYPE_LARGE = 2; // 0x2
   }
@@ -355,10 +341,10 @@
     method public androidx.car.app.model.GridItem build();
     method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon);
     method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, int);
+    method public androidx.car.app.model.GridItem.Builder setLoading(boolean);
     method public androidx.car.app.model.GridItem.Builder setOnClickListener(androidx.car.app.model.OnClickListener?);
     method public androidx.car.app.model.GridItem.Builder setText(CharSequence?);
-    method public androidx.car.app.model.GridItem.Builder setTitle(CharSequence?);
-    method public androidx.car.app.model.GridItem.Builder setToggle(androidx.car.app.model.Toggle?);
+    method public androidx.car.app.model.GridItem.Builder setTitle(CharSequence);
   }
 
   public final class GridTemplate implements androidx.car.app.model.Template {
@@ -389,8 +375,8 @@
     method public static androidx.car.app.model.ItemList.Builder builder();
     method public java.util.List<java.lang.Object!> getItems();
     method public androidx.car.app.model.CarText? getNoItemsMessage();
-    method public androidx.car.app.OnItemVisibilityChangedListenerWrapper? getOnItemsVisibilityChangedListener();
-    method public androidx.car.app.OnSelectedListenerWrapper? getOnSelectedListener();
+    method public androidx.car.app.model.OnItemVisibilityChangedListenerWrapper? getOnItemsVisibilityChangedListener();
+    method public androidx.car.app.model.OnSelectedListenerWrapper? getOnSelectedListener();
     method public int getSelectedIndex();
   }
 
@@ -442,7 +428,7 @@
 
   public final class MessageTemplate implements androidx.car.app.model.Template {
     method public static androidx.car.app.model.MessageTemplate.Builder builder(CharSequence);
-    method public androidx.car.app.model.ActionList? getActionList();
+    method public androidx.car.app.model.ActionList? getActions();
     method public androidx.car.app.model.CarText? getDebugMessage();
     method public androidx.car.app.model.Action? getHeaderAction();
     method public androidx.car.app.model.CarIcon? getIcon();
@@ -474,6 +460,10 @@
     method public androidx.car.app.model.Metadata.Builder setPlace(androidx.car.app.model.Place?);
   }
 
+  public interface OnCheckedChangeListenerWrapper {
+    method public void onCheckedChange(boolean, androidx.car.app.OnDoneCallback);
+  }
+
   public interface OnClickListener {
     method public void onClick();
   }
@@ -483,9 +473,17 @@
     method public void onClick(androidx.car.app.OnDoneCallback);
   }
 
+  public interface OnItemVisibilityChangedListenerWrapper {
+    method public void onItemVisibilityChanged(int, int, androidx.car.app.OnDoneCallback);
+  }
+
+  public interface OnSelectedListenerWrapper {
+    method public void onSelected(int, androidx.car.app.OnDoneCallback);
+  }
+
   public final class Pane {
     method public static androidx.car.app.model.Pane.Builder builder();
-    method public androidx.car.app.model.ActionList? getActionList();
+    method public androidx.car.app.model.ActionList? getActions();
     method public java.util.List<java.lang.Object!> getRows();
     method public boolean isLoading();
   }
@@ -603,14 +601,19 @@
     method public androidx.car.app.model.Row.Builder setToggle(androidx.car.app.model.Toggle?);
   }
 
+  public interface SearchCallbackWrapper {
+    method public void onSearchSubmitted(String, androidx.car.app.OnDoneCallback);
+    method public void onSearchTextChanged(String, androidx.car.app.OnDoneCallback);
+  }
+
   public final class SearchTemplate implements androidx.car.app.model.Template {
-    method public static androidx.car.app.model.SearchTemplate.Builder builder(androidx.car.app.SearchListener);
+    method public static androidx.car.app.model.SearchTemplate.Builder builder(androidx.car.app.model.SearchTemplate.SearchCallback);
     method public androidx.car.app.model.ActionStrip? getActionStrip();
     method public androidx.car.app.model.Action? getHeaderAction();
     method public String? getInitialSearchText();
     method public androidx.car.app.model.ItemList? getItemList();
+    method public androidx.car.app.model.SearchCallbackWrapper getSearchCallback();
     method public String? getSearchHint();
-    method public androidx.car.app.SearchListenerWrapper getSearchListener();
     method public boolean isLoading();
     method public boolean isShowKeyboardByDefault();
   }
@@ -626,6 +629,11 @@
     method public androidx.car.app.model.SearchTemplate.Builder setShowKeyboardByDefault(boolean);
   }
 
+  public static interface SearchTemplate.SearchCallback {
+    method public void onSearchSubmitted(String);
+    method public void onSearchTextChanged(String);
+  }
+
   public class SectionedItemList {
     method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, androidx.car.app.model.CarText);
     method public androidx.car.app.model.CarText getHeader();
@@ -659,7 +667,7 @@
 
   public class Toggle {
     method public static androidx.car.app.model.Toggle.Builder builder(androidx.car.app.model.Toggle.OnCheckedChangeListener);
-    method public androidx.car.app.OnCheckedChangeListenerWrapper getOnCheckedChangeListener();
+    method public androidx.car.app.model.OnCheckedChangeListenerWrapper getOnCheckedChangeListener();
     method public boolean isChecked();
   }
 
@@ -675,111 +683,20 @@
 
 }
 
-package androidx.car.app.model.constraints {
-
-  public class ActionsConstraints {
-    method @VisibleForTesting public static androidx.car.app.model.constraints.ActionsConstraints.Builder builder();
-    method public java.util.Set<java.lang.Integer!> getDisallowedActionTypes();
-    method public int getMaxActions();
-    method public int getMaxCustomTitles();
-    method public java.util.Set<java.lang.Integer!> getRequiredActionTypes();
-    method @VisibleForTesting public androidx.car.app.model.constraints.ActionsConstraints.Builder newBuilder();
-    method public void validateOrThrow(java.util.List<java.lang.Object!>);
-    field public static final androidx.car.app.model.constraints.ActionsConstraints ACTIONS_CONSTRAINTS_HEADER;
-    field public static final androidx.car.app.model.constraints.ActionsConstraints ACTIONS_CONSTRAINTS_NAVIGATION;
-    field public static final androidx.car.app.model.constraints.ActionsConstraints ACTIONS_CONSTRAINTS_SIMPLE;
-  }
-
-  @VisibleForTesting public static final class ActionsConstraints.Builder {
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder addDisallowedActionType(int);
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder addRequiredActionType(int);
-    method public androidx.car.app.model.constraints.ActionsConstraints build();
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder setMaxActions(int);
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder setMaxCustomTitles(int);
-  }
-
-  public class CarColorConstraints {
-    method public void validateOrThrow(androidx.car.app.model.CarColor);
-    field public static final androidx.car.app.model.constraints.CarColorConstraints STANDARD_ONLY;
-    field public static final androidx.car.app.model.constraints.CarColorConstraints UNCONSTRAINED;
-  }
-
-  public class CarIconConstraints {
-    method public androidx.core.graphics.drawable.IconCompat checkSupportedIcon(androidx.core.graphics.drawable.IconCompat);
-    method public void validateOrThrow(androidx.car.app.model.CarIcon?);
-    field public static final androidx.car.app.model.constraints.CarIconConstraints DEFAULT;
-    field public static final androidx.car.app.model.constraints.CarIconConstraints UNCONSTRAINED;
-  }
-
-  public class RowConstraints {
-    method public static androidx.car.app.model.constraints.RowConstraints.Builder builder();
-    method public androidx.car.app.model.constraints.CarIconConstraints getCarIconConstraints();
-    method public int getMaxActionsExclusive();
-    method public int getMaxTextLinesPerRow();
-    method public boolean isImageAllowed();
-    method public boolean isOnClickListenerAllowed();
-    method public boolean isToggleAllowed();
-    method public androidx.car.app.model.constraints.RowConstraints.Builder newBuilder();
-    method public void validateOrThrow(Object);
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_CONSERVATIVE;
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_FULL_LIST;
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_PANE;
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_SIMPLE;
-    field public static final androidx.car.app.model.constraints.RowConstraints UNCONSTRAINED;
-  }
-
-  public static final class RowConstraints.Builder {
-    method public androidx.car.app.model.constraints.RowConstraints build();
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setCarIconConstraints(androidx.car.app.model.constraints.CarIconConstraints);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setImageAllowed(boolean);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setMaxActionsExclusive(int);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setMaxTextLinesPerRow(int);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setOnClickListenerAllowed(boolean);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setToggleAllowed(boolean);
-  }
-
-  public class RowListConstraints {
-    method public static androidx.car.app.model.constraints.RowListConstraints.Builder builder();
-    method public int getMaxActions();
-    method public androidx.car.app.model.constraints.RowConstraints getRowConstraints();
-    method public int getRowListType();
-    method public boolean isAllowSelectableLists();
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder newBuilder();
-    method public void validateOrThrow(androidx.car.app.model.ItemList);
-    method public void validateOrThrow(java.util.List<androidx.car.app.model.SectionedItemList!>);
-    method public void validateOrThrow(androidx.car.app.model.Pane);
-    field public static final int DEFAULT_LIST = 0; // 0x0
-    field public static final int PANE = 1; // 0x1
-    field public static final int ROUTE_PREVIEW = 2; // 0x2
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_CONSERVATIVE;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_FULL_LIST;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_PANE;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_ROUTE_PREVIEW;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_SIMPLE;
-  }
-
-  public static final class RowListConstraints.Builder {
-    method public androidx.car.app.model.constraints.RowListConstraints build();
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setAllowSelectableLists(boolean);
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setMaxActions(int);
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setRowConstraints(androidx.car.app.model.constraints.RowConstraints);
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setRowListType(int);
-  }
-
-}
-
 package androidx.car.app.navigation {
 
   public class NavigationManager {
+    method @MainThread public void clearNavigationManagerCallback();
     method @MainThread public void navigationEnded();
     method @MainThread public void navigationStarted();
-    method @MainThread public void setListener(androidx.car.app.navigation.NavigationManagerListener?);
+    method @MainThread public void setNavigationManagerCallback(androidx.car.app.navigation.NavigationManagerCallback);
+    method @MainThread public void setNavigationManagerCallback(java.util.concurrent.Executor, androidx.car.app.navigation.NavigationManagerCallback);
     method @MainThread public void updateTrip(androidx.car.app.navigation.model.Trip);
   }
 
-  public interface NavigationManagerListener {
+  public interface NavigationManagerCallback {
     method public void onAutoDriveEnabled();
-    method public void stopNavigation();
+    method public void onStopNavigation();
   }
 
 }
@@ -942,9 +859,8 @@
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate build();
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip?);
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setHeaderAction(androidx.car.app.model.Action?);
-    method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setIsLoading(boolean);
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setItemList(androidx.car.app.model.ItemList?);
-    method @VisibleForTesting public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setItemListForTesting(androidx.car.app.model.ItemList?);
+    method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setLoading(boolean);
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setTitle(CharSequence?);
   }
 
@@ -963,9 +879,8 @@
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate build();
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip?);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setHeaderAction(androidx.car.app.model.Action?);
-    method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setIsLoading(boolean);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setItemList(androidx.car.app.model.ItemList?);
-    method @VisibleForTesting public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setItemListForTesting(androidx.car.app.model.ItemList?);
+    method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setLoading(boolean);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setNavigateAction(androidx.car.app.model.Action);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setTitle(CharSequence?);
   }
@@ -982,8 +897,8 @@
   public static final class RoutingInfo.Builder {
     method public androidx.car.app.navigation.model.RoutingInfo build();
     method public androidx.car.app.navigation.model.RoutingInfo.Builder setCurrentStep(androidx.car.app.navigation.model.Step, androidx.car.app.model.Distance);
-    method public androidx.car.app.navigation.model.RoutingInfo.Builder setIsLoading(boolean);
     method public androidx.car.app.navigation.model.RoutingInfo.Builder setJunctionImage(androidx.car.app.model.CarIcon?);
+    method public androidx.car.app.navigation.model.RoutingInfo.Builder setLoading(boolean);
     method public androidx.car.app.navigation.model.RoutingInfo.Builder setNextStep(androidx.car.app.navigation.model.Step?);
   }
 
@@ -1050,7 +965,7 @@
     method public androidx.car.app.navigation.model.Trip.Builder clearStepTravelEstimates();
     method public androidx.car.app.navigation.model.Trip.Builder clearSteps();
     method public androidx.car.app.navigation.model.Trip.Builder setCurrentRoad(CharSequence?);
-    method public androidx.car.app.navigation.model.Trip.Builder setIsLoading(boolean);
+    method public androidx.car.app.navigation.model.Trip.Builder setLoading(boolean);
   }
 
 }
@@ -1067,8 +982,8 @@
     method public CharSequence? getContentTitle();
     method public android.app.PendingIntent? getDeleteIntent();
     method public int getImportance();
-    method public android.graphics.Bitmap? getLargeIconBitmap();
-    method public int getSmallIconResId();
+    method public android.graphics.Bitmap? getLargeIcon();
+    method @DrawableRes public int getSmallIcon();
     method public boolean isExtended();
     method public static boolean isExtended(android.app.Notification);
   }
@@ -1114,3 +1029,13 @@
 
 }
 
+package androidx.car.app.versioning {
+
+  public class CarAppApiLevels {
+    method public static int getLatest();
+    method public static int getOldest();
+    field public static final int LEVEL_1 = 1; // 0x1
+  }
+
+}
+
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 60456c9..1607e2f 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -1,9 +1,16 @@
 // Signature format: 4.0
 package androidx.car.app {
 
+  public final class AppInfo {
+    ctor @VisibleForTesting public AppInfo(int, int, String);
+    method public int getLatestCarAppApiLevel();
+    method public String getLibraryDisplayVersion();
+    method public int getMinCarAppApiLevel();
+  }
+
   public class AppManager {
     method public void invalidate();
-    method public void setSurfaceListener(androidx.car.app.SurfaceListener?);
+    method public void setSurfaceCallback(androidx.car.app.SurfaceCallback?);
     method public void showToast(CharSequence, int);
   }
 
@@ -14,38 +21,20 @@
     field public static final String NAVIGATION_TEMPLATES = "androidx.car.app.NAVIGATION_TEMPLATES";
   }
 
-  public abstract class CarAppService extends android.app.Service implements androidx.lifecycle.LifecycleOwner {
+  public abstract class CarAppService extends android.app.Service {
     ctor public CarAppService();
-    method @CallSuper public void dump(java.io.FileDescriptor, java.io.PrintWriter, String![]?);
-    method public void finish();
-    method public final androidx.car.app.CarContext getCarContext();
-    method public androidx.car.app.HostInfo? getHostInfo();
-    method public androidx.lifecycle.Lifecycle getLifecycle();
-    method @CallSuper public android.os.IBinder? onBind(android.content.Intent);
-    method public void onCarAppFinished();
-    method public void onCarConfigurationChanged(android.content.res.Configuration);
-    method public abstract androidx.car.app.Screen onCreateScreen(android.content.Intent);
-    method public final void onDestroy();
-    method public void onNewIntent(android.content.Intent);
+    method @CallSuper public final void dump(java.io.FileDescriptor, java.io.PrintWriter, String![]?);
+    method public final androidx.car.app.Session? getCurrentSession();
+    method public final androidx.car.app.HostInfo? getHostInfo();
+    method @CallSuper public final android.os.IBinder? onBind(android.content.Intent);
+    method public abstract androidx.car.app.Session onCreateSession();
     method public final boolean onUnbind(android.content.Intent);
     field public static final String SERVICE_INTERFACE = "androidx.car.app.CarAppService";
   }
 
-  public class CarAppVersion {
-    method public boolean isGreaterOrEqualTo(androidx.car.app.CarAppVersion);
-    method public static androidx.car.app.CarAppVersion? of(String) throws androidx.car.app.MalformedVersionException;
-    field public static final androidx.car.app.CarAppVersion HANDSHAKE_MIN_VERSION;
-    field public static final androidx.car.app.CarAppVersion INSTANCE;
-  }
-
-  public enum CarAppVersion.ReleaseSuffix {
-    method public static androidx.car.app.CarAppVersion.ReleaseSuffix fromString(String);
-    enum_constant public static final androidx.car.app.CarAppVersion.ReleaseSuffix RELEASE_SUFFIX_BETA;
-    enum_constant public static final androidx.car.app.CarAppVersion.ReleaseSuffix RELEASE_SUFFIX_EAP;
-  }
-
   public class CarContext extends android.content.ContextWrapper {
     method public void finishCarApp();
+    method public int getCarAppApiLevel();
     method public Object getCarService(String);
     method public <T> T getCarService(Class<T!>);
     method public String getCarServiceName(Class<?>);
@@ -54,11 +43,11 @@
     method public void startCarApp(android.content.Intent);
     method public static void startCarApp(android.content.Intent, android.content.Intent);
     field public static final String ACTION_NAVIGATE = "androidx.car.app.action.NAVIGATE";
-    field public static final String APP_SERVICE = "app_manager";
+    field public static final String APP_SERVICE = "app";
     field public static final String CAR_SERVICE = "car";
-    field public static final String NAVIGATION_SERVICE = "navigation_manager";
-    field public static final String SCREEN_MANAGER_SERVICE = "screen_manager";
-    field public static final String START_CAR_APP_BINDER_KEY = "StartCarAppBinderKey";
+    field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+    field public static final String NAVIGATION_SERVICE = "navigation";
+    field public static final String SCREEN_SERVICE = "screen";
   }
 
   public final class CarToast {
@@ -74,7 +63,6 @@
 
   public class FailureResponse {
     ctor public FailureResponse(Throwable);
-    ctor public FailureResponse();
     method public int getErrorType();
     method public String getStackTrace();
     field public static final int BUNDLER_EXCEPTION = 1; // 0x1
@@ -86,7 +74,16 @@
     field public static final int UNKNOWN_ERROR = 0; // 0x0
   }
 
+  public class HandshakeInfo {
+    ctor public HandshakeInfo(String, int);
+    method public int getHostCarAppApiLevel();
+    method public String getHostPackageName();
+  }
+
   public class HostException extends java.lang.RuntimeException {
+    ctor public HostException(String);
+    ctor public HostException(String, Throwable);
+    ctor public HostException(Throwable);
   }
 
   public class HostInfo {
@@ -95,33 +92,15 @@
     method public int getUid();
   }
 
-  public class MalformedVersionException extends java.lang.Exception {
-    ctor public MalformedVersionException(String?);
-    ctor public MalformedVersionException(String, Throwable);
-    ctor public MalformedVersionException(Throwable?);
-  }
-
-  public interface OnCheckedChangeListenerWrapper {
-    method public void onCheckedChange(boolean, androidx.car.app.OnDoneCallback);
-  }
-
   public interface OnDoneCallback {
     method public void onFailure(androidx.car.app.serialization.Bundleable);
     method public void onSuccess(androidx.car.app.serialization.Bundleable?);
   }
 
-  public interface OnItemVisibilityChangedListenerWrapper {
-    method public void onItemVisibilityChanged(int, int, androidx.car.app.OnDoneCallback);
-  }
-
-  public interface OnScreenResultCallback {
+  public interface OnScreenResultListener {
     method public void onScreenResult(Object?);
   }
 
-  public interface OnSelectedListenerWrapper {
-    method public void onSelected(int, androidx.car.app.OnDoneCallback);
-  }
-
   public abstract class Screen implements androidx.lifecycle.LifecycleOwner {
     ctor protected Screen(androidx.car.app.CarContext);
     method public final void finish();
@@ -133,26 +112,32 @@
     method public abstract androidx.car.app.model.Template onGetTemplate();
     method public void setMarker(String?);
     method public void setResult(Object?);
-    field public static final String ROOT = "ROOT";
   }
 
-  public class ScreenManager {
+  @MainThread public class ScreenManager {
     method public androidx.car.app.Screen getTop();
     method public void pop();
     method public void popTo(String);
+    method public void popToRoot();
     method public void push(androidx.car.app.Screen);
-    method public void pushForResult(androidx.car.app.Screen, androidx.car.app.OnScreenResultCallback);
+    method public void pushForResult(androidx.car.app.Screen, androidx.car.app.OnScreenResultListener);
     method public void remove(androidx.car.app.Screen);
   }
 
-  public interface SearchListener {
-    method public void onSearchSubmitted(String);
-    method public void onSearchTextChanged(String);
+  public abstract class Session implements androidx.lifecycle.LifecycleOwner {
+    ctor public Session();
+    method public final androidx.car.app.CarContext getCarContext();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method public void onCarConfigurationChanged(android.content.res.Configuration);
+    method public abstract androidx.car.app.Screen onCreateScreen(android.content.Intent);
+    method public void onNewIntent(android.content.Intent);
   }
 
-  public interface SearchListenerWrapper {
-    method public void onSearchSubmitted(String, androidx.car.app.OnDoneCallback);
-    method public void onSearchTextChanged(String, androidx.car.app.OnDoneCallback);
+  public interface SurfaceCallback {
+    method public void onStableAreaChanged(android.graphics.Rect);
+    method public void onSurfaceAvailable(androidx.car.app.SurfaceContainer);
+    method public void onSurfaceDestroyed(androidx.car.app.SurfaceContainer);
+    method public void onVisibleAreaChanged(android.graphics.Rect);
   }
 
   public class SurfaceContainer {
@@ -163,19 +148,20 @@
     method public int getWidth();
   }
 
-  public interface SurfaceListener {
-    method public void onStableAreaChanged(android.graphics.Rect);
-    method public void onSurfaceAvailable(androidx.car.app.SurfaceContainer);
-    method public void onSurfaceDestroyed(androidx.car.app.SurfaceContainer);
-    method public void onVisibleAreaChanged(android.graphics.Rect);
-  }
-
   public class WrappedRuntimeException extends java.lang.RuntimeException {
     ctor public WrappedRuntimeException(Throwable?);
   }
 
 }
 
+package androidx.car.app.annotations {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface RequiresCarApi {
+    method public abstract int value();
+  }
+
+}
+
 package androidx.car.app.model {
 
   public final class Action {
@@ -341,12 +327,12 @@
 
   public class GridItem implements androidx.car.app.model.Item {
     method public static androidx.car.app.model.GridItem.Builder builder();
-    method public androidx.car.app.model.CarIcon getImage();
+    method public androidx.car.app.model.CarIcon? getImage();
     method public int getImageType();
     method public androidx.car.app.model.OnClickListenerWrapper? getOnClickListener();
     method public androidx.car.app.model.CarText? getText();
-    method public androidx.car.app.model.CarText? getTitle();
-    method public androidx.car.app.model.Toggle? getToggle();
+    method public androidx.car.app.model.CarText getTitle();
+    method public boolean isLoading();
     field public static final int IMAGE_TYPE_ICON = 1; // 0x1
     field public static final int IMAGE_TYPE_LARGE = 2; // 0x2
   }
@@ -355,10 +341,10 @@
     method public androidx.car.app.model.GridItem build();
     method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon);
     method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, int);
+    method public androidx.car.app.model.GridItem.Builder setLoading(boolean);
     method public androidx.car.app.model.GridItem.Builder setOnClickListener(androidx.car.app.model.OnClickListener?);
     method public androidx.car.app.model.GridItem.Builder setText(CharSequence?);
-    method public androidx.car.app.model.GridItem.Builder setTitle(CharSequence?);
-    method public androidx.car.app.model.GridItem.Builder setToggle(androidx.car.app.model.Toggle?);
+    method public androidx.car.app.model.GridItem.Builder setTitle(CharSequence);
   }
 
   public final class GridTemplate implements androidx.car.app.model.Template {
@@ -389,8 +375,8 @@
     method public static androidx.car.app.model.ItemList.Builder builder();
     method public java.util.List<java.lang.Object!> getItems();
     method public androidx.car.app.model.CarText? getNoItemsMessage();
-    method public androidx.car.app.OnItemVisibilityChangedListenerWrapper? getOnItemsVisibilityChangedListener();
-    method public androidx.car.app.OnSelectedListenerWrapper? getOnSelectedListener();
+    method public androidx.car.app.model.OnItemVisibilityChangedListenerWrapper? getOnItemsVisibilityChangedListener();
+    method public androidx.car.app.model.OnSelectedListenerWrapper? getOnSelectedListener();
     method public int getSelectedIndex();
   }
 
@@ -442,7 +428,7 @@
 
   public final class MessageTemplate implements androidx.car.app.model.Template {
     method public static androidx.car.app.model.MessageTemplate.Builder builder(CharSequence);
-    method public androidx.car.app.model.ActionList? getActionList();
+    method public androidx.car.app.model.ActionList? getActions();
     method public androidx.car.app.model.CarText? getDebugMessage();
     method public androidx.car.app.model.Action? getHeaderAction();
     method public androidx.car.app.model.CarIcon? getIcon();
@@ -474,6 +460,10 @@
     method public androidx.car.app.model.Metadata.Builder setPlace(androidx.car.app.model.Place?);
   }
 
+  public interface OnCheckedChangeListenerWrapper {
+    method public void onCheckedChange(boolean, androidx.car.app.OnDoneCallback);
+  }
+
   public interface OnClickListener {
     method public void onClick();
   }
@@ -483,9 +473,17 @@
     method public void onClick(androidx.car.app.OnDoneCallback);
   }
 
+  public interface OnItemVisibilityChangedListenerWrapper {
+    method public void onItemVisibilityChanged(int, int, androidx.car.app.OnDoneCallback);
+  }
+
+  public interface OnSelectedListenerWrapper {
+    method public void onSelected(int, androidx.car.app.OnDoneCallback);
+  }
+
   public final class Pane {
     method public static androidx.car.app.model.Pane.Builder builder();
-    method public androidx.car.app.model.ActionList? getActionList();
+    method public androidx.car.app.model.ActionList? getActions();
     method public java.util.List<java.lang.Object!> getRows();
     method public boolean isLoading();
   }
@@ -603,14 +601,19 @@
     method public androidx.car.app.model.Row.Builder setToggle(androidx.car.app.model.Toggle?);
   }
 
+  public interface SearchCallbackWrapper {
+    method public void onSearchSubmitted(String, androidx.car.app.OnDoneCallback);
+    method public void onSearchTextChanged(String, androidx.car.app.OnDoneCallback);
+  }
+
   public final class SearchTemplate implements androidx.car.app.model.Template {
-    method public static androidx.car.app.model.SearchTemplate.Builder builder(androidx.car.app.SearchListener);
+    method public static androidx.car.app.model.SearchTemplate.Builder builder(androidx.car.app.model.SearchTemplate.SearchCallback);
     method public androidx.car.app.model.ActionStrip? getActionStrip();
     method public androidx.car.app.model.Action? getHeaderAction();
     method public String? getInitialSearchText();
     method public androidx.car.app.model.ItemList? getItemList();
+    method public androidx.car.app.model.SearchCallbackWrapper getSearchCallback();
     method public String? getSearchHint();
-    method public androidx.car.app.SearchListenerWrapper getSearchListener();
     method public boolean isLoading();
     method public boolean isShowKeyboardByDefault();
   }
@@ -626,6 +629,11 @@
     method public androidx.car.app.model.SearchTemplate.Builder setShowKeyboardByDefault(boolean);
   }
 
+  public static interface SearchTemplate.SearchCallback {
+    method public void onSearchSubmitted(String);
+    method public void onSearchTextChanged(String);
+  }
+
   public class SectionedItemList {
     method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, androidx.car.app.model.CarText);
     method public androidx.car.app.model.CarText getHeader();
@@ -659,7 +667,7 @@
 
   public class Toggle {
     method public static androidx.car.app.model.Toggle.Builder builder(androidx.car.app.model.Toggle.OnCheckedChangeListener);
-    method public androidx.car.app.OnCheckedChangeListenerWrapper getOnCheckedChangeListener();
+    method public androidx.car.app.model.OnCheckedChangeListenerWrapper getOnCheckedChangeListener();
     method public boolean isChecked();
   }
 
@@ -675,111 +683,20 @@
 
 }
 
-package androidx.car.app.model.constraints {
-
-  public class ActionsConstraints {
-    method @VisibleForTesting public static androidx.car.app.model.constraints.ActionsConstraints.Builder builder();
-    method public java.util.Set<java.lang.Integer!> getDisallowedActionTypes();
-    method public int getMaxActions();
-    method public int getMaxCustomTitles();
-    method public java.util.Set<java.lang.Integer!> getRequiredActionTypes();
-    method @VisibleForTesting public androidx.car.app.model.constraints.ActionsConstraints.Builder newBuilder();
-    method public void validateOrThrow(java.util.List<java.lang.Object!>);
-    field public static final androidx.car.app.model.constraints.ActionsConstraints ACTIONS_CONSTRAINTS_HEADER;
-    field public static final androidx.car.app.model.constraints.ActionsConstraints ACTIONS_CONSTRAINTS_NAVIGATION;
-    field public static final androidx.car.app.model.constraints.ActionsConstraints ACTIONS_CONSTRAINTS_SIMPLE;
-  }
-
-  @VisibleForTesting public static final class ActionsConstraints.Builder {
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder addDisallowedActionType(int);
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder addRequiredActionType(int);
-    method public androidx.car.app.model.constraints.ActionsConstraints build();
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder setMaxActions(int);
-    method public androidx.car.app.model.constraints.ActionsConstraints.Builder setMaxCustomTitles(int);
-  }
-
-  public class CarColorConstraints {
-    method public void validateOrThrow(androidx.car.app.model.CarColor);
-    field public static final androidx.car.app.model.constraints.CarColorConstraints STANDARD_ONLY;
-    field public static final androidx.car.app.model.constraints.CarColorConstraints UNCONSTRAINED;
-  }
-
-  public class CarIconConstraints {
-    method public androidx.core.graphics.drawable.IconCompat checkSupportedIcon(androidx.core.graphics.drawable.IconCompat);
-    method public void validateOrThrow(androidx.car.app.model.CarIcon?);
-    field public static final androidx.car.app.model.constraints.CarIconConstraints DEFAULT;
-    field public static final androidx.car.app.model.constraints.CarIconConstraints UNCONSTRAINED;
-  }
-
-  public class RowConstraints {
-    method public static androidx.car.app.model.constraints.RowConstraints.Builder builder();
-    method public androidx.car.app.model.constraints.CarIconConstraints getCarIconConstraints();
-    method public int getMaxActionsExclusive();
-    method public int getMaxTextLinesPerRow();
-    method public boolean isImageAllowed();
-    method public boolean isOnClickListenerAllowed();
-    method public boolean isToggleAllowed();
-    method public androidx.car.app.model.constraints.RowConstraints.Builder newBuilder();
-    method public void validateOrThrow(Object);
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_CONSERVATIVE;
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_FULL_LIST;
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_PANE;
-    field public static final androidx.car.app.model.constraints.RowConstraints ROW_CONSTRAINTS_SIMPLE;
-    field public static final androidx.car.app.model.constraints.RowConstraints UNCONSTRAINED;
-  }
-
-  public static final class RowConstraints.Builder {
-    method public androidx.car.app.model.constraints.RowConstraints build();
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setCarIconConstraints(androidx.car.app.model.constraints.CarIconConstraints);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setImageAllowed(boolean);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setMaxActionsExclusive(int);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setMaxTextLinesPerRow(int);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setOnClickListenerAllowed(boolean);
-    method public androidx.car.app.model.constraints.RowConstraints.Builder setToggleAllowed(boolean);
-  }
-
-  public class RowListConstraints {
-    method public static androidx.car.app.model.constraints.RowListConstraints.Builder builder();
-    method public int getMaxActions();
-    method public androidx.car.app.model.constraints.RowConstraints getRowConstraints();
-    method public int getRowListType();
-    method public boolean isAllowSelectableLists();
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder newBuilder();
-    method public void validateOrThrow(androidx.car.app.model.ItemList);
-    method public void validateOrThrow(java.util.List<androidx.car.app.model.SectionedItemList!>);
-    method public void validateOrThrow(androidx.car.app.model.Pane);
-    field public static final int DEFAULT_LIST = 0; // 0x0
-    field public static final int PANE = 1; // 0x1
-    field public static final int ROUTE_PREVIEW = 2; // 0x2
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_CONSERVATIVE;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_FULL_LIST;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_PANE;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_ROUTE_PREVIEW;
-    field public static final androidx.car.app.model.constraints.RowListConstraints ROW_LIST_CONSTRAINTS_SIMPLE;
-  }
-
-  public static final class RowListConstraints.Builder {
-    method public androidx.car.app.model.constraints.RowListConstraints build();
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setAllowSelectableLists(boolean);
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setMaxActions(int);
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setRowConstraints(androidx.car.app.model.constraints.RowConstraints);
-    method public androidx.car.app.model.constraints.RowListConstraints.Builder setRowListType(int);
-  }
-
-}
-
 package androidx.car.app.navigation {
 
   public class NavigationManager {
+    method @MainThread public void clearNavigationManagerCallback();
     method @MainThread public void navigationEnded();
     method @MainThread public void navigationStarted();
-    method @MainThread public void setListener(androidx.car.app.navigation.NavigationManagerListener?);
+    method @MainThread public void setNavigationManagerCallback(androidx.car.app.navigation.NavigationManagerCallback);
+    method @MainThread public void setNavigationManagerCallback(java.util.concurrent.Executor, androidx.car.app.navigation.NavigationManagerCallback);
     method @MainThread public void updateTrip(androidx.car.app.navigation.model.Trip);
   }
 
-  public interface NavigationManagerListener {
+  public interface NavigationManagerCallback {
     method public void onAutoDriveEnabled();
-    method public void stopNavigation();
+    method public void onStopNavigation();
   }
 
 }
@@ -942,9 +859,8 @@
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate build();
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip?);
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setHeaderAction(androidx.car.app.model.Action?);
-    method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setIsLoading(boolean);
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setItemList(androidx.car.app.model.ItemList?);
-    method @VisibleForTesting public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setItemListForTesting(androidx.car.app.model.ItemList?);
+    method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setLoading(boolean);
     method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setTitle(CharSequence?);
   }
 
@@ -963,9 +879,8 @@
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate build();
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip?);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setHeaderAction(androidx.car.app.model.Action?);
-    method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setIsLoading(boolean);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setItemList(androidx.car.app.model.ItemList?);
-    method @VisibleForTesting public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setItemListForTesting(androidx.car.app.model.ItemList?);
+    method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setLoading(boolean);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setNavigateAction(androidx.car.app.model.Action);
     method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setTitle(CharSequence?);
   }
@@ -982,8 +897,8 @@
   public static final class RoutingInfo.Builder {
     method public androidx.car.app.navigation.model.RoutingInfo build();
     method public androidx.car.app.navigation.model.RoutingInfo.Builder setCurrentStep(androidx.car.app.navigation.model.Step, androidx.car.app.model.Distance);
-    method public androidx.car.app.navigation.model.RoutingInfo.Builder setIsLoading(boolean);
     method public androidx.car.app.navigation.model.RoutingInfo.Builder setJunctionImage(androidx.car.app.model.CarIcon?);
+    method public androidx.car.app.navigation.model.RoutingInfo.Builder setLoading(boolean);
     method public androidx.car.app.navigation.model.RoutingInfo.Builder setNextStep(androidx.car.app.navigation.model.Step?);
   }
 
@@ -1050,7 +965,7 @@
     method public androidx.car.app.navigation.model.Trip.Builder clearStepTravelEstimates();
     method public androidx.car.app.navigation.model.Trip.Builder clearSteps();
     method public androidx.car.app.navigation.model.Trip.Builder setCurrentRoad(CharSequence?);
-    method public androidx.car.app.navigation.model.Trip.Builder setIsLoading(boolean);
+    method public androidx.car.app.navigation.model.Trip.Builder setLoading(boolean);
   }
 
 }
@@ -1067,8 +982,8 @@
     method public CharSequence? getContentTitle();
     method public android.app.PendingIntent? getDeleteIntent();
     method public int getImportance();
-    method public android.graphics.Bitmap? getLargeIconBitmap();
-    method public int getSmallIconResId();
+    method public android.graphics.Bitmap? getLargeIcon();
+    method @DrawableRes public int getSmallIcon();
     method public boolean isExtended();
     method public static boolean isExtended(android.app.Notification);
   }
@@ -1114,3 +1029,13 @@
 
 }
 
+package androidx.car.app.versioning {
+
+  public class CarAppApiLevels {
+    method public static int getLatest();
+    method public static int getOldest();
+    field public static final int LEVEL_1 = 1; // 0x1
+  }
+
+}
+
diff --git a/car/app/app/build.gradle b/car/app/app/build.gradle
index 5e853f9..fdcf40a 100644
--- a/car/app/app/build.gradle
+++ b/car/app/app/build.gradle
@@ -31,26 +31,24 @@
     implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0"
     implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
 
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
     // TODO(shiufai): We need this for assertThrows. Point back to the AndroidX shared version if
     // it is ever upgraded.
-    androidTestImplementation("junit:junit:4.13")
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_ANDROID)
+    testImplementation("junit:junit:4.13")
+    testImplementation(ANDROIDX_TEST_CORE)
+    testImplementation(ANDROIDX_TEST_RUNNER)
+    testImplementation(JUNIT)
+    testImplementation(MOCKITO_CORE)
+    testImplementation(ROBOLECTRIC)
+    testImplementation(TRUTH)
 }
 
 android {
     defaultConfig {
         minSdkVersion 21
         multiDexEnabled = true
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
     lintOptions {
-        // We have a bunch of builder/inner classes where the outer classes access the private
-        // fields/constructors directly.
-        disable("SyntheticAccessor")
         // We rely on keeping a bunch of private variables in the library for serialization.
         disable("BanKeepAnnotation")
     }
diff --git a/car/app/app/proguard-rules.pro b/car/app/app/proguard-rules.pro
index 738e0db..fd43f97d 100644
--- a/car/app/app/proguard-rules.pro
+++ b/car/app/app/proguard-rules.pro
@@ -5,13 +5,5 @@
 # For more details, see
 #   http://developer.android.com/guide/developing/tools/proguard.html
 
--keep class androidx.car.app.serialization.Bundleable
--keepclassmembers class * extends androidx.car.app.CarAppService
--keepclassmembers class androidx.car.app.FailureResponse
--keepclassmembers class androidx.car.app.SurfaceContainer
--keepclassmembers class androidx.car.app.CarContext {
-    androidx.car.app.HostDispatcher mHostDispatcher;
-}
--keepclassmembers class * extends androidx.car.app.Screen {
-    androidx.car.app.CarContext mCarContext;
-}
+# Keep all IInterfaces which are needed for host communications.
+-keep class androidx.car.app.** extends android.os.IInterface { *; }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/CarAppServiceTest.java b/car/app/app/src/androidTest/java/androidx/car/app/CarAppServiceTest.java
deleted file mode 100644
index f442e0d..0000000
--- a/car/app/app/src/androidTest/java/androidx/car/app/CarAppServiceTest.java
+++ /dev/null
@@ -1,289 +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.car.app;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.RemoteException;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.PlaceListMapTemplate;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.NavigationManager;
-import androidx.car.app.serialization.Bundleable;
-import androidx.car.app.serialization.BundlerException;
-import androidx.car.app.testing.CarAppServiceController;
-import androidx.car.app.testing.TestCarContext;
-import androidx.lifecycle.DefaultLifecycleObserver;
-import androidx.lifecycle.Lifecycle;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Deque;
-import java.util.Locale;
-
-/** Tests for {@link CarAppService}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class CarAppServiceTest {
-    @Mock
-    ICarHost mMockCarHost;
-    @Mock
-    DefaultLifecycleObserver mLifecycleObserver;
-
-    private TestCarContext mCarContext;
-    private final Template mTemplate =
-            PlaceListMapTemplate.builder()
-                    .setTitle("Title")
-                    .setItemList(ItemList.builder().build())
-                    .build();
-
-    private CarAppService mCarAppService;
-
-    private Intent mIntentSet;
-    private boolean mHasCarAppFinished;
-
-    @Before
-    @UiThreadTest
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mCarContext = TestCarContext.createCarContext(
-                ApplicationProvider.getApplicationContext());
-
-        mCarAppService =
-                new CarAppService() {
-                    @Override
-                    @NonNull
-                    public Screen onCreateScreen(@NonNull Intent intent) {
-                        mIntentSet = intent;
-                        return new Screen(getCarContext()) {
-                            @Override
-                            @NonNull
-                            public Template onGetTemplate() {
-                                return mTemplate;
-                            }
-                        };
-                    }
-
-                    @Override
-                    public void onCarAppFinished() {
-                        mHasCarAppFinished = true;
-                    }
-
-                    @Override
-                    public void onNewIntent(@NonNull Intent intent) {
-                        mIntentSet = intent;
-                    }
-                };
-
-        CarAppServiceController.of(mCarContext, mCarAppService);
-        mCarAppService.onCreate();
-    }
-
-    @Test
-    @UiThreadTest
-    public void onAppCreate_createsFirstScreen() throws RemoteException {
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
-
-        assertThat(
-                mCarAppService
-                        .getCarContext()
-                        .getCarService(ScreenManager.class)
-                        .getTopTemplate()
-                        .getTemplate())
-                .isInstanceOf(PlaceListMapTemplate.class);
-    }
-
-    @Test
-    @UiThreadTest
-    public void onAppCreate_withIntent_callsWithOnCreateScreenWithIntent() throws
-            RemoteException {
-        IOnDoneCallback callback = mock(IOnDoneCallback.class);
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        Intent intent = new Intent("Foo");
-        carApp.onAppCreate(mMockCarHost, intent, new Configuration(), callback);
-
-        assertThat(mIntentSet).isEqualTo(intent);
-        verify(callback).onSuccess(any());
-    }
-
-    @Test
-    @UiThreadTest
-    public void onAppCreate_alreadyPreviouslyCreated_callsOnNewIntent() throws RemoteException {
-        IOnDoneCallback callback = mock(IOnDoneCallback.class);
-
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        Intent intent = new Intent("Foo");
-        carApp.onAppCreate(mMockCarHost, intent, new Configuration(), callback);
-        verify(callback).onSuccess(any());
-
-        IOnDoneCallback callback2 = mock(IOnDoneCallback.class);
-        Intent intent2 = new Intent("Foo2");
-        carApp.onAppCreate(mMockCarHost, intent2, new Configuration(), callback2);
-
-        assertThat(mIntentSet).isEqualTo(intent2);
-        verify(callback2).onSuccess(any());
-    }
-
-    @Test
-    @UiThreadTest
-    public void onAppCreate_updatesTheConfiguration() throws RemoteException {
-        Configuration configuration = new Configuration();
-        configuration.setToDefaults();
-        configuration.setLocale(Locale.CANADA_FRENCH);
-
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        carApp.onAppCreate(mMockCarHost, null, configuration, mock(IOnDoneCallback.class));
-
-        assertThat(mCarContext.getResources().getConfiguration().getLocales().get(0))
-                .isEqualTo(Locale.CANADA_FRENCH);
-    }
-
-    @Test
-    @UiThreadTest
-    public void onNewIntent_callsOnNewIntentWithIntent() throws RemoteException {
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        Intent intent = new Intent("Foo");
-        carApp.onAppCreate(mMockCarHost, intent, new Configuration(), mock(IOnDoneCallback.class));
-
-        Intent intent2 = new Intent("Foo2");
-        carApp.onNewIntent(intent2, mock(IOnDoneCallback.class));
-
-        assertThat(mIntentSet).isEqualTo(intent2);
-    }
-
-    @Test
-    public void getNavigationManager() throws RemoteException {
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
-
-        assertThat(
-                mCarAppService.getCarContext().getCarService(NavigationManager.class)).isNotNull();
-    }
-
-    @Test
-    @UiThreadTest
-    public void onConfigurationChanged_updatesTheConfiguration() throws RemoteException {
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
-
-        Configuration configuration = new Configuration();
-        configuration.setToDefaults();
-        configuration.setLocale(Locale.CANADA_FRENCH);
-
-        carApp.onConfigurationChanged(configuration, mock(IOnDoneCallback.class));
-
-        assertThat(mCarContext.getResources().getConfiguration().getLocales().get(0))
-                .isEqualTo(Locale.CANADA_FRENCH);
-    }
-
-    @Test
-    public void onHandshakeCompleted_updatesHostInfo() throws RemoteException, BundlerException {
-        String hostPackageName = "com.google.projection.gearhead";
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        HandshakeInfo handshakeInfo = new HandshakeInfo(hostPackageName);
-        carApp.onHandshakeCompleted(Bundleable.create(handshakeInfo), mock(IOnDoneCallback
-                .class));
-        assertThat(mCarAppService.getHostInfo().getPackageName()).isEqualTo(hostPackageName);
-    }
-
-    @Test
-    @UiThreadTest
-    public void onUnbind_movesLifecycleStateToStopped() throws RemoteException {
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
-        carApp.onAppStart(mock(IOnDoneCallback.class));
-
-        mCarAppService.getLifecycle().addObserver(mLifecycleObserver);
-
-        assertThat(mCarAppService.onUnbind(null)).isTrue();
-
-        verify(mLifecycleObserver).onStop(any());
-    }
-
-    @Test
-    @UiThreadTest
-    public void onUnbind_rebind_callsOnCreateScreen() throws RemoteException {
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
-        carApp.onAppStart(mock(IOnDoneCallback.class));
-
-        mCarAppService.getLifecycle().addObserver(mLifecycleObserver);
-
-        assertThat(mCarAppService.onUnbind(null)).isTrue();
-        assertThat(mHasCarAppFinished).isTrue();
-
-        verify(mLifecycleObserver).onStop(any());
-
-        assertThat(
-                mCarAppService.getCarContext().getCarService(ScreenManager.class).getScreenStack())
-                .isEmpty();
-
-        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
-        assertThat(
-                mCarAppService.getCarContext().getCarService(ScreenManager.class).getScreenStack())
-                .hasSize(1);
-    }
-
-    @Test
-    @UiThreadTest
-    public void onUnbind_clearsScreenStack() throws RemoteException {
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
-
-        Deque<Screen> screenStack =
-                mCarAppService.getCarContext().getCarService(ScreenManager.class).getScreenStack();
-        assertThat(screenStack).hasSize(1);
-
-        Screen screen = screenStack.getFirst();
-        assertThat(screen.getLifecycle().getCurrentState()).isAtLeast(Lifecycle.State.CREATED);
-
-        mCarAppService.onUnbind(null);
-
-        assertThat(screenStack).isEmpty();
-        assertThat(screen.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.DESTROYED);
-        assertThat(mHasCarAppFinished).isTrue();
-    }
-
-    @Test
-    @UiThreadTest
-    public void finish() throws RemoteException {
-        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
-        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
-
-        mCarAppService.finish();
-
-        assertThat(mCarContext.hasCalledFinishCarApp()).isTrue();
-    }
-}
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/CarAppVersionTest.java b/car/app/app/src/androidTest/java/androidx/car/app/CarAppVersionTest.java
deleted file mode 100644
index b4d28ee..0000000
--- a/car/app/app/src/androidTest/java/androidx/car/app/CarAppVersionTest.java
+++ /dev/null
@@ -1,131 +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.car.app;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertThrows;
-
-import androidx.car.app.CarAppVersion.ReleaseSuffix;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/** Tests for {@link CarAppVersion}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class CarAppVersionTest {
-
-    @Test
-    public void majorVersion() {
-        CarAppVersion hostVersion = CarAppVersion.create(2, 0, 0);
-        CarAppVersion clientVersion = CarAppVersion.create(1, 0, 0);
-
-        assertThat(hostVersion.isGreaterOrEqualTo(clientVersion)).isTrue();
-        assertThat(clientVersion.isGreaterOrEqualTo(hostVersion)).isFalse();
-    }
-
-    @Test
-    public void minorVersion() {
-        CarAppVersion hostVersion = CarAppVersion.create(2, 1, 0);
-        CarAppVersion clientVersion = CarAppVersion.create(2, 0, 0);
-
-        assertThat(hostVersion.isGreaterOrEqualTo(clientVersion)).isTrue();
-        assertThat(clientVersion.isGreaterOrEqualTo(hostVersion)).isFalse();
-    }
-
-    @Test
-    public void patchVersion() {
-        CarAppVersion hostVersion = CarAppVersion.create(3, 2, 1);
-        CarAppVersion clientVersion = CarAppVersion.create(3, 2, 0);
-
-        assertThat(hostVersion.isGreaterOrEqualTo(clientVersion)).isTrue();
-        assertThat(clientVersion.isGreaterOrEqualTo(hostVersion)).isFalse();
-    }
-
-    @Test
-    public void eapVersion_requiresExactMatch() {
-        CarAppVersion hostVersion = CarAppVersion.create(3, 2, 1, ReleaseSuffix.RELEASE_SUFFIX_EAP,
-                1);
-
-        CarAppVersion mismatchedClientVersion = CarAppVersion.create(4, 3, 2);
-        assertThat(hostVersion.isGreaterOrEqualTo(mismatchedClientVersion)).isFalse();
-        assertThat(mismatchedClientVersion.isGreaterOrEqualTo(hostVersion)).isFalse();
-
-        CarAppVersion matchedClientVersion =
-                CarAppVersion.create(3, 2, 1, ReleaseSuffix.RELEASE_SUFFIX_EAP, 1);
-        assertThat(hostVersion.isGreaterOrEqualTo(matchedClientVersion)).isTrue();
-        assertThat(matchedClientVersion.isGreaterOrEqualTo(hostVersion)).isTrue();
-    }
-
-    @Test
-    public void stableVersion_compatibleWithAllBeta() {
-        CarAppVersion hostVersion = CarAppVersion.create(3, 2, 1);
-
-        CarAppVersion clientVersion1 =
-                CarAppVersion.create(3, 2, 1, ReleaseSuffix.RELEASE_SUFFIX_BETA, 1);
-        assertThat(hostVersion.isGreaterOrEqualTo(clientVersion1)).isTrue();
-        assertThat(clientVersion1.isGreaterOrEqualTo(hostVersion)).isFalse();
-
-        CarAppVersion clientVersion2 =
-                CarAppVersion.create(3, 2, 1, ReleaseSuffix.RELEASE_SUFFIX_BETA, 2);
-        assertThat(hostVersion.isGreaterOrEqualTo(clientVersion2)).isTrue();
-        assertThat(clientVersion2.isGreaterOrEqualTo(hostVersion)).isFalse();
-    }
-
-    @Test
-    public void versionString_malformed_multipleHyphens() {
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2.3-eap.4-5"));
-    }
-
-    @Test
-    public void versionString_malformed_mainVersionIncorrectNumbers() {
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1"));
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1."));
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2"));
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2."));
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2.3.4"));
-    }
-
-    @Test
-    public void versionString_malformed_invalidNumberFormat() {
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2.3c-eap.4"));
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2.3-eap.4c"));
-    }
-
-    @Test
-    public void versionString_malformed_incorrectReleaseSuffix() {
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2.3-"));
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2.3-eap"));
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2.3-eap."));
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2.3-eap.4."));
-        assertThrows(MalformedVersionException.class, () -> CarAppVersion.of("1.2.3-eaP.4"));
-    }
-
-    @Test
-    public void versionString() throws MalformedVersionException {
-        String version1 = "1.2.3";
-        String version2 = "1.2.3-eap.0";
-        String version3 = "1.2.3-beta.1";
-
-        assertThat(CarAppVersion.of(version1).toString()).isEqualTo(version1);
-        assertThat(CarAppVersion.of(version2).toString()).isEqualTo(version2);
-        assertThat(CarAppVersion.of(version3).toString()).isEqualTo(version3);
-    }
-}
diff --git a/car/app/app/src/main/aidl/androidx/car/app/IAppHost.aidl b/car/app/app/src/main/aidl/androidx/car/app/IAppHost.aidl
index 080b381..8ca48e6 100644
--- a/car/app/app/src/main/aidl/androidx/car/app/IAppHost.aidl
+++ b/car/app/app/src/main/aidl/androidx/car/app/IAppHost.aidl
@@ -16,7 +16,7 @@
 
 package androidx.car.app;
 
-import androidx.car.app.ISurfaceListener;
+import androidx.car.app.ISurfaceCallback;
 
 /** @hide */
 interface IAppHost {
@@ -27,7 +27,7 @@
   void showToast(CharSequence text, int duration) = 2;
 
   /**
-   * Registers the listener to get callbacks for surface events.
+   * Registers the callback to get surface events.
    */
-  void setSurfaceListener(@nullable ISurfaceListener listener) = 3;
+  void setSurfaceCallback(@nullable ISurfaceCallback callback) = 3;
 }
diff --git a/car/app/app/src/main/aidl/androidx/car/app/ICarApp.aidl b/car/app/app/src/main/aidl/androidx/car/app/ICarApp.aidl
index e8f3995..b7e4f99 100644
--- a/car/app/app/src/main/aidl/androidx/car/app/ICarApp.aidl
+++ b/car/app/app/src/main/aidl/androidx/car/app/ICarApp.aidl
@@ -61,12 +61,12 @@
   void getManager(in String type, IOnDoneCallback callback) = 8;
 
   /**
-   * Requests the version string of the library used for building the app.
+   * Requests information of the application (min API level, target API level, etc.).
    */
-  void getCarAppVersion(IOnDoneCallback callback) = 9;
+  void getAppInfo(IOnDoneCallback callback) = 9;
 
   /**
-   * Sends host information to the app.
+   * Sends host information and negotiated API level to the app.
    */
   void onHandshakeCompleted(in Bundleable handshakeInfo, IOnDoneCallback callback) = 10;
 }
diff --git a/car/app/app/src/main/aidl/androidx/car/app/ISurfaceListener.aidl b/car/app/app/src/main/aidl/androidx/car/app/ISurfaceCallback.aidl
similarity index 97%
rename from car/app/app/src/main/aidl/androidx/car/app/ISurfaceListener.aidl
rename to car/app/app/src/main/aidl/androidx/car/app/ISurfaceCallback.aidl
index 7d9192d..e8f5a8b 100644
--- a/car/app/app/src/main/aidl/androidx/car/app/ISurfaceListener.aidl
+++ b/car/app/app/src/main/aidl/androidx/car/app/ISurfaceCallback.aidl
@@ -23,7 +23,7 @@
 import androidx.car.app.IOnDoneCallback;
 
 /** @hide */
-oneway interface ISurfaceListener {
+oneway interface ISurfaceCallback {
   /**
    * Notifies the app that the surface has changed.
    */
diff --git a/car/app/app/src/main/aidl/androidx/car/app/IOnCheckedChangeListener.aidl b/car/app/app/src/main/aidl/androidx/car/app/model/IOnCheckedChangeListener.aidl
similarity index 95%
rename from car/app/app/src/main/aidl/androidx/car/app/IOnCheckedChangeListener.aidl
rename to car/app/app/src/main/aidl/androidx/car/app/model/IOnCheckedChangeListener.aidl
index 8383e12..46e758ed 100644
--- a/car/app/app/src/main/aidl/androidx/car/app/IOnCheckedChangeListener.aidl
+++ b/car/app/app/src/main/aidl/androidx/car/app/model/IOnCheckedChangeListener.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.car.app.model;
 
 import androidx.car.app.IOnDoneCallback;
 
diff --git a/car/app/app/src/main/aidl/androidx/car/app/IOnItemVisibilityChangedListener.aidl b/car/app/app/src/main/aidl/androidx/car/app/model/IOnItemVisibilityChangedListener.aidl
similarity index 96%
rename from car/app/app/src/main/aidl/androidx/car/app/IOnItemVisibilityChangedListener.aidl
rename to car/app/app/src/main/aidl/androidx/car/app/model/IOnItemVisibilityChangedListener.aidl
index ff43f3f..451a2b0 100644
--- a/car/app/app/src/main/aidl/androidx/car/app/IOnItemVisibilityChangedListener.aidl
+++ b/car/app/app/src/main/aidl/androidx/car/app/model/IOnItemVisibilityChangedListener.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.car.app.model;
 
 import androidx.car.app.IOnDoneCallback;
 
diff --git a/car/app/app/src/main/aidl/androidx/car/app/IOnSelectedListener.aidl b/car/app/app/src/main/aidl/androidx/car/app/model/IOnSelectedListener.aidl
similarity index 95%
rename from car/app/app/src/main/aidl/androidx/car/app/IOnSelectedListener.aidl
rename to car/app/app/src/main/aidl/androidx/car/app/model/IOnSelectedListener.aidl
index 2896a83..2ee9f5f 100644
--- a/car/app/app/src/main/aidl/androidx/car/app/IOnSelectedListener.aidl
+++ b/car/app/app/src/main/aidl/androidx/car/app/model/IOnSelectedListener.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.car.app.model;
 
 import androidx.car.app.IOnDoneCallback;
 
diff --git a/car/app/app/src/main/aidl/androidx/car/app/ISearchListener.aidl b/car/app/app/src/main/aidl/androidx/car/app/model/ISearchCallback.aidl
similarity index 91%
rename from car/app/app/src/main/aidl/androidx/car/app/ISearchListener.aidl
rename to car/app/app/src/main/aidl/androidx/car/app/model/ISearchCallback.aidl
index 9af81e0..0ff0eab 100644
--- a/car/app/app/src/main/aidl/androidx/car/app/ISearchListener.aidl
+++ b/car/app/app/src/main/aidl/androidx/car/app/model/ISearchCallback.aidl
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.car.app.model;
 
 import androidx.car.app.IOnDoneCallback;
 
 /** @hide */
-oneway interface ISearchListener {
+oneway interface ISearchCallback {
   void onSearchTextChanged(String text, IOnDoneCallback callback) = 1;
   void onSearchSubmitted(String text, IOnDoneCallback callback) = 2;
 }
diff --git a/car/app/app/src/main/aidl/androidx/car/app/navigation/INavigationManager.aidl b/car/app/app/src/main/aidl/androidx/car/app/navigation/INavigationManager.aidl
index 4e16e7e..7a3a01d 100644
--- a/car/app/app/src/main/aidl/androidx/car/app/navigation/INavigationManager.aidl
+++ b/car/app/app/src/main/aidl/androidx/car/app/navigation/INavigationManager.aidl
@@ -28,5 +28,5 @@
   * <p>The app should stop any audio guidance, routing notifications tagged for
   * the car, and metadata state updates.
   */
-  void stopNavigation(IOnDoneCallback callback) = 1;
+  void onStopNavigation(IOnDoneCallback callback) = 1;
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/AppInfo.java b/car/app/app/src/main/java/androidx/car/app/AppInfo.java
new file mode 100644
index 0000000..d034601
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/AppInfo.java
@@ -0,0 +1,158 @@
+/*
+ * 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.car.app;
+
+import static androidx.car.app.utils.CommonUtils.TAG;
+
+import static java.util.Objects.requireNonNull;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.annotation.VisibleForTesting;
+import androidx.car.app.versioning.CarAppApiLevel;
+import androidx.car.app.versioning.CarAppApiLevels;
+
+/**
+ * Container class for information about the app the host is connected to.
+ * <p>
+ * Hosts will use this information to provide the right level of compatibility, based on the
+ * application's minimum and maximum API level and its own set of supported API levels.
+ * <p>
+ * The application minimum API level is defined in the application's manifest using the
+ * following declaration.
+ * <pre>{@code
+ * <manifest ...>
+ *   <application ...>
+ *     <meta-data
+ *         android:name="androidx.car.app.min-api-level"
+ *         android:value="2" />
+ *     ...
+ *   </application>
+ * </manifest>
+ * }</pre>
+ * <p>
+ *
+ * @see CarContext#getCarAppApiLevel()
+ */
+public final class AppInfo {
+    // TODO(b/174803562): Automatically update the this version using Gradle
+    private static final String LIBRARY_VERSION = "1.0.0-alpha01";
+
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY)
+    @VisibleForTesting
+    public static final String MIN_API_LEVEL_MANIFEST_KEY = "androidx.car.app.min-api-level";
+
+    @Nullable
+    private final String mLibraryVersion;
+    @CarAppApiLevel
+    private final int mMinCarAppApiLevel;
+    @CarAppApiLevel
+    private final int mLatestCarAppApiLevel;
+
+    /**
+     * Creates an instance of {@link AppInfo} based on the input {@link Context}.
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY)
+    @NonNull
+    public static AppInfo create(@NonNull Context context) {
+        @CarAppApiLevel
+        int minApiLevel = retrieveMinCarAppApiLevel(context);
+        if (minApiLevel < CarAppApiLevels.getOldest()
+                || minApiLevel > CarAppApiLevels.getLatest()) {
+            throw new IllegalArgumentException("Min API level (" + MIN_API_LEVEL_MANIFEST_KEY
+                    + "=" + minApiLevel + ") is out of range (" + CarAppApiLevels.getOldest() + "-"
+                    + CarAppApiLevels.getLatest() + ")");
+        }
+        return new AppInfo(minApiLevel, CarAppApiLevels.getLatest(), LIBRARY_VERSION);
+    }
+
+
+    /**
+     * Creates an instance of {@link AppInfo} with the provided version information.
+     *
+     * @param minCarAppApiLevel    the minimal API level that can work with an app built with
+     *                             the library.
+     * @param latestCarAppApiLevel the latest API level the library supports.
+     * @param libraryVersion       the library artifact version.
+     */
+    @VisibleForTesting
+    public AppInfo(@CarAppApiLevel int minCarAppApiLevel, @CarAppApiLevel int latestCarAppApiLevel,
+            @NonNull String libraryVersion) {
+        mMinCarAppApiLevel = minCarAppApiLevel;
+        mLibraryVersion = libraryVersion;
+        mLatestCarAppApiLevel = latestCarAppApiLevel;
+    }
+
+    // Used for serialization
+    private AppInfo() {
+        mMinCarAppApiLevel = 0;
+        mLibraryVersion = null;
+        mLatestCarAppApiLevel = 0;
+    }
+
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY)
+    @VisibleForTesting
+    @CarAppApiLevel
+    public static int retrieveMinCarAppApiLevel(@NonNull Context context) {
+        try {
+            ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(
+                    context.getPackageName(),
+                    PackageManager.GET_META_DATA);
+            if (applicationInfo.metaData == null) {
+                Log.i(TAG, "Min API level not found (" + MIN_API_LEVEL_MANIFEST_KEY + "). "
+                        + "Assuming min API level = " + CarAppApiLevels.getLatest());
+                return CarAppApiLevels.getLatest();
+            }
+            return applicationInfo.metaData.getInt(MIN_API_LEVEL_MANIFEST_KEY,
+                    CarAppApiLevels.getLatest());
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Unable to read min API level from manifest. Assuming "
+                    + CarAppApiLevels.getLatest(), e);
+            return CarAppApiLevels.getLatest();
+        }
+    }
+
+    /**
+     * String representation of library version. This version string is opaque and not meant to
+     * be parsed.
+     */
+    @NonNull
+    public String getLibraryDisplayVersion() {
+        return requireNonNull(mLibraryVersion);
+    }
+
+    @CarAppApiLevel
+    public int getMinCarAppApiLevel() {
+        return mMinCarAppApiLevel;
+    }
+
+    @CarAppApiLevel
+    public int getLatestCarAppApiLevel() {
+        return mLatestCarAppApiLevel;
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/AppManager.java b/car/app/app/src/main/java/androidx/car/app/AppManager.java
index 1196b19..5a78c38 100644
--- a/car/app/app/src/main/java/androidx/car/app/AppManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/AppManager.java
@@ -41,11 +41,9 @@
     private final HostDispatcher mHostDispatcher;
 
     /**
-     * Sets the {@link SurfaceListener} to get changes and updates to the surface on which the
+     * Sets the {@link SurfaceCallback} to get changes and updates to the surface on which the
      * app can draw custom content, or {@code null} to reset the listener.
      *
-     *
-     *
      * <p>This call requires the {@code androidx.car.app.ACCESS_SURFACE}
      * permission to be declared.
      *
@@ -59,11 +57,11 @@
      * @throws HostException     if the remote call fails.
      */
     @SuppressLint("ExecutorRegistration")
-    public void setSurfaceListener(@Nullable SurfaceListener surfaceListener) {
+    public void setSurfaceCallback(@Nullable SurfaceCallback surfaceCallback) {
         mHostDispatcher.dispatch(
                 CarContext.APP_SERVICE,
                 (IAppHost host) -> {
-                    host.setSurfaceListener(RemoteUtils.stubSurfaceListener(surfaceListener));
+                    host.setSurfaceCallback(RemoteUtils.stubSurfaceCallback(surfaceCallback));
                     return null;
                 },
                 "setSurfaceListener");
@@ -116,41 +114,43 @@
         return new AppManager(carContext, hostDispatcher);
     }
 
+    // Strictly to avoid synthetic accessor.
+    @NonNull
+    CarContext getCarContext() {
+        return mCarContext;
+    }
+
     /** @hide */
     @RestrictTo(LIBRARY_GROUP) // Restrict to testing library
     protected AppManager(@NonNull CarContext carContext, @NonNull HostDispatcher hostDispatcher) {
         this.mCarContext = carContext;
         this.mHostDispatcher = hostDispatcher;
-        mAppManager =
-                new IAppManager.Stub() {
-                    @Override
-                    public void getTemplate(IOnDoneCallback callback) {
-                        ThreadUtils.runOnMain(
-                                () -> {
-                                    TemplateWrapper templateWrapper;
-                                    try {
-                                        templateWrapper =
-                                                AppManager.this
-                                                        .mCarContext
-                                                        .getCarService(ScreenManager.class)
-                                                        .getTopTemplate();
-                                    } catch (RuntimeException e) {
-                                        RemoteUtils.sendFailureResponse(callback,
-                                                "getTemplate", e);
-                                        throw new WrappedRuntimeException(e);
-                                    }
+        mAppManager = new IAppManager.Stub() {
+            @Override
+            public void getTemplate(IOnDoneCallback callback) {
+                ThreadUtils.runOnMain(
+                        () -> {
+                            TemplateWrapper templateWrapper;
+                            try {
+                                templateWrapper = getCarContext().getCarService(
+                                        ScreenManager.class).getTopTemplate();
+                            } catch (RuntimeException e) {
+                                RemoteUtils.sendFailureResponse(callback,
+                                        "getTemplate", e);
+                                throw new WrappedRuntimeException(e);
+                            }
 
-                                    RemoteUtils.sendSuccessResponse(callback, "getTemplate",
-                                            templateWrapper);
-                                });
-                    }
+                            RemoteUtils.sendSuccessResponse(callback, "getTemplate",
+                                    templateWrapper);
+                        });
+            }
 
-                    @Override
-                    public void onBackPressed(IOnDoneCallback callback) {
-                        RemoteUtils.dispatchHostCall(
-                                carContext.getOnBackPressedDispatcher()::onBackPressed, callback,
-                                "onBackPressed");
-                    }
-                };
+            @Override
+            public void onBackPressed(IOnDoneCallback callback) {
+                RemoteUtils.dispatchHostCall(
+                        carContext.getOnBackPressedDispatcher()::onBackPressed, callback,
+                        "onBackPressed");
+            }
+        };
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/CarAppService.java b/car/app/app/src/main/java/androidx/car/app/CarAppService.java
index 7c3c854..51e43e2 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarAppService.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarAppService.java
@@ -35,10 +35,10 @@
 import androidx.car.app.serialization.BundlerException;
 import androidx.car.app.utils.RemoteUtils;
 import androidx.car.app.utils.ThreadUtils;
+import androidx.car.app.versioning.CarAppApiLevels;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.Lifecycle.Event;
 import androidx.lifecycle.Lifecycle.State;
-import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
 
 import java.io.FileDescriptor;
@@ -78,39 +78,37 @@
  * service</a>. Also note that accessing location may become unreliable when the phone is in the
  * battery saver mode.
  */
-// This lint warning is triggered because this has a finish() API. Suppress because we are not
-// actually cleaning any held resources in that method.
-@SuppressWarnings("NotCloseable")
-public abstract class CarAppService extends Service implements LifecycleOwner {
+public abstract class CarAppService extends Service {
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
     public static final String SERVICE_INTERFACE = "androidx.car.app.CarAppService";
-
     private static final String TAG = "CarAppService";
     private static final String AUTO_DRIVE = "AUTO_DRIVE";
 
-    @SuppressWarnings({"argument.type.incompatible", "assignment.type.incompatible"})
-    private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
-
-    @SuppressWarnings("argument.type.incompatible")
-    private final CarContext mCarContext = CarContext.create(mRegistry);
+    private AppInfo mAppInfo;
 
     @Nullable
-    HostInfo mHostInfo;
+    private Session mCurrentSession;
+
+    @Nullable
+    private HostInfo mHostInfo;
+
+    @Nullable
+    private HandshakeInfo mHandshakeInfo;
 
     /**
      * Handles the host binding to this car app.
      *
      * <p>This method is final to ensure this car app's lifecycle is handled properly.
      *
-     * <p>Use {@link #onCreateScreen} and {@link #onNewIntent} instead to handle incoming {@link
-     * Intent}s.
+     * <p>Use {@link #onCreateSession()} and {@link Session#onNewIntent} instead to handle incoming
+     * {@link Intent}s.
      */
     @Override
     @CallSuper
     @Nullable
-    public IBinder onBind(@NonNull Intent intent) {
+    public final IBinder onBind(@NonNull Intent intent) {
         return mBinder;
     }
 
@@ -118,28 +116,28 @@
      * Handles the host unbinding from this car app.
      *
      * <p>This method is final to ensure this car app's lifecycle is handled properly.
-     *
-     * <p>Use {@link #onCarAppFinished} instead.
      */
     @Override
     public final boolean onUnbind(@NonNull Intent intent) {
         Log.d(TAG, "onUnbind intent: " + intent);
         runOnMain(() -> {
-            // Stop the car app
-            mRegistry.handleLifecycleEvent(Event.ON_STOP);
+            // Destroy the session
+            if (mCurrentSession != null) {
+                CarContext carContext = mCurrentSession.getCarContext();
 
-            // Stop any active navigation
-            mCarContext.getCarService(NavigationManager.class).stopNavigation();
+                // Stop any active navigation
+                carContext.getCarService(NavigationManager.class).onStopNavigation();
 
-            // Destroy all screens in the stack
-            mCarContext.getCarService(ScreenManager.class).destroyAndClearScreenStack();
+                // Destroy all screens in the stack
+                carContext.getCarService(ScreenManager.class).destroyAndClearScreenStack();
 
-            // Remove binders to the host
-            mCarContext.resetHosts();
+                // Remove binders to the host
+                carContext.resetHosts();
 
-            // Notify the app that the host has unbinded so that it may treat it similar
-            // to destroy
-            onCarAppFinished();
+                ((LifecycleRegistry) mCurrentSession.getLifecycle()).handleLifecycleEvent(
+                        Event.ON_DESTROY);
+            }
+            mCurrentSession = null;
         });
 
         // Return true to request an onRebind call.  This means that the process will cache this
@@ -149,194 +147,37 @@
     }
 
     /**
-     * Handles the system destroying this {@link CarAppService}.
+     * Creates a new {@link Session} for the application.
      *
-     * <p>This method is final to ensure this car app's lifecycle is handled properly.
+     * <p>This method is invoked the first time the app is started, or if the previous
+     * {@link Session} instance has been destroyed and the system has not yet destroyed
+     * this service.
      *
-     * <p>Use a {@link androidx.lifecycle.LifecycleObserver} to observe this car app's {@link
-     * Lifecycle}.
-     *
-     * @see #getLifecycle
-     */
-    @Override
-    public final void onDestroy() {
-        Log.d(TAG, "onDestroy");
-        runOnMain(
-                () -> {
-                    mRegistry.handleLifecycleEvent(Event.ON_DESTROY);
-                });
-        super.onDestroy();
-        Log.d(TAG, "onDestroy completed");
-    }
-
-    /**
-     * Notifies that this car app has finished and should be treated as if it is destroyed.
-     *
-     * <p>The {@link Screen}s in the stack managed by the {@link ScreenManager} are now all
-     * destroyed and removed from the screen stack.
-     *
-     * <p>{@link #onCreateScreen} will be called if the user reopens the app before the system has
-     * destroyed it.
-     *
-     * <p>For the purposes of the app's lifecycle, you should perform similar destroy functions that
-     * you would when this instance's {@link Lifecycle} becomes {@link Lifecycle.State#DESTROYED}.
+     * <p>Once the method returns, {@link Session#onCreateScreen(Intent)} will be called on the
+     * {@link Session} returned.
      *
      * <p>Called by the system, do not call this method directly.
      *
-     * @see #getLifecycle
-     */
-    public void onCarAppFinished() {
-    }
-
-    /**
-     * Requests to finish the car app.
-     *
-     * <p>Call this when your app is done and should be closed.
-     *
-     * <p>At some point after this call {@link #onCarAppFinished} will be called, and eventually the
-     * system will destroy this {@link CarAppService}.
-     */
-    public void finish() {
-        mCarContext.finishCarApp();
-    }
-
-    /**
-     * Returns the {@link CarContext} for this car app.
-     *
-     * <p><b>The {@link CarContext} is not fully initialized until this car app's {@link
-     * Lifecycle.State} is at least {@link Lifecycle.State#CREATED}</b>
-     *
-     * @see #getLifecycle
-     */
-    @NonNull
-    public final CarContext getCarContext() {
-        return mCarContext;
-    }
-
-    /**
-     * Requests the first {@link Screen} for the application.
-     *
-     * <p>This method is invoked when this car app is first opened by the user.
-     *
-     * <p>Once the method returns, {@link Screen#onGetTemplate} will be called on the {@link Screen}
-     * returned, and the app will be displayed on the car screen.
-     *
-     * <p>To pre-seed a back stack, you can push {@link Screen}s onto the stack, via {@link
-     * ScreenManager#push} during this method call.
-     *
-     * <p>This method is invoked the first time the app is started, or if this {@link CarAppService}
-     * instance has been previously finished and the system has not yet destroyed this car app (See
-     * {@link #onCarAppFinished}).
-     *
-     * <p>Called by the system, do not call this method directly.
-     *
-     * @param intent the intent that was used to start this app. If the app was started with a
-     *               call to {@link CarContext#startCarApp}, this intent will be equal to the
-     *               intent passed to that method.
      * @see CarContext#startCarApp
      */
     @NonNull
-    public abstract Screen onCreateScreen(@NonNull Intent intent);
-
-    /**
-     * Notifies that the car app has received a new {@link Intent}.
-     *
-     * <p>Once the method returns, {@link Screen#onGetTemplate} will be called on the {@link Screen}
-     * that is on top of the {@link Screen} stack managed by the {@link ScreenManager}, and the app
-     * will be displayed on the car screen.
-     *
-     * <p>In contrast to {@link #onCreateScreen}, this method is invoked when the app has already
-     * been launched and has not been finished.
-     *
-     * <p>Often used to update the current {@link Screen} or pushing a new one on the stack,
-     * based off of the information in the {@code intent}.
-     *
-     * <p>Called by the system, do not call this method directly.
-     *
-     * @param intent the intent that was used to start this app. If the app was started with a
-     *               call to {@link CarContext#startCarApp}, this intent will be equal to the
-     *               intent passed to that method.
-     * @see CarContext#startCarApp
-     */
-    public void onNewIntent(@NonNull Intent intent) {
-    }
-
-    /**
-     * Notifies that the {@link CarContext}'s {@link Configuration} has changed.
-     *
-     * <p>At the time that this function is called, the {@link CarContext}'s resources object will
-     * have been updated to return resource values matching the new configuration.
-     *
-     * <p>Called by the system, do not call this method directly.
-     *
-     * @see CarContext
-     */
-    public void onCarConfigurationChanged(@NonNull Configuration newConfiguration) {
-    }
-
-    /**
-     * Returns the {@link CarAppService}'s {@link Lifecycle}.
-     *
-     * <p>Here are some of the ways you can use the car app's {@link Lifecycle}:
-     *
-     * <ul>
-     *   <li>Observe its {@link Lifecycle} by calling {@link Lifecycle#addObserver}. You can use the
-     *       {@link androidx.lifecycle.LifecycleObserver} to take specific actions whenever the
-     *       {@link Screen} receives different {@link Event}s.
-     *   <li>Use this {@link CarAppService} to observe {@link androidx.lifecycle.LiveData}s that
-     *       may drive the backing data for your application.
-     * </ul>
-     *
-     * <p>What each lifecycle related event means for a car app:
-     *
-     * <dl>
-     *   <dt>{@link Event#ON_CREATE}
-     *   <dd>The car app has just been launched, and this car app is being initialized. {@link
-     *       #onCreateScreen} will be called at a point after this call.
-     *   <dt>{@link #onCreateScreen}
-     *   <dd>The host is ready for this car app to create the first {@link Screen} so that it can
-     *       display its template.
-     *   <dt>{@link Event#ON_START}
-     *   <dd>The application is now visible in the car screen.
-     *   <dt>{@link Event#ON_RESUME}
-     *   <dd>The user can now interact with this application.
-     *   <dt>{@link Event#ON_PAUSE}
-     *   <dd>The user can no longer interact with this application.
-     *   <dt>{@link Event#ON_STOP}
-     *   <dd>The application is no longer visible.
-     *   <dt>{@link #onCarAppFinished}
-     *   <dd>Either this car app has requested to be finished (see {@link #finish}), or the host has
-     *       finished this car app. Unless this is a navigation app, after a period of time that the
-     *       app is no longer displaying in the car, the host may finish this car app.
-     *   <dt>{@link Event#ON_DESTROY}
-     *   <dd>The OS has now destroyed this {@link CarAppService} instance, and it is no longer
-     *       valid.
-     * </dl>
-     *
-     * <p>Listeners that are added in {@link Event#ON_START}, should be removed in {@link
-     * Event#ON_STOP}.
-     *
-     * <p>Listeners that are added in {@link Event#ON_CREATE} should be removed in {@link
-     * Event#ON_DESTROY}.
-     *
-     * @see androidx.lifecycle.LifecycleObserver
-     */
-    @NonNull
-    @Override
-    public Lifecycle getLifecycle() {
-        return mRegistry;
-    }
+    public abstract Session onCreateSession();
 
     @Override
     @CallSuper
-    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
+    public final void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
             @Nullable String[] args) {
         super.dump(fd, writer, args);
 
         for (String arg : args) {
             if (AUTO_DRIVE.equals(arg)) {
                 Log.d(TAG, "Executing onAutoDriveEnabled");
-                runOnMain(mCarContext.getCarService(NavigationManager.class)::onAutoDriveEnabled);
+                runOnMain(() -> {
+                    if (mCurrentSession != null) {
+                        mCurrentSession.getCarContext().getCarService(
+                                NavigationManager.class).onAutoDriveEnabled();
+                    }
+                });
             }
         }
     }
@@ -347,10 +188,54 @@
      * @see HostInfo
      */
     @Nullable
-    public HostInfo getHostInfo() {
+    public final HostInfo getHostInfo() {
         return mHostInfo;
     }
 
+    void setHostInfo(@Nullable HostInfo hostInfo) {
+        mHostInfo = hostInfo;
+    }
+
+    /**
+     * Retrieves the current {@link Session} for this service.
+     *
+     * @see Session
+     */
+    @Nullable
+    public final Session getCurrentSession() {
+        return mCurrentSession;
+    }
+
+    // Strictly to avoid synthetic accessor.
+    void setCurrentSession(@Nullable Session session) {
+        mCurrentSession = session;
+    }
+
+    // Strictly to avoid synthetic accessor.
+    @NonNull
+    AppInfo getAppInfo() {
+        if (mAppInfo == null) {
+            // Lazy-initialized as the package manager is not available if this is created inlined.
+            mAppInfo = AppInfo.create(this);
+        }
+        return mAppInfo;
+    }
+
+    // Strictly to avoid synthetic accessor.
+    void setHandshakeInfo(@NonNull HandshakeInfo handshakeInfo) {
+        int apiLevel = handshakeInfo.getHostCarAppApiLevel();
+        if (!CarAppApiLevels.isValid(apiLevel)) {
+            throw new IllegalArgumentException("Invalid Car App API level received: " + apiLevel);
+        }
+        mHandshakeInfo = handshakeInfo;
+    }
+
+    // Strictly to avoid synthetic accessor.
+    @Nullable
+    HandshakeInfo getHandshakeInfo() {
+        return mHandshakeInfo;
+    }
+
     private final ICarApp.Stub mBinder =
             new ICarApp.Stub() {
                 // incompatible argument for parameter context of attachBaseContext.
@@ -367,17 +252,25 @@
                         IOnDoneCallback callback) {
                     Log.d(TAG, "onAppCreate intent: " + intent);
                     RemoteUtils.dispatchHostCall(() -> {
+                        Session session = getCurrentSession();
+                        if (session == null
+                                || session.getLifecycle().getCurrentState() == State.DESTROYED) {
+                            session = onCreateSession();
+                            session.getCarContext().updateHandshakeInfo(getHandshakeInfo());
+                            setCurrentSession(session);
+                        }
+
                         // CarContext is not set up until the base Context is attached. First
                         // thing we need to do here is attach the base Context, so that any usage of
                         // it works after this point.
-                        CarContext carContext = getCarContext();
+                        CarContext carContext = session.getCarContext();
                         carContext.attachBaseContext(CarAppService.this, configuration);
                         carContext.setCarHost(carHost);
 
                         // Whenever the host unbinds, the screens in the stack are destroyed.  If
                         // there is another bind, before the OS has destroyed this Service, then
                         // the stack will be empty, and we need to treat it as a new instance.
-                        LifecycleRegistry registry = (LifecycleRegistry) getLifecycle();
+                        LifecycleRegistry registry = (LifecycleRegistry) session.getLifecycle();
                         Lifecycle.State state = registry.getCurrentState();
                         int screenStackSize = carContext.getCarService(
                                 ScreenManager.class).getScreenStack().size();
@@ -388,10 +281,10 @@
                                     + ", stack size: " + screenStackSize);
                             registry.handleLifecycleEvent(Event.ON_CREATE);
                             carContext.getCarService(ScreenManager.class).push(
-                                    onCreateScreen(intent));
+                                    session.onCreateScreen(intent));
                         } else {
                             Log.d(TAG, "onAppCreate the app was already created");
-                            onNewIntentInternal(intent);
+                            onNewIntentInternal(session, intent);
                         }
                     }, callback, "onAppCreate");
                     Log.d(TAG, "onAppCreate completed");
@@ -400,34 +293,50 @@
                 @Override
                 public void onAppStart(IOnDoneCallback callback) {
                     RemoteUtils.dispatchHostCall(
-                            () -> ((LifecycleRegistry) getLifecycle()).handleLifecycleEvent(
-                                    Event.ON_START), callback, "onAppStart");
+                            () -> {
+                                ((LifecycleRegistry) throwIfInvalid(
+                                        getCurrentSession()).getLifecycle())
+                                        .handleLifecycleEvent(Event.ON_START);
+                            }, callback,
+                            "onAppStart");
                 }
 
                 @Override
                 public void onAppResume(IOnDoneCallback callback) {
                     RemoteUtils.dispatchHostCall(
-                            () -> ((LifecycleRegistry) getLifecycle()).handleLifecycleEvent(
-                                    Event.ON_RESUME), callback, "onAppResume");
+                            () -> {
+                                ((LifecycleRegistry) throwIfInvalid(
+                                        getCurrentSession()).getLifecycle())
+                                        .handleLifecycleEvent(Event.ON_RESUME);
+                            }, callback,
+                            "onAppResume");
                 }
 
                 @Override
                 public void onAppPause(IOnDoneCallback callback) {
                     RemoteUtils.dispatchHostCall(
-                            () -> ((LifecycleRegistry) getLifecycle()).handleLifecycleEvent(
-                                    Event.ON_PAUSE), callback, "onAppPause");
+                            () -> {
+                                ((LifecycleRegistry) throwIfInvalid(
+                                        getCurrentSession()).getLifecycle())
+                                        .handleLifecycleEvent(Event.ON_PAUSE);
+                            }, callback, "onAppPause");
                 }
 
                 @Override
                 public void onAppStop(IOnDoneCallback callback) {
                     RemoteUtils.dispatchHostCall(
-                            () -> ((LifecycleRegistry) getLifecycle()).handleLifecycleEvent(
-                                    Event.ON_STOP), callback, "onAppStop");
+                            () -> {
+                                ((LifecycleRegistry) throwIfInvalid(
+                                        getCurrentSession()).getLifecycle())
+                                        .handleLifecycleEvent(Event.ON_STOP);
+                            }, callback, "onAppStop");
                 }
 
                 @Override
                 public void onNewIntent(Intent intent, IOnDoneCallback callback) {
-                    RemoteUtils.dispatchHostCall(() -> onNewIntentInternal(intent), callback,
+                    RemoteUtils.dispatchHostCall(
+                            () -> onNewIntentInternal(throwIfInvalid(getCurrentSession()), intent),
+                            callback,
                             "onNewIntent");
                 }
 
@@ -435,7 +344,8 @@
                 public void onConfigurationChanged(Configuration configuration,
                         IOnDoneCallback callback) {
                     RemoteUtils.dispatchHostCall(
-                            () -> onConfigurationChangedInternal(configuration),
+                            () -> onConfigurationChangedInternal(
+                                    throwIfInvalid(getCurrentSession()), configuration),
                             callback,
                             "onConfigurationChanged");
                 }
@@ -443,19 +353,20 @@
                 @Override
                 public void getManager(@CarServiceType @NonNull String type,
                         IOnDoneCallback callback) {
+                    Session session = throwIfInvalid(getCurrentSession());
                     switch (type) {
                         case CarContext.APP_SERVICE:
                             RemoteUtils.sendSuccessResponse(
                                     callback,
                                     "getManager",
-                                    getCarContext().getCarService(
+                                    session.getCarContext().getCarService(
                                             AppManager.class).getIInterface());
                             return;
                         case CarContext.NAVIGATION_SERVICE:
                             RemoteUtils.sendSuccessResponse(
                                     callback,
                                     "getManager",
-                                    mCarContext.getCarService(
+                                    session.getCarContext().getCarService(
                                             NavigationManager.class).getIInterface());
                             return;
                         default:
@@ -467,9 +378,9 @@
                 }
 
                 @Override
-                public void getCarAppVersion(IOnDoneCallback callback) {
+                public void getAppInfo(IOnDoneCallback callback) {
                     RemoteUtils.sendSuccessResponse(
-                            callback, "getCarAppVersion", CarAppVersion.INSTANCE.toString());
+                            callback, "getAppInfo", CarAppService.this.getAppInfo());
                 }
 
                 @Override
@@ -480,32 +391,43 @@
                                 (HandshakeInfo) handshakeInfo.get();
                         String packageName = deserializedHandshakeInfo.getHostPackageName();
                         int uid = Binder.getCallingUid();
-                        mHostInfo = new HostInfo(packageName, uid);
-                    } catch (BundlerException e) {
-                        mHostInfo = null;
+                        setHostInfo(new HostInfo(packageName, uid));
+                        setHandshakeInfo(deserializedHandshakeInfo);
+                        RemoteUtils.sendSuccessResponse(callback, "onHandshakeCompleted", null);
+                    } catch (BundlerException | IllegalArgumentException e) {
+                        setHostInfo(null);
+                        RemoteUtils.sendFailureResponse(callback, "onHandshakeCompleted", e);
                     }
-                    RemoteUtils.sendSuccessResponse(callback, "onHandshakeCompleted", null);
                 }
 
                 // call to onNewIntent(android.content.Intent) not allowed on the given receiver.
                 @SuppressWarnings("nullness:method.invocation.invalid")
                 @MainThread
-                private void onNewIntentInternal(Intent intent) {
+                private void onNewIntentInternal(Session session, Intent intent) {
                     ThreadUtils.checkMainThread();
-
-                    CarAppService.this.onNewIntent(intent);
+                    session.onNewIntent(intent);
                 }
 
                 // call to onCarConfigurationChanged(android.content.res.Configuration) not
                 // allowed on the given receiver.
                 @SuppressWarnings("nullness:method.invocation.invalid")
                 @MainThread
-                private void onConfigurationChangedInternal(Configuration configuration) {
+                private void onConfigurationChangedInternal(Session session,
+                        Configuration configuration) {
                     ThreadUtils.checkMainThread();
                     Log.d(TAG, "onCarConfigurationChanged configuration: " + configuration);
 
-                    getCarContext().onCarConfigurationChanged(configuration);
-                    onCarConfigurationChanged(getCarContext().getResources().getConfiguration());
+                    session.getCarContext().onCarConfigurationChanged(configuration);
+                    session.onCarConfigurationChanged(
+                            session.getCarContext().getResources().getConfiguration());
                 }
             };
+
+    Session throwIfInvalid(Session session) {
+        if (session == null) {
+            throw new IllegalStateException("Null session found when non-null expected.");
+        }
+
+        return session;
+    }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/CarAppVersion.java b/car/app/app/src/main/java/androidx/car/app/CarAppVersion.java
deleted file mode 100644
index d23f7e1..0000000
--- a/car/app/app/src/main/java/androidx/car/app/CarAppVersion.java
+++ /dev/null
@@ -1,270 +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.car.app;
-
-import static androidx.car.app.CarAppVersion.ReleaseSuffix.RELEASE_SUFFIX_BETA;
-import static androidx.car.app.CarAppVersion.ReleaseSuffix.RELEASE_SUFFIX_EAP;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.Locale;
-
-/**
- * A versioning class used for compatibility checks between the host and client.
- *
- * <p>The version scheme follows semantic versioning and is defined as:
- *
- * <pre>major.minor.patch[-releasesSuffix.build]<pre>
- *
- * where:
- *
- * <ul>
- *   <li>major version differences denote binary incompatibility (e.g. API removal)
- *   <li>minor version differences denote compatible API changes (e.g. API additional/deprecation)
- *   <li>patch version differences denote non-API altering internal changes (e.g. bug fixes)
- *   <li>releaseSuffix is{@code null} for stable versions but otherwise reserved for special-purpose
- *       EAP builds and one-off public betas. For cases where a release suffix is provided, the
- *       appended build number is used to differentiate versions within the same suffix category.
- *       (e.g. 1.0.0-eap.1 vs 1.0.0-eap.2).
- * </ul>
- */
-public class CarAppVersion {
-    private static final String MAIN_VERSION_FORMAT = "%d.%d.%d";
-    private static final String SUFFIX_VERSION_FORMAT = "-%s.%d";
-
-    public static final CarAppVersion INSTANCE =
-            CarAppVersion.create(1, 0, 0, RELEASE_SUFFIX_BETA, 1);
-
-    /** Min version of the SDK which supports handshake completed binder call. */
-    public static final CarAppVersion HANDSHAKE_MIN_VERSION =
-            CarAppVersion.create(1, 0, 0, RELEASE_SUFFIX_BETA, 1);
-
-    /** Different types of supported version suffixes. */
-    public enum ReleaseSuffix {
-        RELEASE_SUFFIX_EAP("eap"),
-        RELEASE_SUFFIX_BETA("beta");
-
-        private final String mValue;
-
-        ReleaseSuffix(String value) {
-            this.mValue = value;
-        }
-
-        /** Creates a {@link ReleaseSuffix} from the string standard representation as described
-         * in the {@link #toString()} method */
-        @NonNull
-        public static ReleaseSuffix fromString(@NonNull String value) {
-            switch (value) {
-                case "eap":
-                    return RELEASE_SUFFIX_EAP;
-                case "beta":
-                    return RELEASE_SUFFIX_BETA;
-                default:
-                    throw new IllegalArgumentException(value + " is not a valid release suffix");
-            }
-        }
-
-        @NonNull
-        @Override
-        public String toString() {
-            return mValue;
-        }
-    }
-
-    private final int mMajor;
-    private final int mMinor;
-    private final int mPatch;
-    @Nullable
-    private final ReleaseSuffix mReleaseSuffix;
-    private final int mBuild;
-
-    /** Creates a {@link CarAppVersion} without a release suffix. (e.g. 1.0.0) */
-    @NonNull
-    static CarAppVersion create(int major, int minor, int patch) {
-        return new CarAppVersion(major, minor, patch, null, 0);
-    }
-
-    /**
-     * Creates a {@link CarAppVersion} with a release suffix and build number. (e.g. 1.0.0-eap.0)
-     *
-     * <p>Note that if {@code releaseSuffix} is {@code null}, then {@code build} is ignored.
-     */
-    @NonNull
-    static CarAppVersion create(
-            int major, int minor, int patch, ReleaseSuffix releaseSuffix, int build) {
-        return new CarAppVersion(major, minor, patch, releaseSuffix, build);
-    }
-
-    /**
-     * Creates a {@link CarAppVersion} instance based on its string representation.
-     *
-     * <p>The string should be of the format major.minor.patch(-releaseSuffix.build). If the
-     * string is malformed or {@code null}, {@code null} will be returned.
-     */
-    @Nullable
-    public static CarAppVersion of(@NonNull String versionString) throws MalformedVersionException {
-        return parseVersionString(versionString);
-    }
-
-    private static CarAppVersion parseVersionString(String versionString)
-            throws MalformedVersionException {
-        String[] versionSplit = versionString.split("-", -1);
-        if (versionSplit.length > 2) {
-            throw new MalformedVersionException(
-                    "Malformed version string (more than 1 \"-\" detected): " + versionString);
-        }
-
-        String mainVersion = versionSplit[0];
-        String[] mainVersionSplit = mainVersion.split("\\.", -1);
-
-        // Main version should be formatted as major.minor.patch
-        if (mainVersionSplit.length != 3) {
-            throw new MalformedVersionException(
-                    "Malformed version string (invalid main version format): " + versionString);
-        }
-
-        int major;
-        int minor;
-        int patch;
-        ReleaseSuffix releaseSuffix = null;
-        int build = 0;
-
-        try {
-            major = Integer.parseInt(mainVersionSplit[0]);
-            minor = Integer.parseInt(mainVersionSplit[1]);
-            patch = Integer.parseInt(mainVersionSplit[2]);
-
-            String suffixVersion = versionSplit.length > 1 ? versionSplit[1] : null;
-            if (suffixVersion != null) {
-                String[] suffixVersionSplit = suffixVersion.split("\\.", -1);
-
-                // Release suffix should be formatted as releaseSuffix.build
-                if (suffixVersionSplit.length != 2) {
-                    throw new MalformedVersionException(
-                            "Malformed version string (invalid suffix version format): "
-                                    + versionString);
-                }
-
-                try {
-                    releaseSuffix = ReleaseSuffix.fromString(suffixVersionSplit[0]);
-                } catch (IllegalArgumentException e) {
-                    throw new MalformedVersionException(
-                            "Malformed version string (unsupported suffix): " + versionString, e);
-                }
-                build = Integer.parseInt(suffixVersionSplit[1]);
-            }
-
-            return new CarAppVersion(major, minor, patch, releaseSuffix, build);
-        } catch (NumberFormatException exception) {
-            throw new MalformedVersionException(
-                    "Malformed version string (unsupported characters): " + versionString,
-                    exception);
-        }
-    }
-
-    private CarAppVersion(
-            int major, int minor, int patch, @Nullable ReleaseSuffix releaseSuffix, int build) {
-        this.mMajor = major;
-        this.mMinor = minor;
-        this.mPatch = patch;
-        this.mReleaseSuffix = releaseSuffix;
-        this.mBuild = build;
-    }
-
-    /** Returns the human-readable format of this version. */
-    @NonNull
-    @Override
-    public String toString() {
-        String versionString = String.format(Locale.US, MAIN_VERSION_FORMAT, mMajor, mMinor,
-                mPatch);
-        if (mReleaseSuffix != null) {
-            versionString += String.format(Locale.US, SUFFIX_VERSION_FORMAT, mReleaseSuffix,
-                    mBuild);
-        }
-
-        return versionString;
-    }
-
-    /**
-     * Checks whether this {@link CarAppVersion} is greater than or equal to {@code other}, which is
-     * used to determine compatibility. Returns true if so, false otherwise.
-     *
-     * <p>The rules of comparison are as follow:
-     *
-     * <ul>
-     *   <li>If either versions are suffixed with {@link ReleaseSuffix#RELEASE_SUFFIX_EAP}, the
-     *   version strings have to be exact match to be considered compatible.
-     *   <li>The major version has to be greater or equal to be considered compatible.
-     *   <li>If major versions are equal, the minor version has to be greater or equal to be
-     *   considered compatible.
-     *   <li>If major.minor versions are equal, the patch version has to be greater or equal to
-     *   be considered compatible.
-     *   <li>If the major.minor.patch versions are equal, for stable versions release suffix
-     *   equals {@code null}, the instance is considered compatible with all
-     *   {@link ReleaseSuffix#RELEASE_SUFFIX_BETA} versions; for
-     *   {@link ReleaseSuffix#RELEASE_SUFFIX_BETA}, the instance is considered compatible iff the
-     *   other is a {@link ReleaseSuffix#RELEASE_SUFFIX_BETA} version and that the build is
-     *   greater or equal to the other version.
-     * </ul>
-     */
-    public boolean isGreaterOrEqualTo(@NonNull CarAppVersion other) {
-        // For EAP versions, we require an exact match.
-        if (mReleaseSuffix == RELEASE_SUFFIX_EAP || other.mReleaseSuffix == RELEASE_SUFFIX_EAP) {
-            return mMajor == other.mMajor
-                    && mMinor == other.mMinor
-                    && mPatch == other.mPatch
-                    && mReleaseSuffix == other.mReleaseSuffix
-                    && mBuild == other.mBuild;
-        }
-
-        int result = Integer.compare(mMajor, other.mMajor);
-        // Major version differs, return.
-        if (result != 0) {
-            return result == 1;
-        }
-
-        // Minor version differs, return.
-        result = Integer.compare(mMinor, other.mMinor);
-        if (result != 0) {
-            return result == 1;
-        }
-
-        // Patch version differs, return.
-        result = Integer.compare(mPatch, other.mPatch);
-        if (result != 0) {
-            return result == 1;
-        }
-
-        if (mReleaseSuffix == null) {
-            // A stable version (is considered compatible to any beta versions, the suffix
-            // version is
-            // ignored in those cases.
-            return true;
-        } else if (mReleaseSuffix == RELEASE_SUFFIX_BETA) {
-            // Compatible if beta build is greater than other's beta build.
-            if (other.mReleaseSuffix == RELEASE_SUFFIX_BETA) {
-                return mBuild >= other.mBuild;
-            } else {
-                // Beta version is incompatible with stable version.
-                return false;
-            }
-        } else {
-            throw new IllegalStateException("Invalid release suffix: " + mReleaseSuffix);
-        }
-    }
-}
diff --git a/car/app/app/src/main/java/androidx/car/app/CarContext.java b/car/app/app/src/main/java/androidx/car/app/CarContext.java
index 11837d2..91d05f1 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarContext.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarContext.java
@@ -40,9 +40,12 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.StringDef;
+import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.navigation.NavigationManager;
 import androidx.car.app.utils.RemoteUtils;
 import androidx.car.app.utils.ThreadUtils;
+import androidx.car.app.versioning.CarAppApiLevel;
+import androidx.car.app.versioning.CarAppApiLevels;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleOwner;
 
@@ -81,22 +84,22 @@
      *
      * @hide
      */
-    @StringDef({APP_SERVICE, CAR_SERVICE, NAVIGATION_SERVICE, SCREEN_MANAGER_SERVICE})
+    @StringDef({APP_SERVICE, CAR_SERVICE, NAVIGATION_SERVICE, SCREEN_SERVICE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface CarServiceType {
     }
 
     /** Manages all app events such as invalidating the UI, showing a toast, etc. */
-    public static final String APP_SERVICE = "app_manager";
+    public static final String APP_SERVICE = "app";
 
     /**
      * Manages all navigation events such as starting navigation when focus is granted, abandoning
      * navigation when focus is lost, etc.
      */
-    public static final String NAVIGATION_SERVICE = "navigation_manager";
+    public static final String NAVIGATION_SERVICE = "navigation";
 
     /** Manages the screens of the app, including the screen stack. */
-    public static final String SCREEN_MANAGER_SERVICE = "screen_manager";
+    public static final String SCREEN_SERVICE = "screen";
 
     /**
      * Internal usage only. Top level binder to host.
@@ -107,7 +110,8 @@
      * Key for including a IStartCarApp in the notification {@link Intent}, for starting the app
      * if it has not been opened yet.
      */
-    public static final String START_CAR_APP_BINDER_KEY = "StartCarAppBinderKey";
+    public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra"
+            + ".START_CAR_APP_BINDER_KEY";
 
     /**
      * Standard action for navigating to a location.
@@ -121,9 +125,12 @@
     private final NavigationManager mNavigationManager;
     private final ScreenManager mScreenManager;
     private final OnBackPressedDispatcher mOnBackPressedDispatcher;
-
     private final HostDispatcher mHostDispatcher;
 
+    /** API level, updated once host connection handshake is completed. */
+    @CarAppApiLevel
+    private int mCarAppApiLevel = CarAppApiLevels.UNKNOWN;
+
     /** @hide */
     @NonNull
     @RestrictTo(LIBRARY)
@@ -143,13 +150,13 @@
      *   <dd>An {@link AppManager} for communication between the app and the host.
      *   <dt>{@link #NAVIGATION_SERVICE}
      *   <dd>A {@link NavigationManager} for management of navigation updates.
-     *   <dt>{@link #SCREEN_MANAGER_SERVICE}
+     *   <dt>{@link #SCREEN_SERVICE}
      *   <dd>A {@link ScreenManager} for management of {@link Screen}s.
      * </dl>
      *
      * @param name The name of the car service requested. This should be one of
      *             {@link #APP_SERVICE},
-     *             {@link #NAVIGATION_SERVICE} or {@link #SCREEN_MANAGER_SERVICE}.
+     *             {@link #NAVIGATION_SERVICE} or {@link #SCREEN_SERVICE}.
      * @return The car service instance.
      * @throws IllegalArgumentException if {@code name} does not refer to a valid car service.
      * @throws NullPointerException     if {@code name} is {@code null}.
@@ -162,7 +169,7 @@
                 return mAppManager;
             case NAVIGATION_SERVICE:
                 return mNavigationManager;
-            case SCREEN_MANAGER_SERVICE:
+            case SCREEN_SERVICE:
                 return mScreenManager;
             default: // fall out
         }
@@ -206,7 +213,7 @@
         } else if (serviceClass.isInstance(mNavigationManager)) {
             return NAVIGATION_SERVICE;
         } else if (serviceClass.isInstance(mScreenManager)) {
-            return SCREEN_MANAGER_SERVICE;
+            return SCREEN_SERVICE;
         }
 
         throw new IllegalArgumentException("The class does not correspond to a car service.");
@@ -215,9 +222,8 @@
     /**
      * Starts a car app on the car screen.
      *
-     * <p>The target application will get the {@link Intent} via
-     * {@link CarAppService#onCreateScreen}
-     * or {@link CarAppService#onNewIntent}.
+     * <p>The target application will get the {@link Intent} via {@link Session#onCreateScreen}
+     * or {@link Session#onNewIntent}.
      *
      * <p>Supported {@link Intent}s:
      *
@@ -282,7 +288,7 @@
         IBinder binder = null;
         Bundle extras = notificationIntent.getExtras();
         if (extras != null) {
-            binder = extras.getBinder(START_CAR_APP_BINDER_KEY);
+            binder = extras.getBinder(EXTRA_START_CAR_APP_BINDER_KEY);
         }
         if (binder == null) {
             throw new IllegalArgumentException("Notification intent missing expected extra");
@@ -301,10 +307,10 @@
     /**
      * Requests to finish the car app.
      *
-     * <p>Call this when your app is done and should be closed.
+     * <p>Call this when your app is done and should be closed. The {@link Session} corresponding
+     * to this {@link CarContext} will become {@code State.DESTROYED}.
      *
-     * <p>At some point after this call, {@link CarAppService#onCarAppFinished} will be called, and
-     * eventually the OS will destroy your {@link CarAppService}.
+     * <p>At some point after this call, the OS will destroy your {@link CarAppService}.
      */
     public void finishCarApp() {
         mHostDispatcher.dispatch(
@@ -323,7 +329,7 @@
      * determines that conditions warrant it, as signaled by the value returned by this method.
      *
      * <p>Whenever the dark mode status changes, you will receive a call to {@link
-     * CarAppService#onCarConfigurationChanged}.
+     * Session#onCarConfigurationChanged}.
      */
     public boolean isDarkMode() {
         return (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
@@ -374,6 +380,17 @@
     }
 
     /**
+     * Updates context information based on the information provided during connection handshake
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    @MainThread
+    void updateHandshakeInfo(HandshakeInfo handshakeInfo) {
+        mCarAppApiLevel = handshakeInfo.getHostCarAppApiLevel();
+    }
+
+    /**
      * Attaches the base {@link Context} for this {@link CarContext} by creating a new display
      * context using {@link #createDisplayContext} with a {@link VirtualDisplay} created using
      * the metrics from the provided {@link Configuration}, and then also calling {@link
@@ -431,6 +448,39 @@
         mHostDispatcher.resetHosts();
     }
 
+    /**
+     * Retrieves the API level negotiated with the host.
+     * <p>
+     * API levels are used during client and host connection handshake to negotiate a common set of
+     * elements that both processes can understand. Different devices might have different host
+     * versions. Each of these hosts will support a
+     * range of API levels, as a way to provide backwards compatibility.
+     * <p>
+     * Applications can also provide forward compatibility, by declaring support for a
+     * {@link AppInfo#getMinCarAppApiLevel()} lower than {@link AppInfo#getLatestCarAppApiLevel()}.
+     * See {@link AppInfo#getMinCarAppApiLevel()} for more details.
+     * <p>
+     * Clients must ensure no elements annotated with a {@link RequiresCarApi} value higher
+     * than {@link #getCarAppApiLevel()} is used at runtime.
+     * <p>
+     * Please refer to {@link RequiresCarApi} description for more details on how to
+     * implement forward compatibility.
+     *
+     * @return a value between {@link AppInfo#getMinCarAppApiLevel()} and
+     * {@link AppInfo#getLatestCarAppApiLevel()}. In case of incompatibility, the host will
+     * disconnect from the service before completing the handshake.
+     *
+     * @throws IllegalStateException if invoked before the connection handshake with the host has
+     * been completed (for example, before {@link Session#onCreateScreen(Intent)}).
+     */
+    @CarAppApiLevel
+    public int getCarAppApiLevel() {
+        if (mCarAppApiLevel == CarAppApiLevels.UNKNOWN) {
+            throw new IllegalStateException("Car App API level hasn't been established yet");
+        }
+        return mCarAppApiLevel;
+    }
+
     /** @hide */
     @RestrictTo(LIBRARY_GROUP) // Restrict to testing library
     @SuppressWarnings({
@@ -442,7 +492,7 @@
 
         this.mHostDispatcher = hostDispatcher;
         mAppManager = AppManager.create(this, hostDispatcher);
-        mNavigationManager = NavigationManager.create(hostDispatcher);
+        mNavigationManager = NavigationManager.create(this, hostDispatcher);
         mScreenManager = ScreenManager.create(this, lifecycle);
         mOnBackPressedDispatcher =
                 new OnBackPressedDispatcher(() -> getCarService(ScreenManager.class).pop());
diff --git a/car/app/app/src/main/java/androidx/car/app/FailureResponse.java b/car/app/app/src/main/java/androidx/car/app/FailureResponse.java
index bf8e38a..a4aede9 100644
--- a/car/app/app/src/main/java/androidx/car/app/FailureResponse.java
+++ b/car/app/app/src/main/java/androidx/car/app/FailureResponse.java
@@ -16,6 +16,8 @@
 
 package androidx.car.app;
 
+import static androidx.annotation.RestrictTo.Scope;
+
 import static java.util.Objects.requireNonNull;
 
 import android.os.RemoteException;
@@ -24,11 +26,13 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
 import androidx.car.app.serialization.BundlerException;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.security.InvalidParameterException;
+import java.util.Objects;
 
 /**
  * Denotes a failure in the client to a host request.
@@ -51,6 +55,7 @@
                     RUNTIME_EXCEPTION,
                     REMOTE_EXCEPTION
             })
+    @RestrictTo(Scope.LIBRARY)
     @Retention(RetentionPolicy.SOURCE)
     public @interface ErrorType {
     }
@@ -68,6 +73,11 @@
     @ErrorType
     private final int mErrorType;
 
+    /**
+     * Creates an instance of {@link FailureResponse}.
+     *
+     * @param exception the originating cause of the failure.
+     */
     public FailureResponse(@NonNull Throwable exception) {
         this.mStackTrace = Log.getStackTraceString(requireNonNull(exception));
         if (exception instanceof BundlerException) {
@@ -88,18 +98,35 @@
     }
 
     // Used for serialization.
-    public FailureResponse() {
+    private FailureResponse() {
         mStackTrace = null;
         mErrorType = UNKNOWN_ERROR;
     }
 
+    /** Returns the stack trace of the originating exception. */
     @NonNull
     public String getStackTrace() {
         return requireNonNull(mStackTrace);
     }
 
+    /** Returns the type of the originating exception. */
     @ErrorType
     public int getErrorType() {
         return mErrorType;
     }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mErrorType, mStackTrace);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof FailureResponse)) {
+            return false;
+        }
+        FailureResponse other = (FailureResponse) obj;
+
+        return mErrorType == other.mErrorType && Objects.equals(mStackTrace, other.mStackTrace);
+    }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/HandshakeInfo.java b/car/app/app/src/main/java/androidx/car/app/HandshakeInfo.java
index 8d3f137..a3328fe 100644
--- a/car/app/app/src/main/java/androidx/car/app/HandshakeInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/HandshakeInfo.java
@@ -20,31 +20,45 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
 
 /**
  * A container for the information conveyed by the host after the handshake with the app is
  * completed.
- *
- * @hide
  */
-@RestrictTo(Scope.LIBRARY)
 public class HandshakeInfo {
     @Nullable
     private final String mHostPackageName;
+    private final int mHostCarAppApiLevel;
 
-    public HandshakeInfo(@NonNull String hostPackageName) {
-        this.mHostPackageName = hostPackageName;
+    /**
+     * Creates an instance of {@link HandshakeInfo}.
+     *
+     * @param hostPackageName    the host package name.
+     * @param hostCarAppApiLevel the API level that should be used to communicate with the host.
+     */
+    public HandshakeInfo(@NonNull String hostPackageName, int hostCarAppApiLevel) {
+        mHostPackageName = hostPackageName;
+        mHostCarAppApiLevel = hostCarAppApiLevel;
     }
 
     // Used for serialization
-    public HandshakeInfo() {
+    private HandshakeInfo() {
         mHostPackageName = null;
+        mHostCarAppApiLevel = 0;
     }
 
+    /**
+     * Returns the host package name.
+     */
     @NonNull
     public String getHostPackageName() {
         return requireNonNull(mHostPackageName);
     }
+
+    /**
+     * Returns the negotiated API level that should be used to communicate with the host.
+     */
+    public int getHostCarAppApiLevel() {
+        return mHostCarAppApiLevel;
+    }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/HostException.java b/car/app/app/src/main/java/androidx/car/app/HostException.java
index a7e878e..092ac41 100644
--- a/car/app/app/src/main/java/androidx/car/app/HostException.java
+++ b/car/app/app/src/main/java/androidx/car/app/HostException.java
@@ -16,28 +16,35 @@
 
 package androidx.car.app;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-
 import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
 
 /** Exceptions that happen on calls to the host. */
 // Developers can catch this exception so keeping it.
 public class HostException extends RuntimeException {
-    /** @hide */
-    @RestrictTo(LIBRARY)
+    /**
+     * Creates an instance of {@link HostException} with the given {@code message}.
+     *
+     * @param message the exception message.
+     */
     public HostException(@NonNull String message) {
         super(message);
     }
 
-    /** @hide */
-    @RestrictTo(LIBRARY)
+    /**
+     * Creates an instance of {@link HostException} with the given {@code message}.
+     *
+     * @param message the exception message.
+     * @param cause   the originating cause of the exception.
+     */
     public HostException(@NonNull String message, @NonNull Throwable cause) {
         super(message, cause);
     }
 
-    /** @hide */
-    @RestrictTo(LIBRARY)
+    /**
+     * Creates an instance of {@link HostException} with the given {@code cause}.
+     *
+     * @param cause the originating cause of the exception.
+     */
     public HostException(@NonNull Throwable cause) {
         super(cause);
     }
diff --git a/car/app/app/src/main/java/androidx/car/app/HostInfo.java b/car/app/app/src/main/java/androidx/car/app/HostInfo.java
index 5b1dad2..f22f061 100644
--- a/car/app/app/src/main/java/androidx/car/app/HostInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/HostInfo.java
@@ -21,7 +21,7 @@
 import androidx.annotation.NonNull;
 
 /**
- * Container class for information about the host the service is connected to.
+ * Container class for information about the host the app is connected to.
  *
  * <p>Apps can use this information to determine how they will respond to the host. For example, a
  * host which is not recognized could receive a message screen while an authorized host could
@@ -29,16 +29,21 @@
  *
  * <p>The package name and uid can used to query the system package manager for a signature or to
  * determine if the host has a system signature.
+ *
+ * <p>The host API level can be used to adjust the models exchanged with the host to those valid
+ * for the specific host version the app is connected to.
  */
 public class HostInfo {
     @NonNull
     private final String mPackageName;
     private final int mUid;
 
-    /** Constructs an instance of the HostInfo from the required package name and uid. */
+    /**
+     * Constructs an instance of the HostInfo from the required package name, uid and API level.
+     */
     public HostInfo(@NonNull String packageName, int uid) {
-        this.mPackageName = requireNonNull(packageName);
-        this.mUid = uid;
+        mPackageName = requireNonNull(packageName);
+        mUid = uid;
     }
 
     /** Retrieves the package name of the host. */
diff --git a/car/app/app/src/main/java/androidx/car/app/OnScreenResultCallback.java b/car/app/app/src/main/java/androidx/car/app/OnScreenResultListener.java
similarity index 92%
rename from car/app/app/src/main/java/androidx/car/app/OnScreenResultCallback.java
rename to car/app/app/src/main/java/androidx/car/app/OnScreenResultListener.java
index 24f0ed3..8e19f38 100644
--- a/car/app/app/src/main/java/androidx/car/app/OnScreenResultCallback.java
+++ b/car/app/app/src/main/java/androidx/car/app/OnScreenResultListener.java
@@ -18,8 +18,8 @@
 
 import androidx.annotation.Nullable;
 
-/** A callback to provide the result set by a {@link Screen}. */
-public interface OnScreenResultCallback {
+/** A listener to provide the result set by a {@link Screen}. */
+public interface OnScreenResultListener {
     /**
      * Provides the {@code result} from the {@link Screen} that was pushed using {@link
      * ScreenManager#pushForResult}, or {@code null} if no result was set.
diff --git a/car/app/app/src/main/java/androidx/car/app/Screen.java b/car/app/app/src/main/java/androidx/car/app/Screen.java
index f36b455..9fe39a3 100644
--- a/car/app/app/src/main/java/androidx/car/app/Screen.java
+++ b/car/app/app/src/main/java/androidx/car/app/Screen.java
@@ -54,18 +54,12 @@
 // actually cleaning any held resources in that method.
 @SuppressWarnings("NotCloseable")
 public abstract class Screen implements LifecycleOwner {
-    /**
-     * A marker to use with {@link ScreenManager#popTo} when it should pop all the way to the root
-     * screen in the stack.
-     */
-    public static final String ROOT = "ROOT";
-
     private final CarContext mCarContext;
 
     @SuppressWarnings({"assignment.type.incompatible", "argument.type.incompatible"})
     private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
 
-    private OnScreenResultCallback mOnScreenResultCallback = (obj) -> {
+    private OnScreenResultListener mOnScreenResultListener = (obj) -> {
     };
 
     @Nullable
@@ -126,7 +120,7 @@
     }
 
     /**
-     * Sets the {@code result} that will be sent to the {@link OnScreenResultCallback} that was
+     * Sets the {@code result} that will be sent to the {@link OnScreenResultListener} that was
      * given when pushing this screen onto the stack using {@link ScreenManager#pushForResult}.
      *
      * <p>Only the final {@code result} set will be sent.
@@ -134,7 +128,7 @@
      * <p>The {@code result} will be propagated when this screen is being destroyed. This can be due
      * to being removed from the stack or explicitly calling {@link #finish}.
      *
-     * @param result the value to send to the {@link OnScreenResultCallback} that was given when
+     * @param result the value to send to the {@link OnScreenResultListener} that was given when
      *               pushing this screen onto the stack using {@link ScreenManager#pushForResult}
      */
     public void setResult(@Nullable Object result) {
@@ -306,9 +300,9 @@
     @NonNull
     public abstract Template onGetTemplate();
 
-    /** Sets a {@link OnScreenResultCallback} for this {@link Screen}. */
-    void setOnResultCallback(OnScreenResultCallback onScreenResultCallback) {
-        this.mOnScreenResultCallback = onScreenResultCallback;
+    /** Sets a {@link OnScreenResultListener} for this {@link Screen}. */
+    void setOnScreenResultListener(OnScreenResultListener onScreenResultListener) {
+        this.mOnScreenResultListener = onScreenResultListener;
     }
 
     /**
@@ -322,7 +316,7 @@
         ThreadUtils.runOnMain(
                 () -> {
                     if (event == Event.ON_DESTROY) {
-                        mOnScreenResultCallback.onScreenResult(mResult);
+                        mOnScreenResultListener.onScreenResult(mResult);
                     }
 
                     mLifecycleRegistry.handleLifecycleEvent(event);
diff --git a/car/app/app/src/main/java/androidx/car/app/ScreenManager.java b/car/app/app/src/main/java/androidx/car/app/ScreenManager.java
index 56b3365..556214e 100644
--- a/car/app/app/src/main/java/androidx/car/app/ScreenManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/ScreenManager.java
@@ -18,6 +18,7 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 import static androidx.car.app.utils.CommonUtils.TAG;
+import static androidx.car.app.utils.ThreadUtils.checkMainThread;
 
 import static java.util.Objects.requireNonNull;
 
@@ -29,7 +30,6 @@
 import androidx.annotation.RestrictTo;
 import androidx.car.app.model.TemplateInfo;
 import androidx.car.app.model.TemplateWrapper;
-import androidx.car.app.utils.ThreadUtils;
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.Lifecycle.Event;
@@ -44,9 +44,8 @@
 
 /**
  * Manages the stack of {@link Screen}s and their respective {@link Lifecycle}s.
- *
- * <p>This class is not safe for concurrent access.
  */
+@MainThread
 public class ScreenManager {
     private final Deque<Screen> mScreenStack = new ArrayDeque<>();
     private final CarContext mCarContext;
@@ -58,10 +57,12 @@
      * @throws NullPointerException if the method is called before a {@link Screen} has been
      *                              pushed to the stack via {@link #push}, or
      *                              {@link #pushForResult}, or returning a {@link Screen} from
-     *                              {@link CarAppService#onCreateScreen}.
+     *                              {@link Session#onCreateScreen}.
+     * @throws IllegalStateException if the current thread is not the main thread.
      */
     @NonNull
     public Screen getTop() {
+        checkMainThread();
         return requireNonNull(mScreenStack.peek());
     }
 
@@ -72,8 +73,10 @@
      * stack.
      *
      * @throws NullPointerException if {@code screen} is {@code null}.
+     * @throws IllegalStateException if the current thread is not the main thread.
      */
     public void push(@NonNull Screen screen) {
+        checkMainThread();
         pushInternal(requireNonNull(screen));
     }
 
@@ -81,17 +84,22 @@
      * Pushes a {@link Screen}, for which you would like a result from, onto the stack.
      *
      * <p>When the given {@code screen} finishes, the {@code onScreenResultCallback} will receive a
-     * callback to {@link OnScreenResultCallback#onScreenResult} with the result that the pushed
+     * callback to {@link OnScreenResultListener#onScreenResult} with the result that the pushed
      * {@code screen} set via {@link Screen#setResult}.
      *
+     * @param screen the {@link Screen} to push on top of the stack.
+     * @param onScreenResultListener the listener that will be executed with the result pushed by
+     *                               the {@code screen} through {@link Screen#setResult}. This
+     *                               callback will be executed on the main thread.
      * @throws NullPointerException if either the {@code screen} or the {@code
      *                              onScreenResultCallback} are {@code null}.
+     * @throws IllegalStateException if the current thread is not the main thread.
      */
-    // TODO(rampara): Add Executor parameter.
     @SuppressLint("ExecutorRegistration")
     public void pushForResult(
-            @NonNull Screen screen, @NonNull OnScreenResultCallback onScreenResultCallback) {
-        requireNonNull(screen).setOnResultCallback(requireNonNull(onScreenResultCallback));
+            @NonNull Screen screen, @NonNull OnScreenResultListener onScreenResultListener) {
+        checkMainThread();
+        requireNonNull(screen).setOnScreenResultListener(requireNonNull(onScreenResultListener));
         pushInternal(screen);
     }
 
@@ -99,8 +107,11 @@
      * Pops the top {@link Screen} from the stack.
      *
      * <p>If the top {@link Screen} is the only {@link Screen} in the stack, it will not be removed.
+     *
+     * @throws IllegalStateException if the current thread is not the main thread.
      */
     public void pop() {
+        checkMainThread();
         if (mScreenStack.size() > 1) {
             popInternal(Collections.singletonList(mScreenStack.pop()));
         }
@@ -110,14 +121,15 @@
      * Removes screens from the top of the stack until a {@link Screen} which has the given {@code
      * marker} is found, or the root has been reached.
      *
-     * <p>To pop to root use {@link Screen#ROOT} as the {@code marker}.
-     *
      * <p>The root {@link Screen} will not be popped.
      *
      * @throws NullPointerException if {@code marker} is {@code null}.
+     * @throws IllegalStateException if the current thread is not the main thread.
+     *
      * @see Screen#setMarker
      */
     public void popTo(@NonNull String marker) {
+        checkMainThread();
         requireNonNull(marker);
 
         // Pop all screens, except until found root or the provided screen.
@@ -135,13 +147,36 @@
     }
 
     /**
+     * Removes all screens from the stack until the root has been reached.
+     *
+     * @throws IllegalStateException if the current thread is not the main thread.
+     */
+    public void popToRoot() {
+        checkMainThread();
+
+        if (mScreenStack.size() <= 1) {
+            return;
+        }
+
+        // Pop all screens, except until found root or the provided screen.
+        List<Screen> screensToPop = new ArrayList<>();
+        while (mScreenStack.size() > 1) {
+            screensToPop.add(mScreenStack.pop());
+        }
+
+        popInternal(screensToPop);
+    }
+
+    /**
      * Removes the {@code screen} from the stack.
      *
      * <p>If the {@code screen} is the only {@link Screen} in the stack, it will not be removed.
      *
      * @throws NullPointerException if {@code screen} is {@code null}.
+     * @throws IllegalStateException if the current thread is not the main thread.
      */
     public void remove(@NonNull Screen screen) {
+        checkMainThread();
         requireNonNull(screen);
 
         if (mScreenStack.size() <= 1) {
@@ -169,9 +204,8 @@
 
     /** Returns the {@link TemplateWrapper} for the {@link Screen} that is on top of the stack. */
     @NonNull
-    @MainThread
     TemplateWrapper getTopTemplate() {
-        ThreadUtils.checkMainThread();
+        checkMainThread();
 
         Screen screen = getTop();
         Log.d(TAG, "Requesting template from Screen " + screen);
@@ -202,10 +236,6 @@
     }
 
     private boolean foundMarker(String marker) {
-        if (Screen.ROOT.equals(marker)) {
-            return mScreenStack.size() < 2;
-        }
-
         return marker.equals(getTop().getMarker());
     }
 
diff --git a/car/app/app/src/main/java/androidx/car/app/SearchListener.java b/car/app/app/src/main/java/androidx/car/app/SearchListener.java
deleted file mode 100644
index ed814d4..0000000
--- a/car/app/app/src/main/java/androidx/car/app/SearchListener.java
+++ /dev/null
@@ -1,41 +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.car.app;
-
-import androidx.annotation.NonNull;
-
-/** A listener for search updates. */
-public interface SearchListener {
-    /**
-     * Notifies the current {@code searchText}.
-     *
-     * <p>The host may invoke this callback as the user types a search text. The frequency of these
-     * updates is not guaranteed to be after every individual keystroke. The host may decide to wait
-     * for several keystrokes before sending a single update.
-     *
-     * @param searchText the current search text that the user has typed.
-     */
-    void onSearchTextChanged(@NonNull String searchText);
-
-    /**
-     * Notifies that the user has submitted the search and the given {@code searchText} is the final
-     * term.
-     *
-     * @param searchText the search text that the user typed.
-     */
-    void onSearchSubmitted(@NonNull String searchText);
-}
diff --git a/car/app/app/src/main/java/androidx/car/app/Session.java b/car/app/app/src/main/java/androidx/car/app/Session.java
new file mode 100644
index 0000000..adb5507
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/Session.java
@@ -0,0 +1,148 @@
+/*
+ * 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.car.app;
+
+import android.content.Intent;
+import android.content.res.Configuration;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
+/**
+ * The base class for implementing a session for a car app.
+ */
+public abstract class Session implements LifecycleOwner {
+    private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
+    private final CarContext mCarContext = CarContext.create(mRegistry);
+
+    /**
+     * Requests the first {@link Screen} for the application.
+     *
+     * <p>Once the method returns, {@link Screen#onGetTemplate()} will be called on the
+     * {@link Screen} returned, and the app will be displayed on the car screen.
+     *
+     * <p>To pre-seed a back stack, you can push {@link Screen}s onto the stack, via {@link
+     * ScreenManager#push} during this method call.
+     *
+     * <p>Called by the system, do not call this method directly.
+     *
+     * @param intent the intent that was used to start this app. If the app was started with a
+     *               call to {@link CarContext#startCarApp}, this intent will be equal to the
+     *               intent passed to that method.
+     */
+    @NonNull
+    public abstract Screen onCreateScreen(@NonNull Intent intent);
+
+    /**
+     * Notifies that the car app has received a new {@link Intent}.
+     *
+     * <p>Once the method returns, {@link Screen#onGetTemplate} will be called on the {@link Screen}
+     * that is on top of the {@link Screen} stack managed by the {@link ScreenManager}, and the app
+     * will be displayed on the car screen.
+     *
+     * <p>Often used to update the current {@link Screen} or pushing a new one on the stack,
+     * based off of the information in the {@code intent}.
+     *
+     * <p>Called by the system, do not call this method directly.
+     *
+     * @param intent the intent that was used to start this app. If the app was started with a
+     *               call to {@link CarContext#startCarApp}, this intent will be equal to the
+     *               intent passed to that method.
+     * @see CarContext#startCarApp
+     */
+    public void onNewIntent(@NonNull Intent intent) {
+    }
+
+    /**
+     * Notifies that the {@link CarContext}'s {@link Configuration} has changed.
+     *
+     * <p>At the time that this function is called, the {@link CarContext}'s resources object will
+     * have been updated to return resource values matching the new configuration.
+     *
+     * <p>Called by the system, do not call this method directly.
+     *
+     * @see CarContext
+     */
+    public void onCarConfigurationChanged(@NonNull Configuration newConfiguration) {
+    }
+
+    /**
+     * Returns the {@link Session}'s {@link Lifecycle}.
+     *
+     * <p>Here are some of the ways you can use the sessions's {@link Lifecycle}:
+     *
+     * <ul>
+     *   <li>Observe its {@link Lifecycle} by calling {@link Lifecycle#addObserver}. You can use the
+     *       {@link androidx.lifecycle.LifecycleObserver} to take specific actions whenever the
+     *       {@link Screen} receives different {@link Lifecycle.Event}s.
+     *   <li>Use this {@link CarAppService} to observe {@link androidx.lifecycle.LiveData}s that
+     *       may drive the backing data for your application.
+     * </ul>
+     *
+     * <p>What each lifecycle related event means for a session:
+     *
+     * <dl>
+     *   <dt>{@link Lifecycle.Event#ON_CREATE}
+     *   <dd>The session has just been launched, and this session is being initialized. {@link
+     *       #onCreateScreen} will be called at a point after this call.
+     *   <dt>{@link #onCreateScreen}
+     *   <dd>The host is ready for this session to create the first {@link Screen} so that it can
+     *       display its template.
+     *   <dt>{@link Lifecycle.Event#ON_START}
+     *   <dd>The application is now visible in the car screen.
+     *   <dt>{@link Lifecycle.Event#ON_RESUME}
+     *   <dd>The user can now interact with this application.
+     *   <dt>{@link Lifecycle.Event#ON_PAUSE}
+     *   <dd>The user can no longer interact with this application.
+     *   <dt>{@link Lifecycle.Event#ON_STOP}
+     *   <dd>The application is no longer visible.
+     *   <dt>{@link Lifecycle.Event#ON_DESTROY}
+     *   <dd>The OS has now destroyed this {@link Session} instance, and it is no longer
+     *       valid.
+     * </dl>
+     *
+     * <p>Listeners that are added in {@link Lifecycle.Event#ON_START}, should be removed in {@link
+     * Lifecycle.Event#ON_STOP}.
+     *
+     * <p>Listeners that are added in {@link Lifecycle.Event#ON_CREATE} should be removed in {@link
+     * Lifecycle.Event#ON_DESTROY}.
+     *
+     * <p>Note lifecycle callbacks will be executed on the main thread.
+     *
+     * @see androidx.lifecycle.LifecycleObserver
+     */
+    @NonNull
+    @Override
+    public Lifecycle getLifecycle() {
+        return mRegistry;
+    }
+
+    /**
+     * Returns the {@link CarContext} for this session.
+     *
+     * <p><b>The {@link CarContext} is not fully initialized until this session's {@link
+     * Lifecycle.State} is at least {@link Lifecycle.State#CREATED}</b>
+     *
+     * @see #getLifecycle
+     */
+    @NonNull
+    public final CarContext getCarContext() {
+        return mCarContext;
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/SurfaceListener.java b/car/app/app/src/main/java/androidx/car/app/SurfaceCallback.java
similarity index 96%
rename from car/app/app/src/main/java/androidx/car/app/SurfaceListener.java
rename to car/app/app/src/main/java/androidx/car/app/SurfaceCallback.java
index f59c8d6..ba8c853 100644
--- a/car/app/app/src/main/java/androidx/car/app/SurfaceListener.java
+++ b/car/app/app/src/main/java/androidx/car/app/SurfaceCallback.java
@@ -20,8 +20,8 @@
 
 import androidx.annotation.NonNull;
 
-/** A listener for changes on the {@link SurfaceContainer} and its attributes. */
-public interface SurfaceListener {
+/** A callback for changes on the {@link SurfaceContainer} and its attributes. */
+public interface SurfaceCallback {
     /**
      * Provides a {@link SurfaceContainer} from the host which is ready for drawing.
      *
diff --git a/car/app/app/src/main/java/androidx/car/app/annotations/RequiresCarApi.java b/car/app/app/src/main/java/androidx/car/app/annotations/RequiresCarApi.java
new file mode 100644
index 0000000..4201a81
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/annotations/RequiresCarApi.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.car.app.annotations;
+
+import androidx.car.app.CarContext;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines the minimum API level required to be able to use this model, field or method.
+ * <p>
+ * Before using any of this elements, application must check that
+ * {@link CarContext#getCarAppApiLevel()} is equal or greater than the value of this annotation.
+ * <p>
+ * For example, if an application wants to use a newer template "Foo" marked with
+ * <code>@RequiresHostApiLevel(2)</code> while maintain backwards compatibility with older hosts
+ * by using an older template "Bar" (<code>@RequiresHostApiLevel(1)</code>), they can do:
+ *
+ * <pre>
+ * if (getCarContext().getCarApiLevel() >= 2) {
+ *     // Use new feature
+ *     return Foo.Builder()....;
+ * } else {
+ *     // Use supported fallback
+ *     return Bar.Builder()....;
+ * }
+ * </pre>
+ *
+ * If a certain model or method has no {@link RequiresCarApi} annotation, it is assumed to
+ * be available in all car API levels.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
+public @interface RequiresCarApi {
+
+    /**
+     * The minimum API level required to be able to use this model, field or method. Applications
+     * shouldn't use any elements annotated with a {@link RequiresCarApi} greater than
+     * {@link CarContext#getCarAppApiLevel()}
+     */
+    int value();
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Action.java b/car/app/app/src/main/java/androidx/car/app/model/Action.java
index 34cb78d..073fa8b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Action.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Action.java
@@ -240,7 +240,7 @@
         this.mType = type;
     }
 
-    private Action(
+    Action(
             @Nullable CarText title,
             @Nullable CarIcon icon,
             CarColor backgroundColor,
@@ -285,21 +285,21 @@
                 && Objects.equals(mListener == null, otherAction.mListener == null);
     }
 
-    private static boolean isStandardActionType(@ActionType int type) {
+    static boolean isStandardActionType(@ActionType int type) {
         return 0 != (type & FLAG_STANDARD);
     }
 
     /** A builder of {@link Action}. */
     public static final class Builder {
         @Nullable
-        private CarText mTitle;
+        CarText mTitle;
         @Nullable
-        private CarIcon mIcon;
+        CarIcon mIcon;
         @Nullable
-        private OnClickListenerWrapper mListener;
-        private CarColor mBackgroundColor = DEFAULT;
+        OnClickListenerWrapper mListener;
+        CarColor mBackgroundColor = DEFAULT;
         @ActionType
-        private int mType = TYPE_CUSTOM;
+        int mType = TYPE_CUSTOM;
 
         /**
          * Sets the title to display in the action, or {@code null} to not display a title.
@@ -409,15 +409,15 @@
             return new Action(mTitle, mIcon, mBackgroundColor, mListener, mType);
         }
 
-        private Builder() {
+        Builder() {
         }
 
-        private Builder(Action action) {
-            mTitle = action.mTitle;
-            mIcon = action.mIcon;
-            mBackgroundColor = action.mBackgroundColor;
-            mListener = action.mListener;
-            mType = action.mType;
+        Builder(Action action) {
+            mTitle = action.getTitle();
+            mIcon = action.getIcon();
+            mBackgroundColor = action.getBackgroundColor();
+            mListener = action.getOnClickListener();
+            mType = action.getType();
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java b/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java
index ddd6ef2..866ed5e 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java
@@ -100,7 +100,7 @@
         return Objects.equals(mActions, otherActionStrip.mActions);
     }
 
-    private ActionStrip(Builder builder) {
+    ActionStrip(Builder builder) {
         mActions = builder.mActions;
     }
 
@@ -111,8 +111,8 @@
 
     /** A builder of {@link ActionStrip}. */
     public static final class Builder {
-        private final List<Object> mActions = new ArrayList<>();
-        private final Set<Integer> mAddedActionTypes = new HashSet<>();
+        final List<Object> mActions = new ArrayList<>();
+        final Set<Integer> mAddedActionTypes = new HashSet<>();
 
         /**
          * Adds an {@link Action} to the list.
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
index 5cf23ac..bec4761 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
@@ -342,7 +342,7 @@
         }
     }
 
-    private CarIcon(@Nullable IconCompat icon, @Nullable CarColor tint, @CarIconType int type) {
+    CarIcon(@Nullable IconCompat icon, @Nullable CarColor tint, @CarIconType int type) {
         this.mType = type;
         this.mIcon = icon;
         this.mTint = tint;
@@ -413,13 +413,13 @@
             return new CarIcon(mIcon, mTint, mType);
         }
 
-        private Builder(@NonNull IconCompat icon) {
+        Builder(@NonNull IconCompat icon) {
             mType = TYPE_CUSTOM;
             this.mIcon = icon;
             mTint = null;
         }
 
-        private Builder(@NonNull CarIcon carIcon) {
+        Builder(@NonNull CarIcon carIcon) {
             mType = carIcon.getType();
             mIcon = carIcon.getIcon();
             mTint = carIcon.getTint();
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java b/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java
index b9fdfb3..8691c2b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java
@@ -16,8 +16,6 @@
 
 package androidx.car.app.model;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-
 import static java.util.Objects.requireNonNull;
 
 import android.text.TextPaint;
@@ -27,8 +25,6 @@
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
 import androidx.car.app.model.constraints.CarColorConstraints;
 
 import java.util.Objects;
@@ -75,14 +71,6 @@
         return new ForegroundCarColorSpan(requireNonNull(carColor));
     }
 
-    /** @hide */
-    @RestrictTo(LIBRARY)
-    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
-    @NonNull
-    public static ForegroundCarColorSpan createForTesting(@NonNull CarColor carColor) {
-        return new ForegroundCarColorSpan(carColor);
-    }
-
     @Override
     public void updateDrawState(@NonNull TextPaint paint) {
         // Not relevant.
diff --git a/car/app/app/src/main/java/androidx/car/app/model/GridItem.java b/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
index 32e9482..7d117ae 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
@@ -28,6 +28,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.car.app.Screen;
 import androidx.car.app.model.constraints.CarIconConstraints;
 
 import java.lang.annotation.Retention;
@@ -37,8 +38,6 @@
 /**
  * Represents a grid item with an image and an optional title.
  */
-// TODO(shiufai): Support toggle state in a grid item.
-// TODO(shiufai): Make grid item browsable.
 public class GridItem implements Item {
     /**
      * The type of images supported within grid items.
@@ -55,7 +54,7 @@
     /**
      * Represents an icon to be displayed in the grid item.
      *
-     * <p>If necessary, icons will be scaled down to fit within a 44 x 44 dp bounding box,
+     * <p>If necessary, icons will be scaled down to fit within a 64 x 64 dp bounding box,
      * preserving
      * their aspect ratios.
      *
@@ -73,6 +72,8 @@
     public static final int IMAGE_TYPE_LARGE = (1 << 1);
 
     @Keep
+    private final boolean mIsLoading;
+    @Keep
     @Nullable
     private final CarText mTitle;
     @Keep
@@ -82,9 +83,6 @@
     @Nullable
     private final CarIcon mImage;
     @Keep
-    @Nullable
-    private final Toggle mToggle;
-    @Keep
     @GridItemImageType
     private final int mImageType;
     @Keep
@@ -97,10 +95,15 @@
         return new Builder();
     }
 
+    /** Returns whether the grid item is loading. */
+    public boolean isLoading() {
+        return mIsLoading;
+    }
+
     /** Returns the title of the grid item. */
-    @Nullable
+    @NonNull
     public CarText getTitle() {
-        return mTitle;
+        return requireNonNull(mTitle);
     }
 
     /** Returns the list of text below the title. */
@@ -110,9 +113,9 @@
     }
 
     /** Returns the image of the grid item. */
-    @NonNull
+    @Nullable
     public CarIcon getImage() {
-        return requireNonNull(mImage);
+        return mImage;
     }
 
     /** Returns the image type of the grid item. */
@@ -122,15 +125,6 @@
     }
 
     /**
-     * Returns the {@link Toggle} in the grid item or {@code null} if the grid item does not
-     * contain a toggle.
-     */
-    @Nullable
-    public Toggle getToggle() {
-        return mToggle;
-    }
-
-    /**
      * Returns the {@link OnClickListener} to be called back when the grid item is clicked, or
      * {@code null} if the grid item is non-clickable.
      */
@@ -148,12 +142,14 @@
                 + CarText.toShortString(mText)
                 + ", image: "
                 + mImage
+                + ", isLoading: "
+                + mIsLoading
                 + "]";
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mTitle, mImage, mImageType, mToggle, mOnClickListener == null);
+        return Objects.hash(mIsLoading, mTitle, mImage, mImageType, mOnClickListener == null);
     }
 
     @Override
@@ -166,52 +162,74 @@
         }
         GridItem otherGridItem = (GridItem) other;
 
-        return Objects.equals(mTitle, otherGridItem.mTitle)
+        return mIsLoading == otherGridItem.mIsLoading
+                && Objects.equals(mTitle, otherGridItem.mTitle)
                 && Objects.equals(mText, otherGridItem.mText)
                 && Objects.equals(mImage, otherGridItem.mImage)
-                && Objects.equals(mToggle, otherGridItem.mToggle)
                 && Objects.equals(mOnClickListener == null, otherGridItem.mOnClickListener == null)
                 && mImageType == otherGridItem.mImageType;
     }
 
-    private GridItem(Builder builder) {
+    GridItem(Builder builder) {
+        mIsLoading = builder.mIsLoading;
         mTitle = builder.mTitle;
         mText = builder.mText;
         mImage = builder.mImage;
         mImageType = builder.mImageType;
-        mToggle = builder.mToggle;
         mOnClickListener = builder.mOnClickListener;
     }
 
     /** Constructs an empty instance, used by serialization code. */
     private GridItem() {
+        mIsLoading = false;
         mTitle = null;
         mText = null;
         mImage = null;
         mImageType = IMAGE_TYPE_LARGE;
-        mToggle = null;
         mOnClickListener = null;
     }
 
     /** A builder of {@link GridItem}. */
     public static final class Builder {
         @Nullable
-        private CarText mTitle;
+        CarText mTitle;
         @Nullable
-        private CarText mText;
+        CarText mText;
         @Nullable
-        private CarIcon mImage;
+        CarIcon mImage;
         @GridItemImageType
-        private int mImageType = IMAGE_TYPE_LARGE;
+        int mImageType = IMAGE_TYPE_LARGE;
         @Nullable
-        private Toggle mToggle;
-        @Nullable
-        private OnClickListenerWrapper mOnClickListener;
+        OnClickListenerWrapper mOnClickListener;
+        boolean mIsLoading;
 
-        /** Sets the title of the grid item, or {@code null} to not show the title. */
+        /**
+         * Sets whether the item is in a loading state.
+         *
+         * <p>If set to {@code true}, the UI shows a loading indicator where the grid item would be
+         * otherwise. The caller is expected to call {@link Screen#invalidate()} and send
+         * the new template content to the host once the data is ready. If set to {@code false},
+         * the UI shows the item  contents.
+         */
         @NonNull
-        public Builder setTitle(@Nullable CharSequence title) {
-            this.mTitle = title == null ? null : CarText.create(title);
+        public Builder setLoading(boolean isLoading) {
+            this.mIsLoading = isLoading;
+            return this;
+        }
+
+        /**
+         * Sets the title of the row.
+         *
+         * @throws NullPointerException     if {@code title} is {@code null}.
+         * @throws IllegalArgumentException if {@code title} is empty.
+         */
+        @NonNull
+        public Builder setTitle(@NonNull CharSequence title) {
+            CarText titleText = CarText.create(requireNonNull(title));
+            if (titleText.isEmpty()) {
+                throw new IllegalArgumentException("The title cannot be null or empty");
+            }
+            this.mTitle = titleText;
             return this;
         }
 
@@ -221,9 +239,7 @@
          *
          * <h2>Text Wrapping</h2>
          *
-         * The string added with {@link #setText} is truncated at the end to fit in a single line
-         * below
-         * the title.
+         * This text is truncated at the end to fit in a single line below the title.
          */
         @NonNull
         public Builder setText(@Nullable CharSequence text) {
@@ -270,19 +286,6 @@
         }
 
         /**
-         * Sets a {@link Toggle} for the grid item, or {@code null} to not have any toggle states
-         * in the grid item. If set, this grid item acts as a toggle.
-         *
-         * <p>If the grid item has a {@link Toggle}, then no {@link OnClickListener} can be added
-         * to it.
-         */
-        @NonNull
-        public Builder setToggle(@Nullable Toggle toggle) {
-            this.mToggle = toggle;
-            return this;
-        }
-
-        /**
          * Sets the {@link OnClickListener} to be called back when the grid item is clicked, or
          * {@code null} to make the grid item non-clickable.
          *
@@ -303,32 +306,31 @@
         /**
          * Constructs the {@link GridItem} defined by this builder.
          *
-         * @throws IllegalStateException if the grid item's image is not set.
-         * @throws IllegalStateException if the grid item doesn't have a title but the text is set.
-         * @throws IllegalStateException if the grid item has both a {@link OnClickListener} and a
-         *                               {@link Toggle}.
+         * @throws IllegalStateException if the grid item's title is not set.
+         * @throws IllegalStateException if the grid item's image is set when it is loading and vice
+         *                               versa.
+         * @throws IllegalStateException if the grid item is loading but the click listener is set.
          */
         @NonNull
         public GridItem build() {
-            if (mImage == null) {
-                throw new IllegalStateException("An image must be set on the grid item");
+            if (mTitle == null) {
+                throw new IllegalStateException("A title must be set on the grid item");
             }
 
-            if (mTitle == null && mText != null) {
+            if (mIsLoading == (mImage != null)) {
                 throw new IllegalStateException(
-                        "If a grid item doesn't have a title, it must not have a text set");
+                        "When a grid item is loading, the image must not be set and vice versa");
             }
 
-            if (mToggle != null && mOnClickListener != null) {
+            if (mIsLoading && mOnClickListener != null) {
                 throw new IllegalStateException(
-                        "If a grid item contains a toggle, it must not have a onClickListener set"
-                                + " and vice versa");
+                        "The click listener must not be set on the grid item when it is loading");
             }
 
             return new GridItem(this);
         }
 
-        private Builder() {
+        Builder() {
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
index 199d19d..19ce5a2 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
@@ -41,10 +41,7 @@
  * <ul>
  *   <li>The template title has not changed, and
  *   <li>The previous template is in a loading state (see {@link Builder#setLoading}, or the
- *       number of grid items and the string contents (title, texts) of each grid item have not
- *       changed.
- *   <li>For grid items that contain a {@link Toggle}, updates to the title, text and image are also
- *       allowed if the toggle state has changed between the previous and new templates.
+ *       number of grid items and the title of each grid item have not changed.
  * </ul>
  */
 public final class GridTemplate implements Template {
@@ -131,7 +128,7 @@
                 && Objects.equals(mBackgroundImage, otherTemplate.mBackgroundImage);
     }
 
-    private GridTemplate(Builder builder) {
+    GridTemplate(Builder builder) {
         mIsLoading = builder.mIsLoading;
         mTitle = builder.mTitle;
         mHeaderAction = builder.mHeaderAction;
@@ -152,19 +149,19 @@
 
     /** A builder of {@link GridTemplate}. */
     public static final class Builder {
-        private boolean mIsLoading;
+        boolean mIsLoading;
         @Nullable
-        private ItemList mSingleList;
+        ItemList mSingleList;
         @Nullable
-        private CarText mTitle;
+        CarText mTitle;
         @Nullable
-        private Action mHeaderAction;
+        Action mHeaderAction;
         @Nullable
-        private ActionStrip mActionStrip;
+        ActionStrip mActionStrip;
 
         /** For internal, host-side use only. */
         @Nullable
-        private CarIcon mBackgroundImage;
+        CarIcon mBackgroundImage;
 
         /**
          * Sets whether the template is in a loading state.
@@ -305,7 +302,7 @@
             return new GridTemplate(this);
         }
 
-        private Builder() {
+        Builder() {
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ItemList.java b/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
index 367931b..a2a7fb0 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
@@ -26,11 +26,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.car.app.IOnDoneCallback;
-import androidx.car.app.IOnItemVisibilityChangedListener;
-import androidx.car.app.IOnSelectedListener;
 import androidx.car.app.OnDoneCallback;
-import androidx.car.app.OnItemVisibilityChangedListenerWrapper;
-import androidx.car.app.OnSelectedListenerWrapper;
 import androidx.car.app.WrappedRuntimeException;
 import androidx.car.app.utils.RemoteUtils;
 
@@ -174,7 +170,7 @@
                 && Objects.equals(mNoItemsMessage, otherList.mNoItemsMessage);
     }
 
-    private ItemList(Builder builder) {
+    ItemList(Builder builder) {
         mSelectedIndex = builder.mSelectedIndex;
         mItems = new ArrayList<>(builder.mItems);
         mNoItemsMessage = builder.mNoItemsMessage;
@@ -193,14 +189,14 @@
 
     /** A builder of {@link ItemList}. */
     public static final class Builder {
-        private final List<Object> mItems = new ArrayList<>();
-        private int mSelectedIndex;
+        final List<Object> mItems = new ArrayList<>();
+        int mSelectedIndex;
         @Nullable
-        private OnSelectedListenerWrapper mOnSelectedListener;
+        OnSelectedListenerWrapper mOnSelectedListener;
         @Nullable
-        private OnItemVisibilityChangedListenerWrapper mOnItemVisibilityChangedListener;
+        OnItemVisibilityChangedListenerWrapper mOnItemVisibilityChangedListener;
         @Nullable
-        private CarText mNoItemsMessage;
+        CarText mNoItemsMessage;
 
         /**
          * Sets the {@link OnItemVisibilityChangedListener} to call when the visible items in the
@@ -237,7 +233,7 @@
          * @see #setSelectedIndex(int)
          */
         @NonNull
-        @SuppressLint({"ExecutorRegistration"})
+        @SuppressLint("ExecutorRegistration")
         public Builder setOnSelectedListener(@Nullable OnSelectedListener onSelectedListener) {
             this.mOnSelectedListener =
                     onSelectedListener == null ? null : createOnSelectedListener(
@@ -338,7 +334,7 @@
     }
 
     @Nullable
-    private static OnClickListenerWrapper getOnClickListener(Object item) {
+    static OnClickListenerWrapper getOnClickListener(Object item) {
         if (item instanceof Row) {
             return ((Row) item).getOnClickListener();
         } else if (item instanceof GridItem) {
@@ -349,17 +345,15 @@
     }
 
     @Nullable
-    private static Toggle getToggle(Object item) {
+    static Toggle getToggle(Object item) {
         if (item instanceof Row) {
             return ((Row) item).getToggle();
-        } else if (item instanceof GridItem) {
-            return ((GridItem) item).getToggle();
         }
 
         return null;
     }
 
-    private static OnSelectedListenerWrapper createOnSelectedListener(
+    static OnSelectedListenerWrapper createOnSelectedListener(
             @NonNull OnSelectedListener listener) {
         return new OnSelectedListenerWrapper() {
             private final IOnSelectedListener mStubListener = new OnSelectedListenerStub(listener);
@@ -380,7 +374,7 @@
     private static class OnSelectedListenerStub extends IOnSelectedListener.Stub {
         private final OnSelectedListener mOnSelectedListener;
 
-        private OnSelectedListenerStub(OnSelectedListener onSelectedListener) {
+        OnSelectedListenerStub(OnSelectedListener onSelectedListener) {
             this.mOnSelectedListener = onSelectedListener;
         }
 
@@ -391,7 +385,7 @@
         }
     }
 
-    private static OnItemVisibilityChangedListenerWrapper createOnItemVisibilityChangedListener(
+    static OnItemVisibilityChangedListenerWrapper createOnItemVisibilityChangedListener(
             @NonNull OnItemVisibilityChangedListener listener) {
         return new OnItemVisibilityChangedListenerWrapper() {
             private final IOnItemVisibilityChangedListener mStubListener =
@@ -416,7 +410,7 @@
             extends IOnItemVisibilityChangedListener.Stub {
         private final OnItemVisibilityChangedListener mOnItemVisibilityChangedListener;
 
-        private OnItemVisibilityChangedListenerStub(
+        OnItemVisibilityChangedListenerStub(
                 OnItemVisibilityChangedListener onItemVisibilityChangedListener) {
             this.mOnItemVisibilityChangedListener = onItemVisibilityChangedListener;
         }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java
index dda936c..5d5d5b9 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java
@@ -16,7 +16,6 @@
 
 package androidx.car.app.model;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.car.app.model.constraints.ActionsConstraints.ACTIONS_CONSTRAINTS_HEADER;
 import static androidx.car.app.model.constraints.ActionsConstraints.ACTIONS_CONSTRAINTS_SIMPLE;
 import static androidx.car.app.model.constraints.RowListConstraints.ROW_LIST_CONSTRAINTS_FULL_LIST;
@@ -28,7 +27,6 @@
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
 import androidx.car.app.Screen;
 
 import java.util.ArrayList;
@@ -138,7 +136,7 @@
                 && Objects.equals(mActionStrip, otherTemplate.mActionStrip);
     }
 
-    private ListTemplate(Builder builder) {
+    ListTemplate(Builder builder) {
         mIsLoading = builder.mIsLoading;
         mTitle = builder.mTitle;
         mHeaderAction = builder.mHeaderAction;
@@ -159,17 +157,17 @@
 
     /** A builder of {@link ListTemplate}. */
     public static final class Builder {
-        private boolean mIsLoading;
+        boolean mIsLoading;
         @Nullable
-        private ItemList mSingleList;
-        private final List<SectionedItemList> mSectionLists = new ArrayList<>();
+        ItemList mSingleList;
+        final List<SectionedItemList> mSectionLists = new ArrayList<>();
         @Nullable
-        private CarText mTitle;
+        CarText mTitle;
         @Nullable
-        private Action mHeaderAction;
+        Action mHeaderAction;
         @Nullable
-        private ActionStrip mActionStrip;
-        private boolean mHasSelectableList;
+        ActionStrip mActionStrip;
+        boolean mHasSelectableList;
 
         /**
          * Sets whether the template is in a loading state.
@@ -190,14 +188,12 @@
 
         /**
          * Sets the {@link Action} that will be displayed in the header of the template, or
-         * {@code null}
-         * to not display an action.
+         * {@code null} to not display an action.
          *
          * <h4>Requirements</h4>
          *
          * This template only supports either one of {@link Action#APP_ICON} and
-         * {@link Action#BACK} as
-         * a header {@link Action}.
+         * {@link Action#BACK} as a header {@link Action}.
          *
          * @throws IllegalArgumentException if {@code headerAction} does not meet the template's
          *                                  requirements.
@@ -213,8 +209,7 @@
 
         /**
          * Sets the {@link CharSequence} to show as the template's title, or {@code null} to not
-         * show a
-         * title.
+         * show a title.
          */
         @NonNull
         public Builder setTitle(@Nullable CharSequence title) {
@@ -226,8 +221,7 @@
          * Sets a single {@link ItemList} to show in the template.
          *
          * <p>Note that this list cannot be mixed with others added via {@link #addList}. If
-         * multiple
-         * lists were previously added, they will be cleared.
+         * multiple lists were previously added, they will be cleared.
          *
          * @throws NullPointerException if {@code list} is null.
          * @see #addList(ItemList, CharSequence)
@@ -244,12 +238,9 @@
          * Adds an {@link ItemList} to display in the template.
          *
          * <p>Use this method to add multiple {@link ItemList}s to the template. Each
-         * {@link ItemList}
-         * will be grouped under the given {@code header}. These lists cannot be mixed with an
-         * {@link
-         * ItemList} added via {@link #setSingleList}. If a single list was previously added, it
-         * will be
-         * cleared.
+         * {@link ItemList} will be grouped under the given {@code header}. These lists cannot be
+         * mixed with an {@link ItemList} added via {@link #setSingleList}. If a single list was
+         * previously added, it will be cleared.
          *
          * <p>If the added {@link ItemList} contains a {@link ItemList.OnSelectedListener}, then it
          * cannot be added alongside other {@link ItemList}(s).
@@ -292,15 +283,6 @@
             return this;
         }
 
-        /** @hide */
-        @RestrictTo(LIBRARY)
-        @NonNull
-        public Builder addListForTesting(@NonNull ItemList list, @NonNull CharSequence header) {
-            mSingleList = null;
-            mSectionLists.add(SectionedItemList.create(list, CarText.create(header)));
-            return this;
-        }
-
         /**
          * Sets the {@link ActionStrip} for this template, or {@code null} to not display an {@link
          * ActionStrip}.
@@ -362,14 +344,7 @@
             return new ListTemplate(this);
         }
 
-        /** @hide */
-        @RestrictTo(LIBRARY)
-        @NonNull
-        public ListTemplate buildForTesting() {
-            return new ListTemplate(this);
-        }
-
-        private Builder() {
+        Builder() {
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
index 630ead2..12d16f6 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
@@ -93,7 +93,7 @@
     }
 
     @Nullable
-    public ActionList getActionList() {
+    public ActionList getActions() {
         return mActionList;
     }
 
@@ -126,7 +126,7 @@
                 && Objects.equals(mIcon, otherTemplate.mIcon);
     }
 
-    private MessageTemplate(Builder builder) {
+    MessageTemplate(Builder builder) {
         mTitle = builder.mTitle;
         mMessage = builder.mMessage;
         mDebugMessage = builder.mDebugMessage;
@@ -148,20 +148,20 @@
     /** A builder of {@link MessageTemplate}. */
     public static final class Builder {
         @Nullable
-        private CarText mTitle;
-        private CarText mMessage;
+        CarText mTitle;
+        CarText mMessage;
         @Nullable
-        private CarText mDebugMessage;
+        CarText mDebugMessage;
         @Nullable
-        private CarIcon mIcon;
+        CarIcon mIcon;
         @Nullable
-        private Action mHeaderAction;
+        Action mHeaderAction;
         @Nullable
-        private ActionList mActionList;
+        ActionList mActionList;
         @Nullable
-        private Throwable mDebugCause;
+        Throwable mDebugCause;
         @Nullable
-        private String mDebugString;
+        String mDebugString;
 
         /**
          * Sets the {@link CharSequence} to show as the template's title, or {@code null} to not
@@ -273,8 +273,6 @@
          * @throws NullPointerException if {@code actions} is {@code null}.
          */
         @NonNull
-        // TODO(shiufai): consider rename to match getter's name (e.g. setActionList or getActions).
-        @SuppressLint("MissingGetterMatchingBuilder")
         public Builder setActions(@NonNull List<Action> actions) {
             mActionList = ActionList.create(requireNonNull(actions));
             return this;
@@ -317,7 +315,7 @@
             return new MessageTemplate(this);
         }
 
-        private Builder(CharSequence message) {
+        Builder(CharSequence message) {
             this.mMessage = CarText.create(message);
         }
     }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Metadata.java b/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
index 429aab4..e2c1eed 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
@@ -79,7 +79,7 @@
         return Objects.equals(mPlace, otherMetadata.mPlace);
     }
 
-    private Metadata(Builder builder) {
+    Metadata(Builder builder) {
         mPlace = builder.mPlace;
     }
 
@@ -91,7 +91,7 @@
     /** A builder for {@link Metadata}. */
     public static final class Builder {
         @Nullable
-        private Place mPlace;
+        Place mPlace;
 
         /**
          * Sets a {@link Place} used for showing {@link Distance} and {@link PlaceMarker}
@@ -112,11 +112,11 @@
             return new Metadata(this);
         }
 
-        private Builder() {
+        Builder() {
         }
 
-        private Builder(Metadata metadata) {
-            this.mPlace = metadata.mPlace;
+        Builder(Metadata metadata) {
+            this.mPlace = metadata.getPlace();
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/OnCheckedChangeListenerWrapper.java b/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeListenerWrapper.java
similarity index 93%
rename from car/app/app/src/main/java/androidx/car/app/OnCheckedChangeListenerWrapper.java
rename to car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeListenerWrapper.java
index 4aaca10..603323c 100644
--- a/car/app/app/src/main/java/androidx/car/app/OnCheckedChangeListenerWrapper.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeListenerWrapper.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.car.app.model;
 
 import androidx.annotation.NonNull;
+import androidx.car.app.OnDoneCallback;
 
 /**
  * A host-side interface for reporting to clients that the checked state has changed.
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnClickListenerWrapperImpl.java b/car/app/app/src/main/java/androidx/car/app/model/OnClickListenerWrapperImpl.java
index be60b6f..4a6505c 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnClickListenerWrapperImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnClickListenerWrapperImpl.java
@@ -89,7 +89,7 @@
     private static class OnClickListenerStub extends IOnClickListener.Stub {
         private final OnClickListener mOnClickListener;
 
-        private OnClickListenerStub(OnClickListener onClickListener) {
+        OnClickListenerStub(OnClickListener onClickListener) {
             this.mOnClickListener = onClickListener;
         }
 
diff --git a/car/app/app/src/main/java/androidx/car/app/OnItemVisibilityChangedListenerWrapper.java b/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedListenerWrapper.java
similarity index 95%
rename from car/app/app/src/main/java/androidx/car/app/OnItemVisibilityChangedListenerWrapper.java
rename to car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedListenerWrapper.java
index 5d7175f6..62e3e5b 100644
--- a/car/app/app/src/main/java/androidx/car/app/OnItemVisibilityChangedListenerWrapper.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedListenerWrapper.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.car.app.model;
 
 import androidx.annotation.NonNull;
+import androidx.car.app.OnDoneCallback;
 
 /**
  * A host-side interface for reporting to clients that the visibility state has changed.
diff --git a/car/app/app/src/main/java/androidx/car/app/OnSelectedListenerWrapper.java b/car/app/app/src/main/java/androidx/car/app/model/OnSelectedListenerWrapper.java
similarity index 94%
rename from car/app/app/src/main/java/androidx/car/app/OnSelectedListenerWrapper.java
rename to car/app/app/src/main/java/androidx/car/app/model/OnSelectedListenerWrapper.java
index 812d211..92e3006 100644
--- a/car/app/app/src/main/java/androidx/car/app/OnSelectedListenerWrapper.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnSelectedListenerWrapper.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.car.app.model;
 
 import androidx.annotation.NonNull;
+import androidx.car.app.OnDoneCallback;
 
 /**
  * A host-side interface for reporting to clients that an item was selected.
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Pane.java b/car/app/app/src/main/java/androidx/car/app/model/Pane.java
index 513cac3..f356894 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Pane.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Pane.java
@@ -18,8 +18,6 @@
 
 import static java.util.Objects.requireNonNull;
 
-import android.annotation.SuppressLint;
-
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -52,7 +50,7 @@
      * Returns the list of {@link Action}s displayed alongside the {@link Row}s in this pane.
      */
     @Nullable
-    public ActionList getActionList() {
+    public ActionList getActions() {
         return mActionList;
     }
 
@@ -101,7 +99,7 @@
                 && Objects.equals(mRows, otherPane.mRows);
     }
 
-    private Pane(Builder builder) {
+    Pane(Builder builder) {
         mRows = new ArrayList<>(builder.mRows);
         mActionList = builder.mActionList;
         mIsLoading = builder.mIsLoading;
@@ -116,10 +114,10 @@
 
     /** A builder of {@link Pane}. */
     public static final class Builder {
-        private final List<Object> mRows = new ArrayList<>();
+        final List<Object> mRows = new ArrayList<>();
         @Nullable
-        private ActionList mActionList;
-        private boolean mIsLoading;
+        ActionList mActionList;
+        boolean mIsLoading;
 
         /**
          * Sets whether the {@link Pane} is in a loading state.
@@ -157,8 +155,6 @@
          * @throws NullPointerException if {@code actions} is {@code null}.
          */
         @NonNull
-        // TODO(shiufai): consider rename to match getter's name (e.g. setActionList or getActions).
-        @SuppressLint("MissingGetterMatchingBuilder")
         public Builder setActions(@NonNull List<Action> actions) {
             mActionList = ActionList.create(requireNonNull(actions));
             return this;
diff --git a/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java
index c2e8a4b..64f0338 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java
@@ -16,7 +16,6 @@
 
 package androidx.car.app.model;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.car.app.model.constraints.ActionsConstraints.ACTIONS_CONSTRAINTS_HEADER;
 import static androidx.car.app.model.constraints.ActionsConstraints.ACTIONS_CONSTRAINTS_SIMPLE;
 import static androidx.car.app.model.constraints.RowListConstraints.ROW_LIST_CONSTRAINTS_PANE;
@@ -26,7 +25,6 @@
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
 
 import java.util.Collections;
 import java.util.Objects;
@@ -118,7 +116,7 @@
                 && Objects.equals(mActionStrip, otherTemplate.mActionStrip);
     }
 
-    private PaneTemplate(Builder builder) {
+    PaneTemplate(Builder builder) {
         mTitle = builder.mTitle;
         mPane = builder.mPane;
         mHeaderAction = builder.mHeaderAction;
@@ -136,14 +134,14 @@
     /** A builder of {@link PaneTemplate}. */
     public static final class Builder {
         @Nullable
-        private CarText mTitle;
-        private Pane mPane;
+        CarText mTitle;
+        Pane mPane;
         @Nullable
-        private Action mHeaderAction;
+        Action mHeaderAction;
         @Nullable
-        private ActionStrip mActionStrip;
+        ActionStrip mActionStrip;
 
-        private Builder(Pane pane) {
+        Builder(Pane pane) {
             this.mPane = pane;
         }
 
@@ -240,12 +238,5 @@
 
             return new PaneTemplate(this);
         }
-
-        /** @hide */
-        @RestrictTo(LIBRARY)
-        @NonNull
-        public PaneTemplate buildForTesting() {
-            return new PaneTemplate(this);
-        }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Place.java b/car/app/app/src/main/java/androidx/car/app/model/Place.java
index cdef8bd..5caf712 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Place.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Place.java
@@ -85,7 +85,7 @@
                 otherPlace.mMarker);
     }
 
-    private Place(Builder builder) {
+    Place(Builder builder) {
         mLatLng = builder.mLatLng;
         mMarker = builder.mMarker;
     }
@@ -98,17 +98,17 @@
 
     /** A builder of {@link Place}. */
     public static final class Builder {
-        private LatLng mLatLng;
+        LatLng mLatLng;
         @Nullable
-        private PlaceMarker mMarker;
+        PlaceMarker mMarker;
 
-        private Builder(LatLng latLng) {
+        Builder(LatLng latLng) {
             this.mLatLng = latLng;
         }
 
-        private Builder(Place place) {
-            mLatLng = requireNonNull(place.mLatLng);
-            mMarker = place.mMarker;
+        Builder(Place place) {
+            mLatLng = place.getLatLng();
+            mMarker = place.getMarker();
         }
 
         /**
diff --git a/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java
index ada9d74..02f3360 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java
@@ -16,7 +16,6 @@
 
 package androidx.car.app.model;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.car.app.model.constraints.ActionsConstraints.ACTIONS_CONSTRAINTS_HEADER;
 import static androidx.car.app.model.constraints.ActionsConstraints.ACTIONS_CONSTRAINTS_SIMPLE;
 import static androidx.car.app.model.constraints.RowListConstraints.ROW_LIST_CONSTRAINTS_SIMPLE;
@@ -27,7 +26,6 @@
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
 import androidx.car.app.CarAppPermission;
 
 import java.util.Collections;
@@ -152,7 +150,7 @@
                 && Objects.equals(mAnchor, otherTemplate.mAnchor);
     }
 
-    private PlaceListMapTemplate(Builder builder) {
+    PlaceListMapTemplate(Builder builder) {
         mShowCurrentLocation = builder.mShowCurrentLocation;
         mIsLoading = builder.mIsLoading;
         mTitle = builder.mTitle;
@@ -175,18 +173,18 @@
 
     /** A builder of {@link PlaceListMapTemplate}. */
     public static final class Builder {
-        private boolean mShowCurrentLocation;
-        private boolean mIsLoading;
+        boolean mShowCurrentLocation;
+        boolean mIsLoading;
         @Nullable
-        private CarText mTitle;
+        CarText mTitle;
         @Nullable
-        private ItemList mItemList;
+        ItemList mItemList;
         @Nullable
-        private Action mHeaderAction;
+        Action mHeaderAction;
         @Nullable
-        private ActionStrip mActionStrip;
+        ActionStrip mActionStrip;
         @Nullable
-        private Place mAnchor;
+        Place mAnchor;
 
         /**
          * Sets whether to show the current location in the map.
@@ -220,8 +218,7 @@
 
         /**
          * Sets the {@link Action} that will be displayed in the header of the template, or
-         * {@code null}
-         * to not display an action.
+         * {@code null} to not display an action.
          *
          * <h4>Requirements</h4>
          *
@@ -242,8 +239,7 @@
 
         /**
          * Sets the {@link CharSequence} to show as the template's title, or {@code null} to not
-         * display
-         * a title.
+         * display a title.
          */
         @NonNull
         public Builder setTitle(@Nullable CharSequence title) {
@@ -292,14 +288,6 @@
             return this;
         }
 
-        /** @hide */
-        @RestrictTo(LIBRARY)
-        @NonNull
-        public Builder setItemListForTesting(@Nullable ItemList itemList) {
-            this.mItemList = itemList;
-            return this;
-        }
-
         /**
          * Sets the {@link ActionStrip} for this template, or {@code null} to not display an {@link
          * ActionStrip}.
@@ -308,8 +296,7 @@
          *
          * This template allows up to 2 {@link Action}s in its {@link ActionStrip}. Of the 2 allowed
          * {@link Action}s, one of them can contain a title as set via
-         * {@link Action.Builder#setTitle}.
-         * Otherwise, only {@link Action}s with icons are allowed.
+         * {@link Action.Builder#setTitle}. Otherwise, only {@link Action}s with icons are allowed.
          *
          * @throws IllegalArgumentException if {@code actionStrip} does not meet the template's
          *                                  requirements.
@@ -328,13 +315,11 @@
          * <p>The anchor marker is displayed differently from other markers by the host.
          *
          * <p>If not {@code null}, an anchor marker will be shown at the specified {@link LatLng}
-         * on the
-         * map. The camera will adapt to always have the anchor marker visible within its viewport,
-         * along with other places' markers from {@link Row} that are currently visible in the
-         * {@link
-         * Pane}. This can be used to provide a reference point on the map (e.g. the center of a
-         * search
-         * region) as the user pages through the {@link Pane}'s markers, for example.
+         * on the map. The camera will adapt to always have the anchor marker visible within its
+         * viewport, along with other places' markers from {@link Row} that are currently visible
+         * in the {@link Pane}. This can be used to provide a reference point on the map (e.g.
+         * the center of a search region) as the user pages through the {@link Pane}'s markers,
+         * for example.
          */
         @NonNull
         public Builder setAnchor(@Nullable Place anchor) {
@@ -350,8 +335,7 @@
          * Either a header {@link Action} or title must be set on the template.
          *
          * @throws IllegalArgumentException if the template is in a loading state but the list is
-         *                                  set,
-         *                                  or vice versa.
+         *                                  set, or vice versa.
          * @throws IllegalStateException    if the template does not have either a title or header
          *                                  {@link Action} set.
          */
diff --git a/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java b/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java
index 173ea25..7ea38c3 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java
@@ -166,7 +166,7 @@
                 && mIconType == otherMarker.mIconType;
     }
 
-    private PlaceMarker(@NonNull Builder builder) {
+    PlaceMarker(@NonNull Builder builder) {
         mIcon = builder.mIcon;
         mIconType = builder.mIconType;
         mLabel = builder.mLabel;
@@ -184,13 +184,13 @@
     /** A builder of {@link PlaceMarker}. */
     public static final class Builder {
         @Nullable
-        private CarIcon mIcon;
+        CarIcon mIcon;
         @Nullable
-        private CarText mLabel;
+        CarText mLabel;
         @Nullable
-        private CarColor mColor;
+        CarColor mColor;
         @MarkerIconType
-        private int mIconType = TYPE_ICON;
+        int mIconType = TYPE_ICON;
 
         /**
          * Sets the icon to display in the marker, or {@code null} to not display one.
@@ -290,7 +290,7 @@
             return new PlaceMarker(this);
         }
 
-        private Builder() {
+        Builder() {
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Row.java b/car/app/app/src/main/java/androidx/car/app/model/Row.java
index 1099af0..683f69c 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Row.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Row.java
@@ -237,7 +237,7 @@
                 && mRowImageType == otherRow.mRowImageType;
     }
 
-    private Row(Builder builder) {
+    Row(Builder builder) {
         mTitle = builder.mTitle;
         mTexts = new ArrayList<>(builder.mTexts);
         mImage = builder.mImage;
@@ -263,18 +263,18 @@
     /** A builder of {@link Row}. */
     public static final class Builder {
         @Nullable
-        private CarText mTitle;
-        private final List<CarText> mTexts = new ArrayList<>();
+        CarText mTitle;
+        final List<CarText> mTexts = new ArrayList<>();
         @Nullable
-        private CarIcon mImage;
+        CarIcon mImage;
         @Nullable
-        private Toggle mToggle;
+        Toggle mToggle;
         @Nullable
-        private OnClickListenerWrapper mOnClickListener;
-        private Metadata mMetadata = EMPTY_METADATA;
-        private boolean mIsBrowsable;
+        OnClickListenerWrapper mOnClickListener;
+        Metadata mMetadata = EMPTY_METADATA;
+        boolean mIsBrowsable;
         @RowImageType
-        private int mRowImageType = IMAGE_TYPE_SMALL;
+        int mRowImageType = IMAGE_TYPE_SMALL;
 
         /**
          * Sets the title of the row.
@@ -302,7 +302,7 @@
          *
          * <h4>Text Wrapping</h4>
          *
-         * Each string added with {@link #addText} will not wrap more than 1 line in the UI, with
+         * Each string added with this method will not wrap more than 1 line in the UI, with
          * one exception: if the template allows a maximum number of text strings larger than 1, and
          * the app adds a single text string, then this string will wrap up to the maximum.
          *
@@ -491,7 +491,7 @@
             return new Row(this);
         }
 
-        private Builder() {
+        Builder() {
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/SearchListenerWrapper.java b/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackWrapper.java
similarity index 92%
rename from car/app/app/src/main/java/androidx/car/app/SearchListenerWrapper.java
rename to car/app/app/src/main/java/androidx/car/app/model/SearchCallbackWrapper.java
index 050ac89..c405946 100644
--- a/car/app/app/src/main/java/androidx/car/app/SearchListenerWrapper.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackWrapper.java
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.car.app.model;
 
 import androidx.annotation.NonNull;
+import androidx.car.app.OnDoneCallback;
 
 /**
  * A host-side interface for reporting to search updates to clients.
  */
-public interface SearchListenerWrapper {
+public interface SearchCallbackWrapper {
     /**
      * Notifies that the search text has changed.
      *
diff --git a/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java
index 18976ce..eadf70b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java
@@ -28,11 +28,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.car.app.IOnDoneCallback;
-import androidx.car.app.ISearchListener;
 import androidx.car.app.OnDoneCallback;
 import androidx.car.app.Screen;
-import androidx.car.app.SearchListener;
-import androidx.car.app.SearchListenerWrapper;
 import androidx.car.app.WrappedRuntimeException;
 import androidx.car.app.utils.RemoteUtils;
 
@@ -49,10 +46,33 @@
  * results as the user types without the templates being counted against the quota.
  */
 public final class SearchTemplate implements Template {
+
+    /** A listener for search updates. */
+    public interface SearchCallback {
+        /**
+         * Notifies the current {@code searchText}.
+         *
+         * <p>The host may invoke this callback as the user types a search text. The frequency of
+         * these updates is not guaranteed to be after every individual keystroke. The host may
+         * decide to wait for several keystrokes before sending a single update.
+         *
+         * @param searchText the current search text that the user has typed.
+         */
+        void onSearchTextChanged(@NonNull String searchText);
+
+        /**
+         * Notifies that the user has submitted the search and the given {@code searchText} is
+         * the final term.
+         *
+         * @param searchText the search text that the user typed.
+         */
+        void onSearchSubmitted(@NonNull String searchText);
+    }
+
     @Keep
     private final boolean mIsLoading;
     @Keep
-    private final SearchListenerWrapper mSearchListener;
+    private final SearchCallbackWrapper mSearchCallback;
     @Keep
     @Nullable
     private final String mInitialSearchText;
@@ -72,18 +92,18 @@
     private final ActionStrip mActionStrip;
 
     /**
-     * Constructs a new builder of {@link SearchTemplate} with the input {@link SearchListener}.
+     * Constructs a new builder of {@link SearchTemplate} with the input {@link SearchCallback}.
      *
-     * <p>Note that the listener relates to UI events and will be executed on the main thread
+     * <p>Note that the callback relates to UI events and will be executed on the main thread
      * using {@link Looper#getMainLooper()}.
      *
-     * @param listener the listener to be invoked for events such as when the user types new
+     * @param callback the callback to be invoked for events such as when the user types new
      *                 text, or submits a search.
      */
     @NonNull
     @SuppressLint("ExecutorRegistration")
-    public static Builder builder(@NonNull SearchListener listener) {
-        return new Builder(listener);
+    public static Builder builder(@NonNull SearchCallback callback) {
+        return new Builder(callback);
     }
 
     public boolean isLoading() {
@@ -134,11 +154,11 @@
     }
 
     /**
-     * Returns the {@link SearchListenerWrapper} for search callbacks.
+     * Returns the {@link SearchCallbackWrapper} for search callbacks.
      */
     @NonNull
-    public SearchListenerWrapper getSearchListener() {
-        return mSearchListener;
+    public SearchCallbackWrapper getSearchCallback() {
+        return mSearchCallback;
     }
 
     /**
@@ -188,12 +208,12 @@
                 && mShowKeyboardByDefault == otherTemplate.mShowKeyboardByDefault;
     }
 
-    private SearchTemplate(Builder builder) {
+    SearchTemplate(Builder builder) {
         mInitialSearchText = builder.mInitialSearchText;
         mSearchHint = builder.mSearchHint;
         mIsLoading = builder.mIsLoading;
         mItemList = builder.mItemList;
-        mSearchListener = builder.mSearchListener;
+        mSearchCallback = builder.mSearchCallback;
         mShowKeyboardByDefault = builder.mShowKeyboardByDefault;
         mHeaderAction = builder.mHeaderAction;
         mActionStrip = builder.mActionStrip;
@@ -207,8 +227,8 @@
         mItemList = null;
         mHeaderAction = null;
         mActionStrip = null;
-        mSearchListener = createSearchListener(
-                new SearchListener() {
+        mSearchCallback = createSearchCallback(
+                new SearchCallback() {
                     @Override
                     public void onSearchTextChanged(@NonNull String searchText) {
                     }
@@ -222,22 +242,22 @@
 
     /** A builder of {@link SearchTemplate}. */
     public static final class Builder {
-        private final SearchListenerWrapper mSearchListener;
+        final SearchCallbackWrapper mSearchCallback;
         @Nullable
-        private String mInitialSearchText;
+        String mInitialSearchText;
         @Nullable
-        private String mSearchHint;
-        private boolean mIsLoading;
+        String mSearchHint;
+        boolean mIsLoading;
         @Nullable
-        private ItemList mItemList;
-        private boolean mShowKeyboardByDefault = true;
+        ItemList mItemList;
+        boolean mShowKeyboardByDefault = true;
         @Nullable
-        private Action mHeaderAction;
+        Action mHeaderAction;
         @Nullable
-        private ActionStrip mActionStrip;
+        ActionStrip mActionStrip;
 
-        private Builder(SearchListener listener) {
-            mSearchListener = createSearchListener(listener);
+        Builder(SearchCallback callback) {
+            mSearchCallback = createSearchCallback(callback);
         }
 
         /**
@@ -384,15 +404,15 @@
         }
     }
 
-    private static SearchListenerWrapper createSearchListener(@NonNull SearchListener listener) {
-        return new SearchListenerWrapper() {
-            private final ISearchListener mStubListener = new SearchListenerStub(listener);
+    static SearchCallbackWrapper createSearchCallback(@NonNull SearchCallback callback) {
+        return new SearchCallbackWrapper() {
+            private final ISearchCallback mStubCallback = new SearchCallbackStub(callback);
 
             @Override
             public void onSearchTextChanged(@NonNull String searchText,
                     @NonNull OnDoneCallback callback) {
                 try {
-                    mStubListener.onSearchTextChanged(searchText,
+                    mStubCallback.onSearchTextChanged(searchText,
                             RemoteUtils.createOnDoneCallbackStub(callback));
                 } catch (RemoteException e) {
                     throw new WrappedRuntimeException(e);
@@ -403,7 +423,7 @@
             public void onSearchSubmitted(@NonNull String searchText,
                     @NonNull OnDoneCallback callback) {
                 try {
-                    mStubListener.onSearchSubmitted(searchText,
+                    mStubCallback.onSearchSubmitted(searchText,
                             RemoteUtils.createOnDoneCallbackStub(callback));
                 } catch (RemoteException e) {
                     throw new WrappedRuntimeException(e);
@@ -413,24 +433,24 @@
     }
 
     @Keep // We need to keep these stub for Bundler serialization logic.
-    private static class SearchListenerStub extends ISearchListener.Stub {
-        private final SearchListener mSearchListener;
+    private static class SearchCallbackStub extends ISearchCallback.Stub {
+        private final SearchCallback mSearchCallback;
 
-        private SearchListenerStub(SearchListener searchListener) {
-            mSearchListener = searchListener;
+        SearchCallbackStub(SearchCallback searchCallback) {
+            mSearchCallback = searchCallback;
         }
 
         @Override
         public void onSearchTextChanged(String text, IOnDoneCallback callback) {
             RemoteUtils.dispatchHostCall(
-                    () -> mSearchListener.onSearchTextChanged(text), callback,
+                    () -> mSearchCallback.onSearchTextChanged(text), callback,
                     "onSearchTextChanged");
         }
 
         @Override
         public void onSearchSubmitted(String text, IOnDoneCallback callback) {
             RemoteUtils.dispatchHostCall(
-                    () -> mSearchListener.onSearchSubmitted(text), callback, "onSearchSubmitted");
+                    () -> mSearchCallback.onSearchSubmitted(text), callback, "onSearchSubmitted");
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Toggle.java b/car/app/app/src/main/java/androidx/car/app/model/Toggle.java
index f704b7a..8a0c4bd 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Toggle.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Toggle.java
@@ -25,9 +25,7 @@
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.car.app.IOnCheckedChangeListener;
 import androidx.car.app.IOnDoneCallback;
-import androidx.car.app.OnCheckedChangeListenerWrapper;
 import androidx.car.app.OnDoneCallback;
 import androidx.car.app.WrappedRuntimeException;
 import androidx.car.app.utils.RemoteUtils;
@@ -101,7 +99,7 @@
         return mIsChecked == otherToggle.mIsChecked;
     }
 
-    private Toggle(Builder builder) {
+    Toggle(Builder builder) {
         mIsChecked = builder.mIsChecked;
         mOnCheckedChangeListener = builder.mOnCheckedChangeListener;
     }
@@ -114,8 +112,8 @@
 
     /** A builder of {@link Toggle}. */
     public static final class Builder {
-        private OnCheckedChangeListenerWrapper mOnCheckedChangeListener;
-        private boolean mIsChecked;
+        OnCheckedChangeListenerWrapper mOnCheckedChangeListener;
+        boolean mIsChecked;
 
         /**
          * Sets the initial checked state for {@link Toggle}.
@@ -146,7 +144,7 @@
             return this;
         }
 
-        private Builder(@NonNull OnCheckedChangeListener onCheckedChangeListener) {
+        Builder(@NonNull OnCheckedChangeListener onCheckedChangeListener) {
             this.mOnCheckedChangeListener =
                     createOnCheckedChangeListener(onCheckedChangeListener);
         }
@@ -158,7 +156,7 @@
         }
     }
 
-    private static OnCheckedChangeListenerWrapper createOnCheckedChangeListener(
+    static OnCheckedChangeListenerWrapper createOnCheckedChangeListener(
             @NonNull OnCheckedChangeListener listener) {
         return new OnCheckedChangeListenerWrapper() {
             private final IOnCheckedChangeListener mOnCheckedChangeListener =
@@ -180,7 +178,7 @@
     private static class OnCheckedChangeListenerStub extends IOnCheckedChangeListener.Stub {
         private final OnCheckedChangeListener mOnCheckedChangeListener;
 
-        private OnCheckedChangeListenerStub(OnCheckedChangeListener onCheckedChangeListener) {
+        OnCheckedChangeListenerStub(OnCheckedChangeListener onCheckedChangeListener) {
             this.mOnCheckedChangeListener = onCheckedChangeListener;
         }
 
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
index 89857f8..b995bb9 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
@@ -17,7 +17,10 @@
 package androidx.car.app.model.constraints;
 
 
+import static androidx.annotation.RestrictTo.Scope;
+
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.car.app.model.Action;
 import androidx.car.app.model.Action.ActionType;
@@ -30,7 +33,10 @@
 
 /**
  * Encapsulates the constraints to apply when rendering a list of {@link Action}s on a template.
+ *
+ * @hide
  */
+@RestrictTo(Scope.LIBRARY)
 public class ActionsConstraints {
 
     /** Conservative constraints for most template types. */
@@ -168,7 +174,7 @@
         }
     }
 
-    private ActionsConstraints(Builder builder) {
+    ActionsConstraints(Builder builder) {
         mMaxActions = builder.mMaxActions;
         mMaxCustomTitles = builder.mMaxCustomTitles;
         mRequiredActionTypes = new HashSet<>(builder.mRequiredActionTypes);
@@ -194,10 +200,10 @@
      */
     @VisibleForTesting
     public static final class Builder {
-        private int mMaxActions = Integer.MAX_VALUE;
-        private int mMaxCustomTitles;
-        private final Set<Integer> mRequiredActionTypes = new HashSet<>();
-        private final Set<Integer> mDisallowedActionTypes = new HashSet<>();
+        int mMaxActions = Integer.MAX_VALUE;
+        int mMaxCustomTitles;
+        final Set<Integer> mRequiredActionTypes = new HashSet<>();
+        final Set<Integer> mDisallowedActionTypes = new HashSet<>();
 
         /** Sets the maximum number of actions allowed. */
         @NonNull
@@ -235,14 +241,14 @@
             return new ActionsConstraints(this);
         }
 
-        private Builder() {
+        Builder() {
         }
 
-        private Builder(ActionsConstraints constraints) {
-            this.mMaxActions = constraints.mMaxActions;
-            this.mMaxCustomTitles = constraints.mMaxCustomTitles;
-            this.mRequiredActionTypes.addAll(constraints.mRequiredActionTypes);
-            this.mDisallowedActionTypes.addAll(constraints.mDisallowedActionTypes);
+        Builder(ActionsConstraints constraints) {
+            this.mMaxActions = constraints.getMaxActions();
+            this.mMaxCustomTitles = constraints.getMaxCustomTitles();
+            this.mRequiredActionTypes.addAll(constraints.getRequiredActionTypes());
+            this.mDisallowedActionTypes.addAll(constraints.getDisallowedActionTypes());
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java
index 489022b..ae9e383 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java
@@ -16,7 +16,10 @@
 
 package androidx.car.app.model.constraints;
 
+import static androidx.annotation.RestrictTo.Scope;
+
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 import androidx.car.app.model.CarColor;
 import androidx.car.app.model.CarColor.CarColorType;
 
@@ -24,7 +27,10 @@
 
 /**
  * Encapsulates the constraints to apply when rendering a {@link CarColor} on a template.
+ *
+ * @hide
  */
+@RestrictTo(Scope.LIBRARY)
 public class CarColorConstraints {
 
     @NonNull
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java
index 2b9353c..c9cafe0 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java
@@ -20,12 +20,16 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
 import androidx.car.app.model.CarIcon;
 import androidx.core.graphics.drawable.IconCompat;
 
 /**
  * Encapsulates the constraints to apply when rendering a {@link CarIcon} on a template.
+ *
+ * @hide
  */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
 public class CarIconConstraints {
     /** Allow all custom icon types. */
     @NonNull
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
index ca81759..7c901b1 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
@@ -17,13 +17,16 @@
 package androidx.car.app.model.constraints;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.Row;
 
 /**
- * Encapsulates the constraints to apply when rendering a {@link
- * androidx.car.app.model.Row} in different contexts.
+ * Encapsulates the constraints to apply when rendering a {@link Row} in different contexts.
+ *
+ * @hide
  */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
 public class RowConstraints {
     @NonNull
     public static final RowConstraints UNCONSTRAINED = RowConstraints.builder().build();
@@ -152,7 +155,7 @@
         }
     }
 
-    private RowConstraints(Builder builder) {
+    RowConstraints(Builder builder) {
         mIsOnClickListenerAllowed = builder.mIsOnClickListenerAllowed;
         mMaxTextLinesPerRow = builder.mMaxTextLines;
         mMaxActionsExclusive = builder.mMaxActionsExclusive;
@@ -163,12 +166,12 @@
 
     /** A builder of {@link RowConstraints}. */
     public static final class Builder {
-        private boolean mIsOnClickListenerAllowed = true;
-        private boolean mIsToggleAllowed = true;
-        private int mMaxTextLines = Integer.MAX_VALUE;
-        private int mMaxActionsExclusive = Integer.MAX_VALUE;
-        private boolean mIsImageAllowed = true;
-        private CarIconConstraints mCarIconConstraints = CarIconConstraints.UNCONSTRAINED;
+        boolean mIsOnClickListenerAllowed = true;
+        boolean mIsToggleAllowed = true;
+        int mMaxTextLines = Integer.MAX_VALUE;
+        int mMaxActionsExclusive = Integer.MAX_VALUE;
+        boolean mIsImageAllowed = true;
+        CarIconConstraints mCarIconConstraints = CarIconConstraints.UNCONSTRAINED;
 
         /** Sets whether the row can have a click listener associated with it. */
         @NonNull
@@ -220,16 +223,16 @@
             return new RowConstraints(this);
         }
 
-        private Builder() {
+        Builder() {
         }
 
-        private Builder(RowConstraints constraints) {
-            mIsOnClickListenerAllowed = constraints.mIsOnClickListenerAllowed;
-            mMaxTextLines = constraints.mMaxTextLinesPerRow;
-            mMaxActionsExclusive = constraints.mMaxActionsExclusive;
-            mIsToggleAllowed = constraints.mIsToggleAllowed;
-            mIsImageAllowed = constraints.mIsImageAllowed;
-            mCarIconConstraints = constraints.mCarIconConstraints;
+        Builder(RowConstraints constraints) {
+            mIsOnClickListenerAllowed = constraints.isOnClickListenerAllowed();
+            mMaxTextLines = constraints.getMaxTextLinesPerRow();
+            mMaxActionsExclusive = constraints.getMaxActionsExclusive();
+            mIsToggleAllowed = constraints.isToggleAllowed();
+            mIsImageAllowed = constraints.isImageAllowed();
+            mCarIconConstraints = constraints.getCarIconConstraints();
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
index 5d20a94..bece6fc 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
@@ -16,13 +16,11 @@
 
 package androidx.car.app.model.constraints;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.car.app.model.constraints.RowConstraints.ROW_CONSTRAINTS_CONSERVATIVE;
 import static androidx.car.app.model.constraints.RowConstraints.ROW_CONSTRAINTS_FULL_LIST;
 import static androidx.car.app.model.constraints.RowConstraints.ROW_CONSTRAINTS_PANE;
 import static androidx.car.app.model.constraints.RowConstraints.ROW_CONSTRAINTS_SIMPLE;
 
-import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.car.app.model.ActionList;
@@ -30,41 +28,20 @@
 import androidx.car.app.model.Pane;
 import androidx.car.app.model.SectionedItemList;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Encapsulates the constraints to apply when rendering a row list under different contexts.
+ *
+ * @hide
  */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
 public class RowListConstraints {
-    /**
-     * The RowList that is used for max row.
-     *
-     * @hide
-     */
-    // TODO(shiufai): investigate how to expose IntDefs if needed.
-    @IntDef(value = {DEFAULT_LIST, PANE, ROUTE_PREVIEW})
-    @RestrictTo(LIBRARY)
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ListType {
-    }
-
-    @ListType
-    public static final int DEFAULT_LIST = 0;
-
-    @ListType
-    public static final int PANE = 1;
-
-    @ListType
-    public static final int ROUTE_PREVIEW = 2;
-
     /** Conservative constraints for all types lists. */
     @NonNull
     public static final RowListConstraints ROW_LIST_CONSTRAINTS_CONSERVATIVE =
             RowListConstraints.builder()
-                    .setRowListType(DEFAULT_LIST)
                     .setMaxActions(0)
                     .setRowConstraints(ROW_CONSTRAINTS_CONSERVATIVE)
                     .setAllowSelectableLists(false)
@@ -76,7 +53,6 @@
             ROW_LIST_CONSTRAINTS_CONSERVATIVE
                     .newBuilder()
                     .setMaxActions(2)
-                    .setRowListType(PANE)
                     .setRowConstraints(ROW_CONSTRAINTS_PANE)
                     .setAllowSelectableLists(false)
                     .build();
@@ -94,7 +70,6 @@
     public static final RowListConstraints ROW_LIST_CONSTRAINTS_ROUTE_PREVIEW =
             ROW_LIST_CONSTRAINTS_CONSERVATIVE
                     .newBuilder()
-                    .setRowListType(ROUTE_PREVIEW)
                     .setRowConstraints(ROW_CONSTRAINTS_SIMPLE)
                     .setAllowSelectableLists(true)
                     .build();
@@ -108,15 +83,13 @@
                     .setAllowSelectableLists(true)
                     .build();
 
-    @ListType
-    private final int mRowListType;
     private final int mMaxActions;
     private final RowConstraints mRowConstraints;
     private final boolean mAllowSelectableLists;
 
     /** A builder of {@link RowListConstraints}. */
     @NonNull
-    public static Builder builder() {
+    public static RowListConstraints.Builder builder() {
         return new Builder();
     }
 
@@ -126,12 +99,6 @@
         return new Builder(this);
     }
 
-    /** Returns the row list type for this constraint. */
-    @ListType
-    public int getRowListType() {
-        return mRowListType;
-    }
-
     /** Returns the maximum number of actions allowed to be added alongside the list. */
     public int getMaxActions() {
         return mMaxActions;
@@ -189,7 +156,7 @@
      * @throws IllegalArgumentException if the constraints are not met.
      */
     public void validateOrThrow(@NonNull Pane pane) {
-        ActionList actions = pane.getActionList();
+        ActionList actions = pane.getActions();
         if (actions != null && actions.getList().size() > mMaxActions) {
             throw new IllegalArgumentException(
                     "The number of actions on the pane exceeded the supported max of "
@@ -205,29 +172,19 @@
         }
     }
 
-    private RowListConstraints(Builder builder) {
+    RowListConstraints(Builder builder) {
         mMaxActions = builder.mMaxActions;
         mRowConstraints = builder.mRowConstraints;
         mAllowSelectableLists = builder.mAllowSelectableLists;
-        mRowListType = builder.mRowListType;
     }
 
     /**
      * A builder of {@link RowListConstraints}.
      */
     public static final class Builder {
-        @ListType
-        private int mRowListType;
-        private int mMaxActions;
-        private RowConstraints mRowConstraints = RowConstraints.UNCONSTRAINED;
-        private boolean mAllowSelectableLists;
-
-        /** Sets the row list type for this constraint. */
-        @NonNull
-        public Builder setRowListType(@ListType int rowListType) {
-            this.mRowListType = rowListType;
-            return this;
-        }
+        int mMaxActions;
+        RowConstraints mRowConstraints = RowConstraints.UNCONSTRAINED;
+        boolean mAllowSelectableLists;
 
         /** Sets the maximum number of actions allowed to be added alongside the list. */
         @NonNull
@@ -258,14 +215,13 @@
             return new RowListConstraints(this);
         }
 
-        private Builder() {
+        Builder() {
         }
 
-        private Builder(RowListConstraints constraints) {
-            this.mMaxActions = constraints.mMaxActions;
-            this.mRowConstraints = constraints.mRowConstraints;
-            this.mAllowSelectableLists = constraints.mAllowSelectableLists;
-            this.mRowListType = constraints.mRowListType;
+        Builder(RowListConstraints constraints) {
+            this.mMaxActions = constraints.getMaxActions();
+            this.mRowConstraints = constraints.getRowConstraints();
+            this.mAllowSelectableLists = constraints.isAllowSelectableLists();
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java b/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java
index 787fa0e..efed8b9 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java
@@ -23,6 +23,7 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.SuppressLint;
+import android.os.Looper;
 import android.util.Log;
 
 import androidx.annotation.MainThread;
@@ -38,6 +39,9 @@
 import androidx.car.app.serialization.Bundleable;
 import androidx.car.app.serialization.BundlerException;
 import androidx.car.app.utils.RemoteUtils;
+import androidx.core.content.ContextCompat;
+
+import java.util.concurrent.Executor;
 
 /**
  * Manager for communicating navigation related events with the host.
@@ -50,18 +54,21 @@
  * to end navigation or when the destination is reached, {@link #navigationEnded()} should be
  * called.
  *
- * <p>Navigation apps must also register a {@link NavigationManagerListener} to handle callbacks to
- * {@link NavigationManagerListener#stopNavigation()} issued by the host.
+ * <p>Navigation apps must also register a {@link NavigationManagerCallback} to handle callbacks to
+ * {@link NavigationManagerCallback#onStopNavigation()} issued by the host.
  */
 public class NavigationManager {
     private static final String TAG = "NavigationManager";
 
-    private final INavigationManager.Stub mNavigationmanager;
+    private final CarContext mCarContext;
+    private final INavigationManager.Stub mNavigationManager;
     private final HostDispatcher mHostDispatcher;
 
     // Guarded by main thread access.
     @Nullable
-    private NavigationManagerListener mListener;
+    private NavigationManagerCallback mNavigationManagerCallback;
+    @Nullable
+    private Executor mNavigationManagerCallbackExecutor;
     private boolean mIsNavigating;
     private boolean mIsAutoDriveEnabled;
 
@@ -74,7 +81,7 @@
      * <p>This method should only be invoked once the navigation app has called {@link
      * #navigationStarted()}, or else the updates will be dropped by the host. Once the app has
      * called {@link #navigationEnded()} or received
-     * {@link NavigationManagerListener#stopNavigation()} it should stop sending updates.
+     * {@link NavigationManagerCallback#onStopNavigation()} it should stop sending updates.
      *
      * <p>As the location changes, and in accordance with speed and rounded distance changes, the
      * {@link TravelEstimate}s in the provided {@link Trip} should be rebuilt and this method called
@@ -86,13 +93,14 @@
      * androidx.car.app.navigation.model.Maneuver}s of unknown type may be skipped while on other
      * displays the associated icon may be shown.
      *
+     * @param trip destination, steps, and trip estimates to be sent to the host
      * @throws HostException            if the call is invoked by an app that is not declared as
-     *                                  a navigation app in the manifest.
+     *                                  a navigation app in the manifest
      * @throws IllegalStateException    if the call occurs when navigation is not started. See
-     *                                  {@link #navigationStarted()} for more info.
+     *                                  {@link #navigationStarted()} for more info
      * @throws IllegalArgumentException if any of the destinations, steps, or trip position is
-     *                                  not well formed.
-     * @throws IllegalStateException    if the current thread is not the main thread.
+     *                                  not well formed
+     * @throws IllegalStateException    if the current thread is not the main thread
      */
     @MainThread
     public void updateTrip(@NonNull Trip trip) {
@@ -118,25 +126,57 @@
     }
 
     /**
-     * Sets a listener to start receiving navigation manager events, or {@code null} to clear the
-     * listener.
+     * Sets a callback to start receiving navigation manager events.
      *
-     * @throws IllegalStateException if {@code null} is passed in while navigation is started. See
+     * Note that the callback events will be executed on the main thread using
+     * {@link Looper#getMainLooper()}. To specify the execution thread, use
+     * {@link #setNavigationManagerCallback(Executor, NavigationManagerCallback)}.
+     *
+     * @param callback the {@link NavigationManagerCallback} to use
+     * @throws IllegalStateException if the current thread is not the main thread
+     */
+    @SuppressLint("ExecutorRegistration")
+    @MainThread
+    public void setNavigationManagerCallback(@NonNull NavigationManagerCallback callback) {
+        checkMainThread();
+        Executor executor = ContextCompat.getMainExecutor(mCarContext);
+        setNavigationManagerCallback(executor, callback);
+    }
+
+    /**
+     * Sets a callback to start receiving navigation manager events.
+     *
+     * @param executor the executor which will be used for invoking the callback
+     * @param callback the {@link NavigationManagerCallback} to use
+     * @throws IllegalStateException if the current thread is not the main thread.
+     */
+    @MainThread
+    public void setNavigationManagerCallback(@NonNull /* @CallbackExecutor */ Executor executor,
+            @NonNull NavigationManagerCallback callback) {
+        checkMainThread();
+
+        mNavigationManagerCallbackExecutor = executor;
+        mNavigationManagerCallback = callback;
+        if (mIsAutoDriveEnabled) {
+            onAutoDriveEnabled();
+        }
+    }
+
+    /**
+     * Clears the callback for receiving navigation manager events.
+     *
+     * @throws IllegalStateException if navigation is started. See
      *                               {@link #navigationStarted()} for more info.
      * @throws IllegalStateException if the current thread is not the main thread.
      */
-    // TODO(rampara): Add Executor parameter.
-    @SuppressLint("ExecutorRegistration")
     @MainThread
-    public void setListener(@Nullable NavigationManagerListener listener) {
+    public void clearNavigationManagerCallback() {
         checkMainThread();
-        if (mIsNavigating && listener == null) {
-            throw new IllegalStateException("Removing listener while navigating");
+        if (mIsNavigating) {
+            throw new IllegalStateException("Removing callback while navigating");
         }
-        this.mListener = listener;
-        if (mIsAutoDriveEnabled && listener != null) {
-            listener.onAutoDriveEnabled();
-        }
+        mNavigationManagerCallbackExecutor = null;
+        mNavigationManagerCallback = null;
     }
 
     /**
@@ -146,14 +186,15 @@
      * the host. The app must call this method to inform the system that it has started
      * navigation in response to user action.
      *
-     * <p>This function can only called if {@link #setListener(NavigationManagerListener)} has been
-     * called with a non-{@code null} value. The listener is required so that a signal to stop
+     * <p>This function can only called if
+     * {@link #setNavigationManagerCallback(NavigationManagerCallback)} has been
+     * called with a non-{@code null} value. The callback is required so that a signal to stop
      * navigation from the host can be handled using
-     * {@link NavigationManagerListener#stopNavigation()}.
+     * {@link NavigationManagerCallback#onStopNavigation()}.
      *
      * <p>This method is idempotent.
      *
-     * @throws IllegalStateException if no navigation manager listener has been set.
+     * @throws IllegalStateException if no navigation manager callback has been set.
      * @throws IllegalStateException if the current thread is not the main thread.
      */
     @MainThread
@@ -162,8 +203,8 @@
         if (mIsNavigating) {
             return;
         }
-        if (mListener == null) {
-            throw new IllegalStateException("No listener has been set");
+        if (mNavigationManagerCallback == null) {
+            throw new IllegalStateException("No callback has been set");
         }
         mIsNavigating = true;
         mHostDispatcher.dispatch(
@@ -209,8 +250,9 @@
      */
     @RestrictTo(LIBRARY)
     @NonNull
-    public static NavigationManager create(@NonNull HostDispatcher hostDispatcher) {
-        return new NavigationManager(hostDispatcher);
+    public static NavigationManager create(@NonNull CarContext carContext,
+            @NonNull HostDispatcher hostDispatcher) {
+        return new NavigationManager(carContext, hostDispatcher);
     }
 
     /**
@@ -221,7 +263,7 @@
     @RestrictTo(LIBRARY)
     @NonNull
     public INavigationManager.Stub getIInterface() {
-        return mNavigationmanager;
+        return mNavigationManager;
     }
 
     /**
@@ -231,19 +273,20 @@
      */
     @RestrictTo(LIBRARY)
     @MainThread
-    public void stopNavigation() {
+    public void onStopNavigation() {
         checkMainThread();
         if (!mIsNavigating) {
             return;
         }
         mIsNavigating = false;
-        requireNonNull(mListener).stopNavigation();
+        requireNonNull(mNavigationManagerCallbackExecutor).execute(() -> {
+            requireNonNull(mNavigationManagerCallback).onStopNavigation();
+        });
     }
 
     /**
-     * Signifies that from this point, until {@link
-     * androidx.car.app.CarAppService#onCarAppFinished} is called, any navigation
-     * should automatically start driving to the destination as if the user was moving.
+     * Signifies that from this point, until {@link CarContext#finishCarApp()} is called, any
+     * navigation should automatically start driving to the destination as if the user was moving.
      *
      * <p>This is used in a testing environment, allowing testing the navigation app's navigation
      * capabilities without being in a car.
@@ -255,25 +298,30 @@
     public void onAutoDriveEnabled() {
         checkMainThread();
         mIsAutoDriveEnabled = true;
-        if (mListener != null) {
+        if (mNavigationManagerCallback != null) {
             Log.d(TAG, "Executing onAutoDriveEnabled");
-            mListener.onAutoDriveEnabled();
+            requireNonNull(mNavigationManagerCallbackExecutor).execute(() -> {
+                mNavigationManagerCallback.onAutoDriveEnabled();
+            });
         } else {
-            Log.w(TAG, "NavigationManagerListener not set, skipping onAutoDriveEnabled");
+            Log.w(TAG, "NavigationManagerCallback not set, skipping onAutoDriveEnabled");
         }
     }
 
     /** @hide */
     @RestrictTo(LIBRARY_GROUP) // Restrict to testing library
     @SuppressWarnings({"methodref.receiver.bound.invalid"})
-    protected NavigationManager(@NonNull HostDispatcher hostDispatcher) {
-        this.mHostDispatcher = requireNonNull(hostDispatcher);
-        mNavigationmanager =
+    protected NavigationManager(@NonNull CarContext carContext,
+            @NonNull HostDispatcher hostDispatcher) {
+        mCarContext = carContext;
+        mHostDispatcher = requireNonNull(hostDispatcher);
+        mNavigationManager =
                 new INavigationManager.Stub() {
                     @Override
-                    public void stopNavigation(IOnDoneCallback callback) {
+                    public void onStopNavigation(IOnDoneCallback callback) {
                         RemoteUtils.dispatchHostCall(
-                                NavigationManager.this::stopNavigation, callback, "stopNavigation");
+                                NavigationManager.this::onStopNavigation, callback,
+                                "onStopNavigation");
                     }
                 };
     }
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManagerListener.java b/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManagerCallback.java
similarity index 76%
rename from car/app/app/src/main/java/androidx/car/app/navigation/NavigationManagerListener.java
rename to car/app/app/src/main/java/androidx/car/app/navigation/NavigationManagerCallback.java
index ee4c29f..1a44e6c 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManagerListener.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManagerCallback.java
@@ -16,16 +16,15 @@
 
 package androidx.car.app.navigation;
 
-import android.annotation.SuppressLint;
-
+import androidx.car.app.CarContext;
 import androidx.car.app.navigation.model.Trip;
 
 /**
- * Listener of events from the {@link NavigationManager}.
+ * Callback for events from the {@link NavigationManager}.
  *
  * @see NavigationManager
  */
-public interface NavigationManagerListener {
+public interface NavigationManagerCallback {
     /**
      * Notifies the app to stop active navigation, which may occurs when another source such as the
      * car head unit starts navigating.
@@ -34,18 +33,13 @@
      * guidance, routing-related notifications, and updating trip information via {@link
      * NavigationManager#updateTrip(Trip)}.
      */
-
-    // TODO(rampara): Listener method names must follow the on<Something> style. Consider
-    //  onShouldStopNavigation.
-    @SuppressLint("CallbackMethodName")
-    void stopNavigation();
+    void onStopNavigation();
 
     /**
      * Notifies the app that, from this point onwards, when the user chooses to navigate to a
      * destination, the app should start simulating a drive towards that destination.
      *
-     * <p>This mode should remain active until {@link
-     * androidx.car.app.CarAppService#onCarAppFinished} is called.
+     * <p>This mode should remain active until {@link CarContext#finishCarApp()} is called.
      *
      * <p>This functionality is used to allow verifying the app's navigation capabilities without
      * being in an actual car.
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Destination.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Destination.java
index efcd9e6..3092b7e 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Destination.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Destination.java
@@ -102,7 +102,7 @@
         return Objects.hash(mName, mAddress, mImage);
     }
 
-    private Destination(Builder builder) {
+    Destination(Builder builder) {
         this.mName = builder.mName;
         this.mAddress = builder.mAddress;
         this.mImage = builder.mImage;
@@ -118,11 +118,11 @@
     /** A builder of {@link Destination}. */
     public static final class Builder {
         @Nullable
-        private CarText mName;
+        CarText mName;
         @Nullable
-        private CarText mAddress;
+        CarText mAddress;
         @Nullable
-        private CarIcon mImage;
+        CarIcon mImage;
 
         /**
          * Sets the destination name formatted for the user's current locale, or {@code null} to not
@@ -183,7 +183,7 @@
             return new Destination(this);
         }
 
-        private Builder() {
+        Builder() {
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Lane.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Lane.java
index 335035d..ca34d39 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Lane.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Lane.java
@@ -72,7 +72,7 @@
         return Objects.equals(mDirections, otherLane.mDirections);
     }
 
-    private Lane(List<LaneDirection> directions) {
+    Lane(List<LaneDirection> directions) {
         this.mDirections = new ArrayList<>(directions);
     }
 
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java
index d937e9c..55554b9 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java
@@ -576,8 +576,7 @@
                 && Objects.equals(mIcon, otherManeuver.mIcon);
     }
 
-    private Maneuver(
-            @Type int type, int roundaboutExitNumber, int roundaboutExitAngle,
+    Maneuver(@Type int type, int roundaboutExitNumber, int roundaboutExitAngle,
             @Nullable CarIcon icon) {
         this.mType = type;
         this.mRoundaboutExitNumber = roundaboutExitNumber;
@@ -598,14 +597,14 @@
         return (type >= TYPE_UNKNOWN && type <= TYPE_FERRY_TRAIN_RIGHT);
     }
 
-    private static boolean isValidTypeWithExitNumber(@Type int type) {
+    static boolean isValidTypeWithExitNumber(@Type int type) {
         return (type == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW
                 || type == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW
                 || type == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE
                 || type == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE);
     }
 
-    private static boolean isValidTypeWithExitAngle(@Type int type) {
+    static boolean isValidTypeWithExitAngle(@Type int type) {
         return (type == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE
                 || type == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE);
     }
@@ -621,7 +620,7 @@
         @Nullable
         private CarIcon mIcon;
 
-        private Builder(@Type int type) {
+        Builder(@Type int type) {
             this.mType = type;
         }
 
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/MessageInfo.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/MessageInfo.java
index 490d300..6810d01 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/MessageInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/MessageInfo.java
@@ -91,7 +91,7 @@
                 && Objects.equals(mImage, otherInfo.mImage);
     }
 
-    private MessageInfo(Builder builder) {
+    MessageInfo(Builder builder) {
         mTitle = builder.mTitle;
         mText = builder.mText;
         mImage = builder.mImage;
@@ -107,13 +107,13 @@
     /** A builder of {@link MessageInfo}. */
     public static final class Builder {
         @Nullable
-        private CarText mTitle;
+        CarText mTitle;
         @Nullable
-        private CarText mText;
+        CarText mText;
         @Nullable
-        private CarIcon mImage;
+        CarIcon mImage;
 
-        private Builder(@NonNull CharSequence title) {
+        Builder(@NonNull CharSequence title) {
             this.mTitle = CarText.create(requireNonNull(title));
         }
 
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
index b463033..25650aa 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
@@ -28,7 +28,7 @@
 import androidx.annotation.Nullable;
 import androidx.car.app.CarAppPermission;
 import androidx.car.app.Screen;
-import androidx.car.app.SurfaceListener;
+import androidx.car.app.SurfaceCallback;
 import androidx.car.app.model.Action;
 import androidx.car.app.model.ActionStrip;
 import androidx.car.app.model.CarColor;
@@ -52,7 +52,7 @@
  * host with a new template with the updated information.
  *
  * <p>The template itself does not expose a drawing surface. In order to draw on the canvas, use
- * {@link androidx.car.app.AppManager#setSurfaceListener(SurfaceListener)}.
+ * {@link androidx.car.app.AppManager#setSurfaceCallback(SurfaceCallback)}.
  *
  * <p>See {@code androidx.car.app.notification.CarAppExtender} for how to show
  * alerts with notifications. Frequent alert notifications distract the driver and are discouraged.
@@ -151,7 +151,7 @@
         CarAppPermission.checkHasLibraryPermission(context, CarAppPermission.NAVIGATION_TEMPLATES);
     }
 
-    private NavigationTemplate(Builder builder) {
+    NavigationTemplate(Builder builder) {
         mNavigationInfo = builder.mNavigationInfo;
         mBackgroundColor = builder.mBackgroundColor;
         mDestinationTravelEstimate = builder.mDestinationTravelEstimate;
@@ -169,14 +169,14 @@
     /** A builder of {@link NavigationTemplate}. */
     public static final class Builder {
         @Nullable
-        private NavigationInfo mNavigationInfo;
+        NavigationInfo mNavigationInfo;
         @Nullable
-        private CarColor mBackgroundColor;
+        CarColor mBackgroundColor;
         @Nullable
-        private TravelEstimate mDestinationTravelEstimate;
-        private ActionStrip mActionStrip;
+        TravelEstimate mDestinationTravelEstimate;
+        ActionStrip mActionStrip;
 
-        private Builder() {
+        Builder() {
         }
 
         /**
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java
index 48927a8..a9bd2ef 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java
@@ -25,10 +25,9 @@
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 import androidx.car.app.CarAppPermission;
 import androidx.car.app.Screen;
-import androidx.car.app.SurfaceListener;
+import androidx.car.app.SurfaceCallback;
 import androidx.car.app.model.Action;
 import androidx.car.app.model.ActionStrip;
 import androidx.car.app.model.CarText;
@@ -49,7 +48,7 @@
  * A template that supports showing a list of places alongside a custom drawn map.
  *
  * <p>The template itself does not expose a drawing surface. In order to draw on the canvas, use
- * {@link androidx.car.app.AppManager#setSurfaceListener(SurfaceListener)}.
+ * {@link androidx.car.app.AppManager#setSurfaceCallback(SurfaceCallback)}.
  *
  * <h4>Template Restrictions</h4>
  *
@@ -58,7 +57,7 @@
  *
  * <ul>
  *   <li>The template title has not changed, and
- *   <li>The previous template is in a loading state (see {@link Builder#setIsLoading}, or the
+ *   <li>The previous template is in a loading state (see {@link Builder#setLoading}, or the
  *       number of rows and the string contents (title, texts, not counting spans) of each row
  *       between the previous and new {@link ItemList}s have not changed.
  * </ul>
@@ -145,7 +144,7 @@
                 && Objects.equals(mActionStrip, otherTemplate.mActionStrip);
     }
 
-    private PlaceListNavigationTemplate(Builder builder) {
+    PlaceListNavigationTemplate(Builder builder) {
         mTitle = builder.mTitle;
         mIsLoading = builder.mIsLoading;
         mItemList = builder.mItemList;
@@ -165,14 +164,14 @@
     /** A builder of {@link PlaceListNavigationTemplate}. */
     public static final class Builder {
         @Nullable
-        private CarText mTitle;
-        private boolean mIsLoading;
+        CarText mTitle;
+        boolean mIsLoading;
         @Nullable
-        private ItemList mItemList;
+        ItemList mItemList;
         @Nullable
-        private Action mHeaderAction;
+        Action mHeaderAction;
         @Nullable
-        private ActionStrip mActionStrip;
+        ActionStrip mActionStrip;
 
         /** Sets the {@link CharSequence} to show as title, or {@code null} to not show a title. */
         @NonNull
@@ -191,10 +190,8 @@
          * once the data is ready. If set to {@code false}, the UI shows the {@link ItemList}
          * contents added via {@link #setItemList}.
          */
-        // TODO(rampara): Consider renaming to setLoading()
-        @SuppressWarnings("MissingGetterMatchingBuilder")
         @NonNull
-        public Builder setIsLoading(boolean isLoading) {
+        public Builder setLoading(boolean isLoading) {
             this.mIsLoading = isLoading;
             return this;
         }
@@ -262,18 +259,6 @@
         }
 
         /**
-         * Sets an {@link ItemList} for the template. This method does not enforce the
-         * template's requirements and is only intended for testing purposes.
-         */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        @VisibleForTesting
-        @NonNull
-        public Builder setItemListForTesting(@Nullable ItemList itemList) {
-            this.mItemList = itemList;
-            return this;
-        }
-
-        /**
          * Sets the {@link ActionStrip} for this template, or {@code null} to not show an {@link
          * ActionStrip}.
          *
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java
index f64fa55..28995c1 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java
@@ -27,10 +27,9 @@
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 import androidx.car.app.CarAppPermission;
 import androidx.car.app.Screen;
-import androidx.car.app.SurfaceListener;
+import androidx.car.app.SurfaceCallback;
 import androidx.car.app.model.Action;
 import androidx.car.app.model.ActionStrip;
 import androidx.car.app.model.CarText;
@@ -59,7 +58,7 @@
  * </ol>
  *
  * <p>The template itself does not expose a drawing surface. In order to draw on the canvas, use
- * {@link androidx.car.app.AppManager#setSurfaceListener(SurfaceListener)}.
+ * {@link androidx.car.app.AppManager#setSurfaceCallback(SurfaceCallback)}.
  *
  * <h4>Template Restrictions</h4>
  *
@@ -68,7 +67,7 @@
  *
  * <ul>
  *   <li>The template title has not changed, and
- *   <li>The previous template is in a loading state (see {@link Builder#setIsLoading}, or the
+ *   <li>The previous template is in a loading state (see {@link Builder#setLoading}, or the
  *       number of rows and the string contents (title, texts, not counting spans) of each row
  *       between the previous and new {@link ItemList}s have not changed.
  * </ul>
@@ -168,7 +167,7 @@
                 && Objects.equals(mActionStrip, otherTemplate.mActionStrip);
     }
 
-    private RoutePreviewNavigationTemplate(Builder builder) {
+    RoutePreviewNavigationTemplate(Builder builder) {
         mTitle = builder.mTitle;
         mIsLoading = builder.mIsLoading;
         mNavigateAction = builder.mNavigateAction;
@@ -190,16 +189,16 @@
     /** A builder of {@link RoutePreviewNavigationTemplate}. */
     public static final class Builder {
         @Nullable
-        private CarText mTitle;
-        private boolean mIsLoading;
+        CarText mTitle;
+        boolean mIsLoading;
         @Nullable
-        private Action mNavigateAction;
+        Action mNavigateAction;
         @Nullable
-        private ItemList mItemList;
+        ItemList mItemList;
         @Nullable
-        private Action mHeaderAction;
+        Action mHeaderAction;
         @Nullable
-        private ActionStrip mActionStrip;
+        ActionStrip mActionStrip;
 
         /** Sets the {@link CharSequence} to show as title, or {@code null} to not show a title. */
         @NonNull
@@ -218,10 +217,8 @@
          * once the data is ready. If set to {@code false}, the UI shows the {@link ItemList}
          * contents added via {@link #setItemList}.
          */
-        // TODO(rampara): Consider renaming to setLoading()
-        @SuppressWarnings("MissingGetterMatchingBuilder")
         @NonNull
-        public Builder setIsLoading(boolean isLoading) {
+        public Builder setLoading(boolean isLoading) {
             this.mIsLoading = isLoading;
             return this;
         }
@@ -308,18 +305,6 @@
         }
 
         /**
-         * Sets an {@link ItemList} for the template. This method does not enforce the
-         * template's requirements and is only intended for testing purposes.
-         */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        @VisibleForTesting
-        @NonNull
-        public Builder setItemListForTesting(@Nullable ItemList itemList) {
-            this.mItemList = itemList;
-            return this;
-        }
-
-        /**
          * Sets the {@link ActionStrip} for this template, or {@code null} to not show an {@link
          * ActionStrip}.
          *
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
index 1de529f..667951e 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
@@ -106,7 +106,7 @@
                 && Objects.equals(mJunctionImage, otherInfo.mJunctionImage);
     }
 
-    private RoutingInfo(Builder builder) {
+    RoutingInfo(Builder builder) {
         mCurrentStep = builder.mCurrentStep;
         mCurrentDistance = builder.mCurrentDistance;
         mNextStep = builder.mNextStep;
@@ -126,16 +126,16 @@
     /** A builder of {@link RoutingInfo}. */
     public static final class Builder {
         @Nullable
-        private Step mCurrentStep;
+        Step mCurrentStep;
         @Nullable
-        private Distance mCurrentDistance;
+        Distance mCurrentDistance;
         @Nullable
-        private Step mNextStep;
+        Step mNextStep;
         @Nullable
-        private CarIcon mJunctionImage;
-        private boolean mIsLoading;
+        CarIcon mJunctionImage;
+        boolean mIsLoading;
 
-        private Builder() {
+        Builder() {
         }
 
         /**
@@ -214,10 +214,8 @@
          *
          * @see #build
          */
-        // TODO(rampara): Consider renaming to setLoading()
-        @SuppressWarnings("MissingGetterMatchingBuilder")
         @NonNull
-        public Builder setIsLoading(boolean isLoading) {
+        public Builder setLoading(boolean isLoading) {
             this.mIsLoading = isLoading;
             return this;
         }
@@ -228,7 +226,7 @@
          * <h4>Requirements</h4>
          *
          * The {@link RoutingInfo} can be in a loading state by passing {@code true} to {@link
-         * #setIsLoading(boolean)}, in which case no other fields may be set. Otherwise, the current
+         * #setLoading(boolean)}, in which case no other fields may be set. Otherwise, the current
          * step and distance must be set. If the lane information is set with {@link
          * Step.Builder#addLane(Lane)}, then the lane image must also be set with {@link
          * Step.Builder#setLanesImage(CarIcon)}.
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java
index 25721ed..8809735 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java
@@ -141,7 +141,7 @@
                 && Objects.equals(mRoad, otherStep.mRoad);
     }
 
-    private Step(
+    Step(
             @Nullable Maneuver maneuver,
             List<Lane> lanes,
             @Nullable CarIcon lanesImage,
@@ -174,17 +174,17 @@
         @Nullable
         private CarText mRoad;
 
-        private Builder(CharSequence cue) {
+        Builder(CharSequence cue) {
             this.mCue = CarText.create(cue);
         }
 
-        private Builder(Step step) {
-            this.mManeuver = step.mManeuver;
+        Builder(Step step) {
+            this.mManeuver = step.getManeuver();
             this.mLanes.clear();
-            this.mLanes.addAll(step.mLanes);
-            this.mLanesImage = step.mLanesImage;
-            this.mCue = requireNonNull(step.mCue);
-            this.mRoad = step.mRoad;
+            this.mLanes.addAll(step.getLanes());
+            this.mLanesImage = step.getLanesImage();
+            this.mCue = requireNonNull(step.getCue());
+            this.mRoad = step.getRoad();
         }
 
         /**
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
index 8efd3c4..1233bd6 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
@@ -223,7 +223,7 @@
         mRemainingDistanceColor = CarColor.DEFAULT;
     }
 
-    private TravelEstimate(Builder builder) {
+    TravelEstimate(Builder builder) {
         this.mRemainingDistance = builder.mRemainingDistance;
         this.mRemainingTimeSeconds = builder.mRemainingTimeSeconds;
         this.mArrivalTimeAtDestination = builder.mArrivalTimeAtDestination;
@@ -233,13 +233,13 @@
 
     /** A builder of {@link TravelEstimate}. */
     public static final class Builder {
-        private final Distance mRemainingDistance;
-        private long mRemainingTimeSeconds = REMAINING_TIME_UNKNOWN;
-        private final DateTimeWithZone mArrivalTimeAtDestination;
-        private CarColor mRemainingTimeColor = CarColor.DEFAULT;
-        private CarColor mRemainingDistanceColor = CarColor.DEFAULT;
+        final Distance mRemainingDistance;
+        long mRemainingTimeSeconds = REMAINING_TIME_UNKNOWN;
+        final DateTimeWithZone mArrivalTimeAtDestination;
+        CarColor mRemainingTimeColor = CarColor.DEFAULT;
+        CarColor mRemainingDistanceColor = CarColor.DEFAULT;
 
-        private Builder(
+        Builder(
                 Distance remainingDistance,
                 DateTimeWithZone arrivalTimeAtDestination) {
             this.mRemainingDistance = requireNonNull(remainingDistance);
@@ -250,7 +250,7 @@
         // TODO(rampara): Move API 26 calls into separate class.
         @RequiresApi(26)
         @SuppressWarnings("AndroidJdkLibsChecker")
-        private Builder(
+        Builder(
                 Distance remainingDistance,
                 ZonedDateTime arrivalTimeAtDestination) {
             this.mRemainingDistance = remainingDistance;
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
index d3f7b5d..1a52f14 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
@@ -128,7 +128,7 @@
                 && Objects.equals(mIsLoading, otherTrip.mIsLoading);
     }
 
-    private Trip(Builder builder) {
+    Trip(Builder builder) {
         this.mDestinations = new ArrayList<>(builder.mDestinations);
         this.mSteps = new ArrayList<>(builder.mSteps);
         this.mDestinationTravelEstimates = new ArrayList<>(builder.mDestinationTravelEstimates);
@@ -149,13 +149,13 @@
 
     /** A builder of {@link Trip}. */
     public static final class Builder {
-        private final List<Destination> mDestinations = new ArrayList<>();
-        private final List<Step> mSteps = new ArrayList<>();
-        private final List<TravelEstimate> mDestinationTravelEstimates = new ArrayList<>();
-        private final List<TravelEstimate> mStepTravelEstimates = new ArrayList<>();
+        final List<Destination> mDestinations = new ArrayList<>();
+        final List<Step> mSteps = new ArrayList<>();
+        final List<TravelEstimate> mDestinationTravelEstimates = new ArrayList<>();
+        final List<TravelEstimate> mStepTravelEstimates = new ArrayList<>();
         @Nullable
-        private CarText mCurrentRoad;
-        private boolean mIsLoading;
+        CarText mCurrentRoad;
+        boolean mIsLoading;
 
         /**
          * Adds a destination to the trip.
@@ -267,10 +267,8 @@
          * <p>If set to {@code true}, the UI may show a loading indicator, and adding any steps
          * or step travel estimates will throw an {@link IllegalArgumentException}.
          */
-        // TODO(rampara): Consider renaming to setLoading()
-        @SuppressWarnings("MissingGetterMatchingBuilder")
         @NonNull
-        public Builder setIsLoading(boolean isLoading) {
+        public Builder setLoading(boolean isLoading) {
             this.mIsLoading = isLoading;
             return this;
         }
diff --git a/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java b/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java
index 168a094..9caab12 100644
--- a/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java
+++ b/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java
@@ -170,7 +170,7 @@
                         NotificationManagerCompat.IMPORTANCE_UNSPECIFIED);
     }
 
-    private CarAppExtender(Builder builder) {
+    CarAppExtender(Builder builder) {
         this.mContentTitle = builder.mContentTitle;
         this.mContentText = builder.mContentText;
         this.mSmallIconResId = builder.mSmallIconResId;
@@ -278,7 +278,8 @@
      *
      * @see Builder#setSmallIcon(int)
      */
-    public int getSmallIconResId() {
+    @DrawableRes
+    public int getSmallIcon() {
         return mSmallIconResId;
     }
 
@@ -288,7 +289,7 @@
      * @see Builder#setLargeIcon(Bitmap)
      */
     @Nullable
-    public Bitmap getLargeIconBitmap() {
+    public Bitmap getLargeIcon() {
         return mLargeIconBitmap;
     }
 
@@ -336,18 +337,18 @@
     /** A builder of {@link CarAppExtender}. */
     public static final class Builder {
         @Nullable
-        private CharSequence mContentTitle;
+        CharSequence mContentTitle;
         @Nullable
-        private CharSequence mContentText;
-        private int mSmallIconResId;
+        CharSequence mContentText;
+        int mSmallIconResId;
         @Nullable
-        private Bitmap mLargeIconBitmap;
+        Bitmap mLargeIconBitmap;
         @Nullable
-        private PendingIntent mContentIntent;
+        PendingIntent mContentIntent;
         @Nullable
-        private PendingIntent mDeleteIntent;
-        private final ArrayList<Action> mActions = new ArrayList<>();
-        private int mImportance = NotificationManagerCompat.IMPORTANCE_UNSPECIFIED;
+        PendingIntent mDeleteIntent;
+        final ArrayList<Action> mActions = new ArrayList<>();
+        int mImportance = NotificationManagerCompat.IMPORTANCE_UNSPECIFIED;
 
         /**
          * Sets the title of the notification in the car screen, or {@code null} to not override the
@@ -391,8 +392,6 @@
          * <p>This method is equivalent to {@link NotificationCompat.Builder#setSmallIcon(int)} for
          * the car screen.
          */
-        // TODO(rampara): Revisit small icon getter API
-        @SuppressWarnings("MissingGetterMatchingBuilder")
         @NonNull
         public Builder setSmallIcon(int iconResId) {
             this.mSmallIconResId = iconResId;
@@ -412,8 +411,6 @@
          * <p>The large icon will be shown in the notification badge. If the large icon is not
          * set in the {@link CarAppExtender} or the notification, the small icon will show instead.
          */
-        // TODO(rampara): Revisit small icon getter API
-        @SuppressWarnings("MissingGetterMatchingBuilder")
         @NonNull
         public Builder setLargeIcon(@Nullable Bitmap bitmap) {
             this.mLargeIconBitmap = bitmap;
diff --git a/car/app/app/src/main/java/androidx/car/app/utils/CommonUtils.java b/car/app/app/src/main/java/androidx/car/app/utils/CommonUtils.java
index bbf1bac..1e00485 100644
--- a/car/app/app/src/main/java/androidx/car/app/utils/CommonUtils.java
+++ b/car/app/app/src/main/java/androidx/car/app/utils/CommonUtils.java
@@ -28,7 +28,7 @@
 @RestrictTo(LIBRARY)
 public final class CommonUtils {
     /** Tag to use for logging in the library. */
-    public static final String TAG = "car.app";
+    public static final String TAG = "CarApp";
 
     private CommonUtils() {
     }
diff --git a/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java b/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java
index 09d87e2..da69f8f 100644
--- a/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java
+++ b/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java
@@ -30,10 +30,10 @@
 import androidx.car.app.FailureResponse;
 import androidx.car.app.HostException;
 import androidx.car.app.IOnDoneCallback;
-import androidx.car.app.ISurfaceListener;
+import androidx.car.app.ISurfaceCallback;
 import androidx.car.app.OnDoneCallback;
+import androidx.car.app.SurfaceCallback;
 import androidx.car.app.SurfaceContainer;
-import androidx.car.app.SurfaceListener;
 import androidx.car.app.WrappedRuntimeException;
 import androidx.car.app.serialization.Bundleable;
 import androidx.car.app.serialization.BundlerException;
@@ -84,33 +84,33 @@
     }
 
     /**
-     * Returns an {@link ISurfaceListener} stub that invokes the given {@link SurfaceListener},
+     * Returns an {@link ISurfaceCallback} stub that invokes the given {@link SurfaceCallback},
      * if it is not {@code null}, otherwise returns {@code null}.
      */
     @Nullable
-    public static ISurfaceListener stubSurfaceListener(@Nullable SurfaceListener surfaceListener) {
-        if (surfaceListener == null) {
+    public static ISurfaceCallback stubSurfaceCallback(@Nullable SurfaceCallback surfaceCallback) {
+        if (surfaceCallback == null) {
             return null;
         }
 
-        return new SurfaceListenerStub(surfaceListener);
+        return new SurfaceCallbackStub(surfaceCallback);
     }
 
     private RemoteUtils() {
     }
 
-    private static class SurfaceListenerStub extends ISurfaceListener.Stub {
+    private static class SurfaceCallbackStub extends ISurfaceCallback.Stub {
 
-        private final SurfaceListener mSurfaceListener;
+        private final SurfaceCallback mSurfaceCallback;
 
-        SurfaceListenerStub(SurfaceListener surfaceListener) {
-            this.mSurfaceListener = surfaceListener;
+        SurfaceCallbackStub(SurfaceCallback surfaceCallback) {
+            this.mSurfaceCallback = surfaceCallback;
         }
 
         @Override
         public void onSurfaceAvailable(Bundleable surfaceContainer, IOnDoneCallback callback) {
             dispatchHostCall(
-                    () -> mSurfaceListener.onSurfaceAvailable(
+                    () -> mSurfaceCallback.onSurfaceAvailable(
                             (SurfaceContainer) surfaceContainer.get()),
                     callback,
                     "onSurfaceAvailable");
@@ -119,7 +119,7 @@
         @Override
         public void onVisibleAreaChanged(Rect visibleArea, IOnDoneCallback callback) {
             dispatchHostCall(
-                    () -> mSurfaceListener.onVisibleAreaChanged(visibleArea),
+                    () -> mSurfaceCallback.onVisibleAreaChanged(visibleArea),
                     callback,
                     "onVisibleAreaChanged");
         }
@@ -127,14 +127,14 @@
         @Override
         public void onStableAreaChanged(Rect stableArea, IOnDoneCallback callback) {
             dispatchHostCall(
-                    () -> mSurfaceListener.onStableAreaChanged(stableArea), callback,
+                    () -> mSurfaceCallback.onStableAreaChanged(stableArea), callback,
                     "onStableAreaChanged");
         }
 
         @Override
         public void onSurfaceDestroyed(Bundleable surfaceContainer, IOnDoneCallback callback) {
             dispatchHostCall(
-                    () -> mSurfaceListener.onSurfaceDestroyed(
+                    () -> mSurfaceCallback.onSurfaceDestroyed(
                             (SurfaceContainer) surfaceContainer.get()),
                     callback,
                     "onSurfaceDestroyed");
diff --git a/car/app/app/src/main/aidl/androidx/car/app/IOnSelectedListener.aidl b/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevel.java
similarity index 64%
copy from car/app/app/src/main/aidl/androidx/car/app/IOnSelectedListener.aidl
copy to car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevel.java
index 2896a83..0da9df6 100644
--- a/car/app/app/src/main/aidl/androidx/car/app/IOnSelectedListener.aidl
+++ b/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevel.java
@@ -14,11 +14,17 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.car.app.versioning;
 
-import androidx.car.app.IOnDoneCallback;
+import androidx.annotation.IntDef;
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /** @hide */
-oneway interface IOnSelectedListener {
-  void onSelected(int index, IOnDoneCallback callback) = 1;
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@IntDef(value = {CarAppApiLevels.LEVEL_1})
+@Retention(RetentionPolicy.SOURCE)
+public @interface CarAppApiLevel {
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java b/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java
new file mode 100644
index 0000000..1aeda53
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java
@@ -0,0 +1,77 @@
+/*
+ * 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.car.app.versioning;
+
+import androidx.annotation.RestrictTo;
+import androidx.car.app.CarContext;
+
+/**
+ * API levels supported by this library.
+ * <p>
+ * Each level denotes a set of elements (classes, fields and methods) known to both clients and
+ * hosts.
+ *
+ * @see CarContext#getCarAppApiLevel()
+ */
+public class CarAppApiLevels {
+
+    /**
+     * Initial API level.
+     * <p>
+     * Includes core API services and managers, and templates for parking,
+     * charging, and navigation apps.
+     */
+    @CarAppApiLevel
+    public static final int LEVEL_1 = 1;
+
+    /**
+     * Unknown API level. Used when the API level hasn't been established yet
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @CarAppApiLevel
+    public static final int UNKNOWN = 0;
+
+    /**
+     * @return true if the given integer is a valid {@link CarAppApiLevel}
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    public static boolean isValid(int carApiLevel) {
+        return carApiLevel >= getOldest() && carApiLevel <= getLatest();
+    }
+
+    /**
+     * Highest API level implemented by this library.
+     */
+    @CarAppApiLevel
+    public static int getLatest() {
+        return LEVEL_1;
+    }
+
+    /**
+     * Lowest API level implement to this library
+     */
+    @CarAppApiLevel
+    public static int getOldest() {
+        return LEVEL_1;
+    }
+
+    private CarAppApiLevels() {}
+}
diff --git a/car/app/app/src/test/java/androidx/car/app/AppInfoTest.java b/car/app/app/src/test/java/androidx/car/app/AppInfoTest.java
new file mode 100644
index 0000000..158e1fc
--- /dev/null
+++ b/car/app/app/src/test/java/androidx/car/app/AppInfoTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.car.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import androidx.car.app.versioning.CarAppApiLevels;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public class AppInfoTest {
+    @Mock
+    private Context mContext;
+    @Mock
+    private PackageManager mPackageManager;
+    private final ApplicationInfo mApplicationInfo = new ApplicationInfo();
+
+    @Before
+    public void setUp() throws PackageManager.NameNotFoundException {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.getApplicationInfo(isNull(), anyInt()))
+                .thenReturn(mApplicationInfo);
+    }
+
+    @Test
+    public void create_minApiLevel_nullMetadata_defaultsToCurrent() {
+        mApplicationInfo.metaData = null;
+        AppInfo appInfo = AppInfo.create(mContext);
+        assertThat(appInfo.getMinCarAppApiLevel()).isEqualTo(CarAppApiLevels.getLatest());
+    }
+
+    @Test
+    public void create_minApiLevel_noMetadataKey_defaultsToCurrent() {
+        mApplicationInfo.metaData = new Bundle();
+        AppInfo appInfo = AppInfo.create(mContext);
+        assertThat(appInfo.getMinCarAppApiLevel()).isEqualTo(CarAppApiLevels.getLatest());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void create_minApiLevel_cannotBeLowerThanOldest() {
+        int minApiLevel = CarAppApiLevels.getOldest() - 1;
+        mApplicationInfo.metaData = new Bundle();
+        mApplicationInfo.metaData.putInt(AppInfo.MIN_API_LEVEL_MANIFEST_KEY, minApiLevel);
+        AppInfo.create(mContext);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void create_minApiLevel_cannotBeHigherThanLatest() {
+        int minApiLevel = CarAppApiLevels.getLatest() + 1;
+        mApplicationInfo.metaData = new Bundle();
+        mApplicationInfo.metaData.putInt(AppInfo.MIN_API_LEVEL_MANIFEST_KEY, minApiLevel);
+        AppInfo.create(mContext);
+    }
+
+    @Test
+    public void retrieveMinApiLevel_isReadFromManifest() {
+        int minApiLevel = 123;
+        mApplicationInfo.metaData = new Bundle();
+        mApplicationInfo.metaData.putInt(AppInfo.MIN_API_LEVEL_MANIFEST_KEY, minApiLevel);
+        assertThat(AppInfo.retrieveMinCarAppApiLevel(mContext)).isEqualTo(minApiLevel);
+    }
+}
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/AppManagerTest.java b/car/app/app/src/test/java/androidx/car/app/AppManagerTest.java
similarity index 89%
rename from car/app/app/src/androidTest/java/androidx/car/app/AppManagerTest.java
rename to car/app/app/src/test/java/androidx/car/app/AppManagerTest.java
index 6dd5a7f..941c03e 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/AppManagerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/AppManagerTest.java
@@ -30,20 +30,19 @@
 import androidx.annotation.Nullable;
 import androidx.car.app.model.Template;
 import androidx.car.app.testing.TestCarContext;
-import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link AppManager}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public final class AppManagerTest {
     @Mock
     private ICarHost mMockCarHost;
@@ -58,7 +57,6 @@
     private AppManager mAppManager;
 
     @Before
-    @UiThreadTest
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
 
@@ -79,9 +77,9 @@
                     }
 
                     @Override
-                    public void setSurfaceListener(@Nullable ISurfaceListener surfaceListener)
+                    public void setSurfaceCallback(@Nullable ISurfaceCallback surfaceCallback)
                             throws RemoteException {
-                        mMockAppHost.setSurfaceListener(surfaceListener);
+                        mMockAppHost.setSurfaceCallback(surfaceCallback);
                     }
                 };
         when(mMockCarHost.getHost(any())).thenReturn(appHost.asBinder());
@@ -92,7 +90,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void getTemplate_serializationFails_throwsIllegalStateException()
             throws RemoteException {
         mTestCarContext
@@ -113,7 +110,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void invalidate_forwardsRequestToHost() throws RemoteException {
         mAppManager.invalidate();
 
@@ -121,7 +117,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void invalidate_hostThrowsRemoteException_throwsHostException() throws
             RemoteException {
         doThrow(new RemoteException()).when(mMockAppHost).invalidate();
@@ -130,7 +125,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void invalidate_hostThrowsRuntimeException_throwsHostException() throws
             RemoteException {
         doThrow(new IllegalStateException()).when(mMockAppHost).invalidate();
@@ -164,32 +158,32 @@
 
     @Test
     public void setSurfaceListener_forwardsRequestToHost() throws RemoteException {
-        mAppManager.setSurfaceListener(null);
+        mAppManager.setSurfaceCallback(null);
 
-        verify(mMockAppHost).setSurfaceListener(null);
+        verify(mMockAppHost).setSurfaceCallback(null);
     }
 
     @Test
     public void setSurfaceListener_hostThrowsSecurityException_throwsSecurityException()
             throws RemoteException {
-        doThrow(new SecurityException()).when(mMockAppHost).setSurfaceListener(any());
+        doThrow(new SecurityException()).when(mMockAppHost).setSurfaceCallback(any());
 
-        assertThrows(SecurityException.class, () -> mAppManager.setSurfaceListener(null));
+        assertThrows(SecurityException.class, () -> mAppManager.setSurfaceCallback(null));
     }
 
     @Test
     public void etSurfaceListener_hostThrowsRemoteException_throwsHostException()
             throws RemoteException {
-        doThrow(new RemoteException()).when(mMockAppHost).setSurfaceListener(any());
+        doThrow(new RemoteException()).when(mMockAppHost).setSurfaceCallback(any());
 
-        assertThrows(HostException.class, () -> mAppManager.setSurfaceListener(null));
+        assertThrows(HostException.class, () -> mAppManager.setSurfaceCallback(null));
     }
 
     @Test
     public void setSurfaceListener_hostThrowsRuntimeException_throwsHostException()
             throws RemoteException {
-        doThrow(new IllegalStateException()).when(mMockAppHost).setSurfaceListener(any());
+        doThrow(new IllegalStateException()).when(mMockAppHost).setSurfaceCallback(any());
 
-        assertThrows(HostException.class, () -> mAppManager.setSurfaceListener(null));
+        assertThrows(HostException.class, () -> mAppManager.setSurfaceCallback(null));
     }
 }
diff --git a/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java b/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java
new file mode 100644
index 0000000..2f8a585
--- /dev/null
+++ b/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java
@@ -0,0 +1,386 @@
+/*
+ * 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.car.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.PlaceListMapTemplate;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.NavigationManager;
+import androidx.car.app.serialization.Bundleable;
+import androidx.car.app.serialization.BundlerException;
+import androidx.car.app.testing.CarAppServiceController;
+import androidx.car.app.testing.TestCarContext;
+import androidx.car.app.versioning.CarAppApiLevels;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.Lifecycle;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+import java.util.Deque;
+import java.util.Locale;
+
+/** Tests for {@link CarAppService}. */
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public final class CarAppServiceTest {
+    @Mock
+    ICarHost mMockCarHost;
+    @Mock
+    DefaultLifecycleObserver mLifecycleObserver;
+
+    private TestCarContext mCarContext;
+    private final Template mTemplate =
+            PlaceListMapTemplate.builder()
+                    .setTitle("Title")
+                    .setItemList(ItemList.builder().build())
+                    .build();
+
+    private CarAppService mCarAppService;
+    private CarAppServiceController mCarAppServiceController;
+    private Intent mIntentSet;
+    @Captor
+    ArgumentCaptor<Bundleable> mBundleableArgumentCaptor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mCarContext = TestCarContext.createCarContext(
+                ApplicationProvider.getApplicationContext());
+        mCarAppService =
+                new CarAppService() {
+                    @Override
+                    @NonNull
+                    public Session onCreateSession() {
+                        Session testSession = createTestSession();
+                        CarAppServiceController.of(mCarContext, testSession, mCarAppService);
+                        return testSession;
+                    }
+                };
+
+        mCarAppService.onCreate();
+        mCarAppServiceController = CarAppServiceController.of(mCarContext, createTestSession(),
+                mCarAppService);
+    }
+
+    private Session createTestSession() {
+        return new Session() {
+            @NonNull
+            @Override
+            public Screen onCreateScreen(@NonNull Intent intent) {
+                mIntentSet = intent;
+                return new Screen(getCarContext()) {
+                    @Override
+                    @NonNull
+                    public Template onGetTemplate() {
+                        return mTemplate;
+                    }
+                };
+            }
+
+            @Override
+            public void onNewIntent(@NonNull Intent intent) {
+                mIntentSet = intent;
+            }
+        };
+    }
+
+    @Test
+    public void onAppCreate_updatesCarApiLevel() throws RemoteException, BundlerException {
+        String hostPackageName = "com.google.projection.gearhead";
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        int hostApiLevel = CarAppApiLevels.LEVEL_1;
+        HandshakeInfo handshakeInfo = new HandshakeInfo(hostPackageName, hostApiLevel);
+
+        mCarAppService.setCurrentSession(null);
+        carApp.onHandshakeCompleted(Bundleable.create(handshakeInfo), mock(IOnDoneCallback.class));
+        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+
+        assertThat(
+                mCarAppService.getCurrentSession().getCarContext().getCarAppApiLevel()).isEqualTo(
+                hostApiLevel);
+    }
+
+    @Test
+    public void onAppCreate_createsFirstScreen() throws RemoteException {
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+
+        assertThat(
+                mCarAppService
+                        .getCurrentSession()
+                        .getCarContext()
+                        .getCarService(ScreenManager.class)
+                        .getTopTemplate()
+                        .getTemplate())
+                .isInstanceOf(PlaceListMapTemplate.class);
+    }
+
+    @Test
+    public void onAppCreate_withIntent_callsWithOnCreateScreenWithIntent() throws
+            RemoteException {
+        IOnDoneCallback callback = mock(IOnDoneCallback.class);
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        Intent intent = new Intent("Foo");
+        carApp.onAppCreate(mMockCarHost, intent, new Configuration(), callback);
+
+        assertThat(mIntentSet).isEqualTo(intent);
+        verify(callback).onSuccess(any());
+    }
+
+    @Test
+    public void onAppCreate_alreadyPreviouslyCreated_callsOnNewIntent() throws RemoteException {
+        IOnDoneCallback callback = mock(IOnDoneCallback.class);
+
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        Intent intent = new Intent("Foo");
+        carApp.onAppCreate(mMockCarHost, intent, new Configuration(), callback);
+        verify(callback).onSuccess(any());
+
+        IOnDoneCallback callback2 = mock(IOnDoneCallback.class);
+        Intent intent2 = new Intent("Foo2");
+        carApp.onAppCreate(mMockCarHost, intent2, new Configuration(), callback2);
+
+        assertThat(mIntentSet).isEqualTo(intent2);
+        verify(callback2).onSuccess(any());
+    }
+
+    @Test
+    public void onAppCreate_updatesTheConfiguration() throws RemoteException {
+        Configuration configuration = new Configuration();
+        configuration.setToDefaults();
+        configuration.setLocale(Locale.CANADA_FRENCH);
+
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        carApp.onAppCreate(mMockCarHost, null, configuration, mock(IOnDoneCallback.class));
+
+        assertThat(mCarContext.getResources().getConfiguration().getLocales().get(0))
+                .isEqualTo(Locale.CANADA_FRENCH);
+    }
+
+    @Test
+    public void onNewIntent_callsOnNewIntentWithIntent() throws RemoteException {
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        Intent intent = new Intent("Foo");
+        carApp.onAppCreate(mMockCarHost, intent, new Configuration(), mock(IOnDoneCallback.class));
+
+        Intent intent2 = new Intent("Foo2");
+        carApp.onNewIntent(intent2, mock(IOnDoneCallback.class));
+
+        assertThat(mIntentSet).isEqualTo(intent2);
+    }
+
+    @Test
+    public void getNavigationManager() throws RemoteException {
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+
+        assertThat(mCarAppService.getCurrentSession().getCarContext().getCarService(
+                NavigationManager.class)).isNotNull();
+    }
+
+    @Test
+    public void onConfigurationChanged_updatesTheConfiguration() throws RemoteException {
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+
+        Configuration configuration = new Configuration();
+        configuration.setToDefaults();
+        configuration.setLocale(Locale.CANADA_FRENCH);
+
+        carApp.onConfigurationChanged(configuration, mock(IOnDoneCallback.class));
+
+        assertThat(mCarContext.getResources().getConfiguration().getLocales().get(0))
+                .isEqualTo(Locale.CANADA_FRENCH);
+    }
+
+    @Test
+    public void getAppInfo() throws RemoteException, BundlerException {
+        AppInfo appInfo = new AppInfo(3, 4, "foo");
+        mCarAppServiceController.setAppInfo(appInfo);
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        IOnDoneCallback callback = mock(IOnDoneCallback.class);
+
+        carApp.getAppInfo(callback);
+
+        verify(callback).onSuccess(mBundleableArgumentCaptor.capture());
+        AppInfo receivedAppInfo = (AppInfo) mBundleableArgumentCaptor.getValue().get();
+        assertThat(receivedAppInfo.getMinCarAppApiLevel())
+                .isEqualTo(appInfo.getMinCarAppApiLevel());
+        assertThat(receivedAppInfo.getLatestCarAppApiLevel())
+                .isEqualTo(appInfo.getLatestCarAppApiLevel());
+        assertThat(receivedAppInfo.getLibraryDisplayVersion()).isEqualTo(
+                appInfo.getLibraryDisplayVersion());
+    }
+
+    @Test
+    public void onHandshakeCompleted_updatesHostInfo() throws RemoteException, BundlerException {
+        String hostPackageName = "com.google.projection.gearhead";
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        HandshakeInfo handshakeInfo = new HandshakeInfo(hostPackageName, CarAppApiLevels.LEVEL_1);
+
+        carApp.onHandshakeCompleted(Bundleable.create(handshakeInfo), mock(IOnDoneCallback.class));
+
+        assertThat(mCarAppService.getHostInfo().getPackageName()).isEqualTo(hostPackageName);
+    }
+
+    @Test
+    public void onHandshakeCompleted_updatesHandshakeInfo() throws RemoteException,
+            BundlerException {
+        String hostPackageName = "com.google.projection.gearhead";
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        assertThat(mCarAppService.getHandshakeInfo()).isNull();
+
+        HandshakeInfo handshakeInfo = new HandshakeInfo(hostPackageName, CarAppApiLevels.LEVEL_1);
+        carApp.onHandshakeCompleted(Bundleable.create(handshakeInfo), mock(IOnDoneCallback.class));
+        assertThat(mCarAppService.getHandshakeInfo()).isNotNull();
+        assertThat(mCarAppService.getHandshakeInfo().getHostCarAppApiLevel()).isEqualTo(
+                handshakeInfo.getHostCarAppApiLevel());
+        assertThat(mCarAppService.getHandshakeInfo().getHostPackageName()).isEqualTo(
+                handshakeInfo.getHostPackageName());
+    }
+
+    @Test
+    public void onHandshakeCompleted_lowerThanMinApiLevel_throws() throws BundlerException,
+            RemoteException {
+        AppInfo appInfo = new AppInfo(3, 4, "foo");
+        mCarAppServiceController.setAppInfo(appInfo);
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+
+        HandshakeInfo handshakeInfo = new HandshakeInfo("bar",
+                appInfo.getMinCarAppApiLevel() - 1);
+        IOnDoneCallback callback = mock(IOnDoneCallback.class);
+        carApp.onHandshakeCompleted(Bundleable.create(handshakeInfo), callback);
+
+        verify(callback).onFailure(any());
+    }
+
+    @Test
+    public void onHandshakeCompleted_higherThanCurrentApiLevel_throws() throws BundlerException,
+            RemoteException {
+        AppInfo appInfo = new AppInfo(3, 4, "foo");
+        mCarAppServiceController.setAppInfo(appInfo);
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+
+        HandshakeInfo handshakeInfo = new HandshakeInfo("bar",
+                appInfo.getLatestCarAppApiLevel() + 1);
+        IOnDoneCallback callback = mock(IOnDoneCallback.class);
+        carApp.onHandshakeCompleted(Bundleable.create(handshakeInfo), callback);
+
+        verify(callback).onFailure(any());
+    }
+
+    @Test
+    public void onUnbind_movesLifecycleStateToDestroyed() throws RemoteException {
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+        carApp.onAppStart(mock(IOnDoneCallback.class));
+
+        mCarAppService.getCurrentSession().getLifecycle().addObserver(mLifecycleObserver);
+
+        assertThat(mCarAppService.onUnbind(null)).isTrue();
+
+        verify(mLifecycleObserver).onDestroy(any());
+    }
+
+    @Test
+    public void onUnbind_rebind_callsOnCreateScreen() throws RemoteException, BundlerException {
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+
+        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+        carApp.onAppStart(mock(IOnDoneCallback.class));
+
+        Session currentSession = mCarAppService.getCurrentSession();
+        currentSession.getLifecycle().addObserver(mLifecycleObserver);
+        assertThat(mCarAppService.onUnbind(null)).isTrue();
+
+        verify(mLifecycleObserver).onStop(any());
+
+        assertThat(currentSession.getCarContext().getCarService(
+                ScreenManager.class).getScreenStack()).isEmpty();
+
+        String hostPackageName = "com.google.projection.gearhead";
+        int hostApiLevel = CarAppApiLevels.LEVEL_1;
+        HandshakeInfo handshakeInfo = new HandshakeInfo(hostPackageName, hostApiLevel);
+        carApp.onHandshakeCompleted(Bundleable.create(handshakeInfo), mock(IOnDoneCallback.class));
+        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+        assertThat(currentSession.getCarContext().getCarService(
+                ScreenManager.class).getScreenStack()).hasSize(1);
+    }
+
+    @Test
+    public void onUnbind_clearsScreenStack() throws RemoteException {
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+
+        Deque<Screen> screenStack =
+                mCarAppService.getCurrentSession().getCarContext().getCarService(
+                        ScreenManager.class).getScreenStack();
+        assertThat(screenStack).hasSize(1);
+
+        Screen screen = screenStack.getFirst();
+        assertThat(screen.getLifecycle().getCurrentState()).isAtLeast(Lifecycle.State.CREATED);
+
+        mCarAppService.onUnbind(null);
+
+        assertThat(screenStack).isEmpty();
+        assertThat(screen.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.DESTROYED);
+    }
+
+    @Test
+    public void finish() throws RemoteException {
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+
+        mCarAppService.getCurrentSession().getCarContext().finishCarApp();
+
+        assertThat(mCarContext.hasCalledFinishCarApp()).isTrue();
+    }
+
+    @Test
+    public void onNewIntent_callsSessionIntent() throws
+            RemoteException {
+        assertThat(mIntentSet).isNull();
+
+        IOnDoneCallback callback = mock(IOnDoneCallback.class);
+        ICarApp carApp = (ICarApp) mCarAppService.onBind(null);
+        Intent intent = new Intent("Foo");
+        carApp.onNewIntent(intent, callback);
+
+        assertThat(mIntentSet).isEqualTo(intent);
+        verify(callback).onSuccess(any());
+    }
+}
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/CarContextTest.java b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
similarity index 73%
rename from car/app/app/src/androidTest/java/androidx/car/app/CarContextTest.java
rename to car/app/app/src/test/java/androidx/car/app/CarContextTest.java
index ba52f42..a0d08c2 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/CarContextTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
@@ -21,6 +21,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -35,31 +36,31 @@
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 
+import androidx.activity.OnBackPressedCallback;
 import androidx.annotation.Nullable;
 import androidx.car.app.navigation.NavigationManager;
-import androidx.car.app.test.R;
 import androidx.car.app.testing.TestLifecycleOwner;
 import androidx.lifecycle.Lifecycle.Event;
-import androidx.test.annotation.UiThreadTest;
+import androidx.lifecycle.Lifecycle.State;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.Locale;
 
 /** Tests for {@link CarContext}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class CarContextTest {
-    private static final String APP_SERVICE = "app_manager";
-    private static final String NAVIGATION_SERVICE = "navigation_manager";
-    private static final String SCREEN_MANAGER_SERVICE = "screen_manager";
+    private static final String APP_SERVICE = "app";
+    private static final String NAVIGATION_SERVICE = "navigation";
+    private static final String SCREEN_SERVICE = "screen";
 
     @Mock
     private ICarHost mMockCarHost;
@@ -79,7 +80,6 @@
             new TestLifecycleOwner();
 
     @Before
-    @UiThreadTest
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
         when(mMockCarHost.getHost(CarContext.APP_SERVICE))
@@ -94,14 +94,14 @@
                             }
 
                             @Override
-                            public void setSurfaceListener(@Nullable ISurfaceListener listener) {
+                            public void setSurfaceCallback(@Nullable ISurfaceCallback callback) {
                             }
                         }.asBinder());
 
         TestStartCarAppStub startCarAppStub = new TestStartCarAppStub(mMockStartCarApp);
 
         Bundle extras = new Bundle(1);
-        extras.putBinder(CarContext.START_CAR_APP_BINDER_KEY, startCarAppStub.asBinder());
+        extras.putBinder(CarContext.EXTRA_START_CAR_APP_BINDER_KEY, startCarAppStub.asBinder());
         mIntentFromNotification = new Intent().putExtras(extras);
 
         mCarContext = CarContext.create(mLifecycleOwner.mRegistry);
@@ -130,9 +130,9 @@
 
     @Test
     public void getCarService_screenManager() {
-        assertThat(mCarContext.getCarService(CarContext.SCREEN_MANAGER_SERVICE))
+        assertThat(mCarContext.getCarService(CarContext.SCREEN_SERVICE))
                 .isEqualTo(mCarContext.getCarService(ScreenManager.class));
-        assertThat(mCarContext.getCarService(CarContext.SCREEN_MANAGER_SERVICE)).isNotNull();
+        assertThat(mCarContext.getCarService(CarContext.SCREEN_SERVICE)).isNotNull();
     }
 
     @Test
@@ -161,7 +161,7 @@
     @Test
     public void getCarServiceName_screenManager() {
         assertThat(mCarContext.getCarServiceName(ScreenManager.class)).isEqualTo(
-                SCREEN_MANAGER_SERVICE);
+                SCREEN_SERVICE);
     }
 
     @Test
@@ -210,7 +210,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void onConfigurationChanged_updatesTheConfiguration() {
         Configuration configuration = new Configuration();
         configuration.setToDefaults();
@@ -223,14 +222,13 @@
     }
 
     @Test
-    @UiThreadTest
     public void onConfigurationChanged_loadsCorrectNewResource() {
         Configuration ldpiConfig = new Configuration(mCarContext.getResources().getConfiguration());
         ldpiConfig.densityDpi = 120;
 
         mCarContext.onCarConfigurationChanged(ldpiConfig);
 
-        Drawable ldpiDrawable = mCarContext.getDrawable(R.drawable.banana);
+        Drawable ldpiDrawable = TestUtils.getTestDrawable(mCarContext, "banana");
         assertThat(ldpiDrawable.getIntrinsicHeight()).isEqualTo(48);
 
         Configuration mdpiConfig = new Configuration(mCarContext.getResources().getConfiguration());
@@ -238,7 +236,7 @@
 
         mCarContext.onCarConfigurationChanged(mdpiConfig);
 
-        Drawable mdpiDrawable = mCarContext.getDrawable(R.drawable.banana);
+        Drawable mdpiDrawable = TestUtils.getTestDrawable(mCarContext, "banana");
         assertThat(mdpiDrawable.getIntrinsicHeight()).isEqualTo(64);
 
         Configuration hdpiConfig = new Configuration(mCarContext.getResources().getConfiguration());
@@ -246,12 +244,11 @@
 
         mCarContext.onCarConfigurationChanged(hdpiConfig);
 
-        Drawable hdpiDrawable = mCarContext.getDrawable(R.drawable.banana);
+        Drawable hdpiDrawable = TestUtils.getTestDrawable(mCarContext, "banana");
         assertThat(hdpiDrawable.getIntrinsicHeight()).isEqualTo(96);
     }
 
     @Test
-    @UiThreadTest
     // TODO(rampara): Investigate removing usage of deprecated updateConfiguration API
     @SuppressWarnings("deprecation")
     public void changingApplicationContextConfiguration_doesNotChangeTheCarContextConfiguration() {
@@ -260,7 +257,7 @@
 
         mCarContext.onCarConfigurationChanged(ldpiConfig);
 
-        Drawable ldpiDrawable = mCarContext.getDrawable(R.drawable.banana);
+        Drawable ldpiDrawable = TestUtils.getTestDrawable(mCarContext, "banana");
         assertThat(ldpiDrawable.getIntrinsicHeight()).isEqualTo(48);
 
         Configuration mdpiConfig = new Configuration(mCarContext.getResources().getConfiguration());
@@ -272,15 +269,15 @@
                 .updateConfiguration(mdpiConfig,
                         applicationContext.getResources().getDisplayMetrics());
 
-        Drawable carContextDrawable = mCarContext.getDrawable(R.drawable.banana);
+        Drawable carContextDrawable = TestUtils.getTestDrawable(mCarContext, "banana");
         assertThat(carContextDrawable.getIntrinsicHeight()).isEqualTo(48);
 
-        Drawable applicationContextDrawable = applicationContext.getDrawable(R.drawable.banana);
+        Drawable applicationContextDrawable = TestUtils.getTestDrawable(applicationContext,
+                "banana");
         assertThat(applicationContextDrawable.getIntrinsicHeight()).isEqualTo(64);
     }
 
     @Test
-    @UiThreadTest
     // TODO(rampara): Investigate removing usage of deprecated updateConfiguration API
     @SuppressWarnings("deprecation")
     public void changingApplicationContextDisplayMetrics_doesNotChangeCarContextDisplayMetrics() {
@@ -289,7 +286,7 @@
 
         mCarContext.onCarConfigurationChanged(ldpiConfig);
 
-        Drawable ldpiDrawable = mCarContext.getDrawable(R.drawable.banana);
+        Drawable ldpiDrawable = TestUtils.getTestDrawable(mCarContext, "banana");
         assertThat(ldpiDrawable.getIntrinsicHeight()).isEqualTo(48);
 
         Configuration mdpiConfig = new Configuration(mCarContext.getResources().getConfiguration());
@@ -311,9 +308,8 @@
         applicationContext.getResources().updateConfiguration(mdpiConfig, newDisplayMetrics);
 
         assertThat(applicationContext.getResources().getConfiguration()).isEqualTo(mdpiConfig);
-        // TODO(rampara): Investigate why DisplayMetrics isn't updated
-//        assertThat(applicationContext.getResources().getDisplayMetrics()).isEqualTo(
-//                newDisplayMetrics);
+        assertThat(applicationContext.getResources().getDisplayMetrics()).isEqualTo(
+                newDisplayMetrics);
 
         assertThat(mCarContext.getResources().getConfiguration()).isNotEqualTo(mdpiConfig);
         assertThat(mCarContext.getResources().getDisplayMetrics()).isNotEqualTo(newDisplayMetrics);
@@ -352,60 +348,58 @@
         verify(mMockScreen2).dispatchLifecycleEvent(Event.ON_DESTROY);
     }
 
-    // TODO(rampara): Investigate how to mock final methods
-//    @Test
-//    public void
-//    getOnBackPressedDispatcher_withAListenerThatIsStarted_callsTheListenerAndDoesNotPop() {
-//        mCarContext.getCarService(ScreenManager.class).push(mScreen1);
-//        mCarContext.getCarService(ScreenManager.class).push(mScreen2);
-//
-//        OnBackPressedCallback callback = mock(OnBackPressedCallback.class);
-//        when(callback.isEnabled()).thenReturn(true);
-//        mLifecycleOwner.mRegistry.setCurrentState(State.STARTED);
-//
-//        mCarContext.getOnBackPressedDispatcher().addCallback(mLifecycleOwner, callback);
-//        mCarContext.getOnBackPressedDispatcher().onBackPressed();
-//
-//        verify(callback).handleOnBackPressed();
-//        verify(mMockScreen1, never()).dispatchLifecycleEvent(Event.ON_DESTROY);
-//        verify(mMockScreen2, never()).dispatchLifecycleEvent(Event.ON_DESTROY);
-//    }
-//
-//    @Test
-//    public void getOnBackPressedDispatcher_withAListenerThatIsNotStarted_popsAScreen() {
-//        mCarContext.getCarService(ScreenManager.class).push(mScreen1);
-//        mCarContext.getCarService(ScreenManager.class).push(mScreen2);
-//
-//        OnBackPressedCallback callback = mock(OnBackPressedCallback.class);
-//        when(callback.isEnabled()).thenReturn(true);
-//        mLifecycleOwner.mRegistry.setCurrentState(State.CREATED);
-//
-//        mCarContext.getOnBackPressedDispatcher().addCallback(mLifecycleOwner, callback);
-//        mCarContext.getOnBackPressedDispatcher().onBackPressed();
-//
-//        verify(callback, never()).handleOnBackPressed();
-//        verify(mMockScreen1, never()).dispatchLifecycleEvent(Lifecycle.Event.ON_DESTROY);
-//        verify(mMockScreen2).dispatchLifecycleEvent(Lifecycle.Event.ON_DESTROY);
-//    }
-//
-//    @Test
-//    public void getOnBackPressedDispatcher_callsDefaultListenerWheneverTheAddedOneIsNotSTARTED() {
-//        mCarContext.getCarService(ScreenManager.class).push(mScreen1);
-//        mCarContext.getCarService(ScreenManager.class).push(mScreen2);
-//
-//        OnBackPressedCallback callback = mock(OnBackPressedCallback.class);
-//        when(callback.isEnabled()).thenReturn(true);
-//        mLifecycleOwner.mRegistry.setCurrentState(State.CREATED);
-//
-//        mCarContext.getOnBackPressedDispatcher().addCallback(mLifecycleOwner, callback);
-//        mCarContext.getOnBackPressedDispatcher().onBackPressed();
-//
-//        verify(callback, never()).handleOnBackPressed();
-//        verify(mMockScreen2).dispatchLifecycleEvent(Event.ON_DESTROY);
-//
-//        mLifecycleOwner.mRegistry.setCurrentState(State.STARTED);
-//        mCarContext.getOnBackPressedDispatcher().onBackPressed();
-//
-//        verify(callback).handleOnBackPressed();
-//    }
+    @Test
+    public void getOnBackPressedDispatcher_withListenerThatIsStarted_callsListenerAndDoesNotPop() {
+        mCarContext.getCarService(ScreenManager.class).push(mScreen1);
+        mCarContext.getCarService(ScreenManager.class).push(mScreen2);
+
+        OnBackPressedCallback callback = mock(OnBackPressedCallback.class);
+        when(callback.isEnabled()).thenReturn(true);
+        mLifecycleOwner.mRegistry.setCurrentState(State.STARTED);
+
+        mCarContext.getOnBackPressedDispatcher().addCallback(mLifecycleOwner, callback);
+        mCarContext.getOnBackPressedDispatcher().onBackPressed();
+
+        verify(callback).handleOnBackPressed();
+        verify(mMockScreen1, never()).dispatchLifecycleEvent(Event.ON_DESTROY);
+        verify(mMockScreen2, never()).dispatchLifecycleEvent(Event.ON_DESTROY);
+    }
+
+    @Test
+    public void getOnBackPressedDispatcher_withAListenerThatIsNotStarted_popsAScreen() {
+        mCarContext.getCarService(ScreenManager.class).push(mScreen1);
+        mCarContext.getCarService(ScreenManager.class).push(mScreen2);
+
+        OnBackPressedCallback callback = mock(OnBackPressedCallback.class);
+        when(callback.isEnabled()).thenReturn(true);
+        mLifecycleOwner.mRegistry.setCurrentState(State.CREATED);
+
+        mCarContext.getOnBackPressedDispatcher().addCallback(mLifecycleOwner, callback);
+        mCarContext.getOnBackPressedDispatcher().onBackPressed();
+
+        verify(callback, never()).handleOnBackPressed();
+        verify(mMockScreen1, never()).dispatchLifecycleEvent(Event.ON_DESTROY);
+        verify(mMockScreen2).dispatchLifecycleEvent(Event.ON_DESTROY);
+    }
+
+    @Test
+    public void getOnBackPressedDispatcher_callsDefaultListenerWheneverTheAddedOneIsNotSTARTED() {
+        mCarContext.getCarService(ScreenManager.class).push(mScreen1);
+        mCarContext.getCarService(ScreenManager.class).push(mScreen2);
+
+        OnBackPressedCallback callback = mock(OnBackPressedCallback.class);
+        when(callback.isEnabled()).thenReturn(true);
+        mLifecycleOwner.mRegistry.setCurrentState(State.CREATED);
+
+        mCarContext.getOnBackPressedDispatcher().addCallback(mLifecycleOwner, callback);
+        mCarContext.getOnBackPressedDispatcher().onBackPressed();
+
+        verify(callback, never()).handleOnBackPressed();
+        verify(mMockScreen2).dispatchLifecycleEvent(Event.ON_DESTROY);
+
+        mLifecycleOwner.mRegistry.setCurrentState(State.STARTED);
+        mCarContext.getOnBackPressedDispatcher().onBackPressed();
+
+        verify(callback).handleOnBackPressed();
+    }
 }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/CarToastTest.java b/car/app/app/src/test/java/androidx/car/app/CarToastTest.java
similarity index 92%
rename from car/app/app/src/androidTest/java/androidx/car/app/CarToastTest.java
rename to car/app/app/src/test/java/androidx/car/app/CarToastTest.java
index 42f0e70..3ea0e15 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/CarToastTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarToastTest.java
@@ -22,23 +22,21 @@
 
 import androidx.car.app.testing.TestAppManager;
 import androidx.car.app.testing.TestCarContext;
-import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link CarToast}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public final class CarToastTest {
     private TestCarContext mCarContext;
 
     @Before
-    @UiThreadTest
     public void setUp() {
         mCarContext = TestCarContext.createCarContext(ApplicationProvider.getApplicationContext());
         mCarContext.reset();
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/HandshakeInfoTest.java b/car/app/app/src/test/java/androidx/car/app/HandshakeInfoTest.java
similarity index 77%
rename from car/app/app/src/androidTest/java/androidx/car/app/HandshakeInfoTest.java
rename to car/app/app/src/test/java/androidx/car/app/HandshakeInfoTest.java
index 40b39c8..a367e7a 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/HandshakeInfoTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/HandshakeInfoTest.java
@@ -18,20 +18,21 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public final class HandshakeInfoTest {
 
     @Test
     public void construct_handshakeInfo() {
         String hostPackageName = "com.google.host";
-        HandshakeInfo handshakeInfo = new HandshakeInfo(hostPackageName);
+        int hostApiLevel = 123;
+        HandshakeInfo handshakeInfo = new HandshakeInfo(hostPackageName, hostApiLevel);
         assertThat(handshakeInfo.getHostPackageName()).isEqualTo(hostPackageName);
+        assertThat(handshakeInfo.getHostCarAppApiLevel()).isEqualTo(hostApiLevel);
     }
 }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/HostDispatcherTest.java b/car/app/app/src/test/java/androidx/car/app/HostDispatcherTest.java
similarity index 94%
rename from car/app/app/src/androidTest/java/androidx/car/app/HostDispatcherTest.java
rename to car/app/app/src/test/java/androidx/car/app/HostDispatcherTest.java
index 3e5bc5c..f0abe7e 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/HostDispatcherTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/HostDispatcherTest.java
@@ -29,19 +29,19 @@
 import androidx.annotation.Nullable;
 import androidx.car.app.navigation.INavigationHost;
 import androidx.car.app.serialization.Bundleable;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link HostDispatcher}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class HostDispatcherTest {
 
     @Mock
@@ -55,7 +55,6 @@
     private HostDispatcher mHostDispatcher = new HostDispatcher();
 
     @Before
-    @UiThreadTest
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
 
@@ -73,9 +72,9 @@
                     }
 
                     @Override
-                    public void setSurfaceListener(@Nullable ISurfaceListener surfaceListener)
+                    public void setSurfaceCallback(@Nullable ISurfaceCallback surfaceCallback)
                             throws RemoteException {
-                        mMockAppHost.setSurfaceListener(surfaceListener);
+                        mMockAppHost.setSurfaceCallback(surfaceCallback);
                     }
                 };
 
@@ -149,7 +148,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void getHost_afterResetting_getsFromCarHost() throws RemoteException {
         assertThat(mHostDispatcher.getHost(CarContext.APP_SERVICE)).isEqualTo(mAppHost);
 
@@ -211,7 +209,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void getHost_afterReset_throwsHostException() {
         mHostDispatcher.resetHosts();
 
@@ -219,7 +216,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void getHost_notBound_throwsHostException() {
         mHostDispatcher = new HostDispatcher();
 
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/ScreenManagerTest.java b/car/app/app/src/test/java/androidx/car/app/ScreenManagerTest.java
similarity index 96%
rename from car/app/app/src/androidTest/java/androidx/car/app/ScreenManagerTest.java
rename to car/app/app/src/test/java/androidx/car/app/ScreenManagerTest.java
index 38432dd..aceb9cb 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/ScreenManagerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/ScreenManagerTest.java
@@ -34,10 +34,7 @@
 import androidx.car.app.testing.TestLifecycleOwner;
 import androidx.lifecycle.Lifecycle.Event;
 import androidx.lifecycle.Lifecycle.State;
-import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -45,10 +42,12 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link ScreenManager}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public final class ScreenManagerTest {
 
     private TestScreen mScreen1;
@@ -61,7 +60,7 @@
     @Mock
     private Screen mMockScreen3;
     @Mock
-    private OnScreenResultCallback mOnScreenResultCallback;
+    private OnScreenResultListener mOnScreenResultListener;
 
     @Mock
     private AppManager mMockAppManager;
@@ -71,7 +70,6 @@
     private ScreenManager mScreenManager;
 
     @Before
-    @UiThreadTest
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
@@ -131,7 +129,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void push_stackHadScreen_addsToStack_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockAppManager);
@@ -158,7 +155,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void push_screenAlreadyInStack_movesToTop_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockAppManager);
@@ -194,7 +190,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void push_screenAlreadyTopOfStack_noFurtherLifecycleCalls() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockAppManager);
@@ -223,14 +218,13 @@
     }
 
     @Test
-    @UiThreadTest
     public void pushForResult_addsToStack_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockAppManager,
-                mOnScreenResultCallback);
+                mOnScreenResultListener);
 
         mScreenManager.push(mScreen1);
-        mScreenManager.pushForResult(mScreen2, mOnScreenResultCallback);
+        mScreenManager.pushForResult(mScreen2, mOnScreenResultListener);
 
         inOrder.verify(mMockScreen1).dispatchLifecycleEvent(Event.ON_CREATE);
         inOrder.verify(mMockAppManager).invalidate();
@@ -251,32 +245,30 @@
     }
 
     @Test
-    @UiThreadTest
     public void pushForResult_screenSetsResult_firstScreenGetsCalled() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
 
         mScreenManager.push(mScreen1);
-        mScreenManager.pushForResult(mScreen2, mOnScreenResultCallback);
+        mScreenManager.pushForResult(mScreen2, mOnScreenResultListener);
 
         String result = "done";
         mScreen2.setResult(result);
 
-        verify(mOnScreenResultCallback, never()).onScreenResult(any());
+        verify(mOnScreenResultListener, never()).onScreenResult(any());
 
         mScreenManager.remove(mScreen2);
 
-        verify(mOnScreenResultCallback).onScreenResult(result);
+        verify(mOnScreenResultListener).onScreenResult(result);
     }
 
     @Test
-    @UiThreadTest
     public void pushForResult_screenSetsResult_firstScreenGetsCalled_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockAppManager,
-                mOnScreenResultCallback);
+                mOnScreenResultListener);
 
         mScreenManager.push(mScreen1);
-        mScreenManager.pushForResult(mScreen2, mOnScreenResultCallback);
+        mScreenManager.pushForResult(mScreen2, mOnScreenResultListener);
 
         String result = "foo";
         mScreen2.setResult(result);
@@ -305,7 +297,7 @@
         inOrder.verify(mMockScreen2).dispatchLifecycleEvent(Event.ON_PAUSE);
         inOrder.verify(mMockScreen2).dispatchLifecycleEvent(Event.ON_STOP);
 
-        inOrder.verify(mOnScreenResultCallback).onScreenResult(result);
+        inOrder.verify(mOnScreenResultListener).onScreenResult(result);
         inOrder.verify(mMockScreen2).dispatchLifecycleEvent(Event.ON_DESTROY);
         inOrder.verify(mMockScreen1).dispatchLifecycleEvent(Event.ON_RESUME);
 
@@ -347,7 +339,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void pop_removes_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockAppManager);
@@ -445,7 +436,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void popTo_pops_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockAppManager);
@@ -486,7 +476,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void popTo_markerScreenNotInStack_popsToRoot_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockScreen3, mMockAppManager);
@@ -537,7 +526,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void popTo_multipleToPop_pops_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockScreen3, mMockAppManager);
@@ -591,7 +579,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void popTo_notARootTarget_popsExpectedScreens_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockScreen3, mMockAppManager);
@@ -647,7 +634,7 @@
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockAppManager);
 
-        mScreenManager.popTo(Screen.ROOT);
+        mScreenManager.popToRoot();
 
         inOrder.verifyNoMoreInteractions();
 
@@ -661,7 +648,7 @@
 
         mScreenManager.push(mScreen1);
 
-        mScreenManager.popTo(Screen.ROOT);
+        mScreenManager.popToRoot();
 
         // Pushing screen1
         inOrder.verify(mMockScreen1).dispatchLifecycleEvent(Event.ON_CREATE);
@@ -675,7 +662,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void popToRoot_pops_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockAppManager);
@@ -683,7 +669,7 @@
         mScreenManager.push(mScreen1);
         mScreenManager.push(mScreen2);
 
-        mScreenManager.popTo(Screen.ROOT);
+        mScreenManager.popToRoot();
 
         // Pushing screen1
         inOrder.verify(mMockScreen1).dispatchLifecycleEvent(Event.ON_CREATE);
@@ -713,7 +699,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void popToRoot_multipleToPop_pops_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockScreen3, mMockAppManager);
@@ -722,7 +707,7 @@
         mScreenManager.push(mScreen2);
         mScreenManager.push(mScreen3);
 
-        mScreenManager.popTo(Screen.ROOT);
+        mScreenManager.popToRoot();
 
         // Pushing screen1
         inOrder.verify(mMockScreen1).dispatchLifecycleEvent(Event.ON_CREATE);
@@ -764,24 +749,23 @@
     }
 
     @Test
-    @UiThreadTest
     public void popTo_multipleToPop_withResult_pops_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder =
                 inOrder(mMockScreen1, mMockScreen2, mMockScreen3, mMockAppManager,
-                        mOnScreenResultCallback);
+                        mOnScreenResultListener);
 
         mScreenManager.push(mScreen1);
 
-        mScreenManager.pushForResult(mScreen2, mOnScreenResultCallback);
+        mScreenManager.pushForResult(mScreen2, mOnScreenResultListener);
         Object result1 = "foo";
         mScreen2.setResult(result1);
 
-        mScreenManager.pushForResult(mScreen3, mOnScreenResultCallback);
+        mScreenManager.pushForResult(mScreen3, mOnScreenResultListener);
         Object result2 = "bar";
         mScreen3.setResult(result2);
 
-        mScreenManager.popTo(Screen.ROOT);
+        mScreenManager.popToRoot();
 
         // Pushing screen1
         inOrder.verify(mMockScreen1).dispatchLifecycleEvent(Event.ON_CREATE);
@@ -811,10 +795,10 @@
 
         inOrder.verify(mMockScreen3).dispatchLifecycleEvent(Event.ON_PAUSE);
         inOrder.verify(mMockScreen3).dispatchLifecycleEvent(Event.ON_STOP);
-        inOrder.verify(mOnScreenResultCallback).onScreenResult(result2);
+        inOrder.verify(mOnScreenResultListener).onScreenResult(result2);
         inOrder.verify(mMockScreen3).dispatchLifecycleEvent(Event.ON_DESTROY);
 
-        inOrder.verify(mOnScreenResultCallback).onScreenResult(result1);
+        inOrder.verify(mOnScreenResultListener).onScreenResult(result1);
         inOrder.verify(mMockScreen2).dispatchLifecycleEvent(Event.ON_DESTROY);
 
         inOrder.verify(mMockScreen1).dispatchLifecycleEvent(Event.ON_RESUME);
@@ -858,7 +842,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void remove_wasTop_removes_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockAppManager);
@@ -897,7 +880,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void remove_wasNotTop_removes_callsProperLifecycleMethods() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockAppManager);
@@ -930,7 +912,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void remove_notInStack_noop() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         InOrder inOrder = inOrder(mMockScreen1, mMockScreen2, mMockScreen3, mMockAppManager);
@@ -960,7 +941,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void getTopTemplate_returnsTemplateFromTopOfStack() {
         Template template =
                 PlaceListMapTemplate.builder()
@@ -993,7 +973,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void dispatchAppLifecycleEvent_onStart_expectedLifecycleChange() {
         mScreenManager.push(mScreen1);
         reset(mMockScreen1);
@@ -1009,7 +988,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void dispatchAppLifecycleEvent_onResume_expectedLifecycleChange() {
         mScreenManager.push(mScreen1);
         reset(mMockScreen1);
@@ -1025,7 +1003,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void dispatchAppLifecycleEvent_onPause_expectedLifecycleChange() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         mScreenManager.push(mScreen1);
@@ -1042,7 +1019,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void dispatchAppLifecycleEvent_onStop_expectedLifecycleChange() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         mScreenManager.push(mScreen1);
@@ -1067,7 +1043,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void dispatchAppLifecycleEvent_onDestroy_screenStopped_onlyDestroys() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         mScreenManager.push(mScreen1);
@@ -1083,7 +1058,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void dispatchAppLifecycleEvent_onDestroy_screenPaused_stopsAndDestroys() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         mScreenManager.push(mScreen1);
@@ -1100,7 +1074,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void dispatchAppLifecycleEvent_onDestroy_screenResumed_pausesStopsAndDestroys() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         mScreenManager.push(mScreen1);
@@ -1117,7 +1090,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void dispatchAppLifecycleEvent_onDestroy_pausesStopsAndDestroysTop_destroysOthers() {
         mLifecycleOwner.mRegistry.handleLifecycleEvent(Event.ON_RESUME);
         mScreenManager.push(mScreen1);
@@ -1140,7 +1112,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void pop_screenReuseLastTemplateId() {
         Template template =
                 PlaceListMapTemplate.builder()
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/ScreenTest.java b/car/app/app/src/test/java/androidx/car/app/ScreenTest.java
similarity index 88%
rename from car/app/app/src/androidTest/java/androidx/car/app/ScreenTest.java
rename to car/app/app/src/test/java/androidx/car/app/ScreenTest.java
index 7ab1650..807337c 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/ScreenTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/ScreenTest.java
@@ -31,30 +31,28 @@
 import androidx.car.app.testing.TestScreenManager;
 import androidx.lifecycle.Lifecycle.Event;
 import androidx.lifecycle.Lifecycle.State;
-import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link Screen}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public final class ScreenTest {
     private TestCarContext mCarContext;
 
     @Mock
-    OnScreenResultCallback mMockOnScreenResultCallback;
+    OnScreenResultListener mMockOnScreenResultListener;
 
     private Screen mScreen;
 
     @Before
-    @UiThreadTest
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mCarContext =
@@ -87,7 +85,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void finish_removesSelf() {
         mScreen.finish();
         assertThat(mCarContext.getCarService(TestScreenManager.class).getScreensRemoved())
@@ -95,14 +92,12 @@
     }
 
     @Test
-    @UiThreadTest
     public void onCreate_expectedLifecycleChange() {
         mScreen.dispatchLifecycleEvent(Event.ON_CREATE);
         assertThat(mScreen.getLifecycle().getCurrentState()).isEqualTo(State.CREATED);
     }
 
     @Test
-    @UiThreadTest
     public void onStart_expectedLifecycleChange() {
         mScreen.dispatchLifecycleEvent(Event.ON_START);
         assertThat(mScreen.getLifecycle().getCurrentState()).isEqualTo(State.STARTED);
@@ -115,43 +110,38 @@
     }
 
     @Test
-    @UiThreadTest
     public void onPause_expectedLifecycleChange() {
         mScreen.dispatchLifecycleEvent(Event.ON_PAUSE);
         assertThat(mScreen.getLifecycle().getCurrentState()).isEqualTo(State.STARTED);
     }
 
     @Test
-    @UiThreadTest
     public void onStop_expectedLifecycleChange() {
         mScreen.dispatchLifecycleEvent(Event.ON_STOP);
         assertThat(mScreen.getLifecycle().getCurrentState()).isEqualTo(State.CREATED);
     }
 
     @Test
-    @UiThreadTest
     public void onDestroy_expectedLifecycleChange() {
         mScreen.dispatchLifecycleEvent(Event.ON_DESTROY);
         assertThat(mScreen.getLifecycle().getCurrentState()).isEqualTo(State.DESTROYED);
     }
 
     @Test
-    @UiThreadTest
     public void setResult_callsThemockOnScreenResultCallback() {
-        mScreen.setOnResultCallback(mMockOnScreenResultCallback);
+        mScreen.setOnScreenResultListener(mMockOnScreenResultListener);
 
         String foo = "yo";
         mScreen.setResult(foo);
 
-        verify(mMockOnScreenResultCallback, never()).onScreenResult(any());
+        verify(mMockOnScreenResultListener, never()).onScreenResult(any());
 
         mScreen.dispatchLifecycleEvent(Event.ON_DESTROY);
 
-        verify(mMockOnScreenResultCallback).onScreenResult(foo);
+        verify(mMockOnScreenResultListener).onScreenResult(foo);
     }
 
     @Test
-    @UiThreadTest
     public void finish_screenIsDestroyed() {
         mScreen.finish();
         assertThat(mScreen.getLifecycle().getCurrentState()).isEqualTo(State.DESTROYED);
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/TestData.java b/car/app/app/src/test/java/androidx/car/app/TestData.java
similarity index 100%
rename from car/app/app/src/androidTest/java/androidx/car/app/TestData.java
rename to car/app/app/src/test/java/androidx/car/app/TestData.java
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/TestScreen.java b/car/app/app/src/test/java/androidx/car/app/TestScreen.java
similarity index 100%
rename from car/app/app/src/androidTest/java/androidx/car/app/TestScreen.java
rename to car/app/app/src/test/java/androidx/car/app/TestScreen.java
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/TestUtils.java b/car/app/app/src/test/java/androidx/car/app/TestUtils.java
similarity index 90%
rename from car/app/app/src/androidTest/java/androidx/car/app/TestUtils.java
rename to car/app/app/src/test/java/androidx/car/app/TestUtils.java
index d946284..7c6f6cc 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/TestUtils.java
+++ b/car/app/app/src/test/java/androidx/car/app/TestUtils.java
@@ -27,8 +27,10 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
 import android.text.SpannableString;
 
+import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -42,6 +44,7 @@
 import androidx.car.app.model.Pane;
 import androidx.car.app.model.Row;
 import androidx.car.app.model.SectionedItemList;
+import androidx.core.graphics.drawable.IconCompat;
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -59,6 +62,20 @@
     private TestUtils() {
     }
 
+    public static Drawable getTestDrawable(Context context, String drawable) {
+        return context.getDrawable(getTestDrawableResId(context, drawable));
+    }
+
+    public static CarIcon getTestCarIcon(Context context, String drawable) {
+        return CarIcon.of(IconCompat.createWithResource(context,
+                TestUtils.getTestDrawableResId(context, drawable)));
+    }
+
+    @DrawableRes
+    public static int getTestDrawableResId(Context context, String drawable) {
+        return context.getResources().getIdentifier(drawable, "drawable", context.getPackageName());
+    }
+
     /**
      * Returns a {@link DateTimeWithZone} instance from a date string and a time zone id.
      *
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/ActionStripTest.java b/car/app/app/src/test/java/androidx/car/app/model/ActionStripTest.java
similarity index 95%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/ActionStripTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/ActionStripTest.java
index b0a3a200..778db2d 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/ActionStripTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ActionStripTest.java
@@ -20,15 +20,14 @@
 
 import static org.junit.Assert.assertThrows;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link ActionStrip}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class ActionStripTest {
     @Test
     public void createEmpty_throws() {
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/ActionTest.java b/car/app/app/src/test/java/androidx/car/app/model/ActionTest.java
similarity index 82%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/ActionTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/ActionTest.java
index 93a2562..1535bf9 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/ActionTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ActionTest.java
@@ -23,16 +23,14 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.ContentResolver;
+import android.content.Context;
 import android.net.Uri;
 
 import androidx.car.app.IOnDoneCallback;
 import androidx.car.app.OnDoneCallback;
-import androidx.car.app.test.R;
+import androidx.car.app.TestUtils;
 import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -40,10 +38,12 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link Action}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class ActionTest {
     @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
@@ -79,23 +79,20 @@
         assertThrows(
                 IllegalArgumentException.class,
                 () -> Action.builder()
-                                .setTitle("foo")
-                                .setOnClickListener(onClickListener)
-                                .setBackgroundColor(CarColor.createCustom(0xdead, 0xbeef))
-                                .build());
+                        .setTitle("foo")
+                        .setOnClickListener(onClickListener)
+                        .setBackgroundColor(CarColor.createCustom(0xdead, 0xbeef))
+                        .build());
     }
 
     @Test
     public void create_noTitleDefault() {
         OnClickListener onClickListener = mock(OnClickListener.class);
         Action action = Action.builder()
-                        .setIcon(
-                                CarIcon.of(
-                                        IconCompat.createWithResource(
-                                                ApplicationProvider.getApplicationContext(),
-                                                R.drawable.ic_test_1)))
-                        .setOnClickListener(onClickListener)
-                        .build();
+                .setIcon(TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                        "ic_test_1"))
+                .setOnClickListener(onClickListener)
+                .build();
         assertThat(action.getTitle()).isNull();
     }
 
@@ -116,19 +113,18 @@
     }
 
     @Test
-    @UiThreadTest
     public void createInstance() {
         OnClickListener onClickListener = mock(OnClickListener.class);
-        IconCompat icon =
-                IconCompat.createWithResource(
-                        ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1);
+        Context context = ApplicationProvider.getApplicationContext();
+        IconCompat icon = IconCompat.createWithResource(
+                context, TestUtils.getTestDrawableResId(context, "ic_test_1"));
         String title = "foo";
         Action action = Action.builder()
-                        .setTitle(title)
-                        .setIcon(CarIcon.of(icon))
-                        .setBackgroundColor(CarColor.BLUE)
-                        .setOnClickListener(onClickListener)
-                        .build();
+                .setTitle(title)
+                .setIcon(CarIcon.of(icon))
+                .setBackgroundColor(CarColor.BLUE)
+                .setOnClickListener(onClickListener)
+                .build();
         assertThat(icon).isEqualTo(action.getIcon().getIcon());
         assertThat(CarText.create(title)).isEqualTo(action.getTitle());
         assertThat(CarColor.BLUE).isEqualTo(action.getBackgroundColor());
@@ -199,9 +195,9 @@
         CarIcon icon2 = CarIcon.APP_ICON;
 
         Action action1 = Action.builder().setOnClickListener(() -> {
-                }).setTitle(title).setIcon(icon1).build();
+        }).setTitle(title).setIcon(icon1).build();
         Action action2 = Action.builder().setOnClickListener(() -> {
-                }).setTitle(title).setIcon(icon2).build();
+        }).setTitle(title).setIcon(icon2).build();
 
         assertThat(action2).isNotEqualTo(action1);
     }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/CarIconSpanTest.java b/car/app/app/src/test/java/androidx/car/app/model/CarIconSpanTest.java
similarity index 84%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/CarIconSpanTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/CarIconSpanTest.java
index 2439237..0237996 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/CarIconSpanTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/CarIconSpanTest.java
@@ -21,29 +21,30 @@
 import static org.junit.Assert.assertThrows;
 
 import android.content.ContentResolver;
+import android.content.Context;
 import android.net.Uri;
 
-import androidx.car.app.test.R;
+import androidx.car.app.TestUtils;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link CarIconSpan}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class CarIconSpanTest {
     private IconCompat mIcon;
 
     @Before
     public void setup() {
-        mIcon =
-                IconCompat.createWithResource(
-                        ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1);
+        Context context = ApplicationProvider.getApplicationContext();
+        mIcon = IconCompat.createWithResource(
+                context, TestUtils.getTestDrawableResId(context, "ic_test_1"));
     }
 
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/CarIconTest.java b/car/app/app/src/test/java/androidx/car/app/model/CarIconTest.java
similarity index 87%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/CarIconTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/CarIconTest.java
index 1152c7e..e6a33ab 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/CarIconTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/CarIconTest.java
@@ -28,32 +28,33 @@
 import static org.junit.Assert.assertThrows;
 
 import android.content.ContentResolver;
+import android.content.Context;
 import android.graphics.Bitmap;
 import android.net.Uri;
 
-import androidx.car.app.test.R;
+import androidx.car.app.TestUtils;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.io.File;
 
 /** Tests for {@link CarIcon}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class CarIconTest {
     private IconCompat mIcon;
 
     @Before
     public void setup() {
-        mIcon =
-                IconCompat.createWithResource(
-                        ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1);
+        Context context = ApplicationProvider.getApplicationContext();
+        mIcon = IconCompat.createWithResource(
+                context, TestUtils.getTestDrawableResId(context, "ic_test_1"));
     }
 
     @Test
@@ -142,11 +143,10 @@
     public void equals() {
         assertThat(BACK.equals(BACK)).isTrue();
         CarIcon carIcon = CarIcon.of(mIcon);
+        Context context = ApplicationProvider.getApplicationContext();
 
-        assertThat(
-                CarIcon.of(
-                        IconCompat.createWithResource(
-                                ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1)))
+        assertThat(CarIcon.of(IconCompat.createWithResource(
+                context, TestUtils.getTestDrawableResId(context, "ic_test_1"))))
                 .isEqualTo(carIcon);
     }
 
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/DateTimeWithZoneTest.java b/car/app/app/src/test/java/androidx/car/app/model/DateTimeWithZoneTest.java
similarity index 85%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/DateTimeWithZoneTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/DateTimeWithZoneTest.java
index 1844d51..43bdbf0 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/DateTimeWithZoneTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/DateTimeWithZoneTest.java
@@ -24,12 +24,10 @@
 
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SdkSuppress;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.time.Duration;
 import java.time.ZonedDateTime;
@@ -38,8 +36,8 @@
 import java.util.TimeZone;
 
 /** Tests for {@link DateTimeWithZone}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class DateTimeWithZoneTest {
     @Test
     @SuppressWarnings("JdkObsolete")
@@ -73,36 +71,29 @@
         // Negative time.
         assertThrows(
                 IllegalArgumentException.class,
-                () -> {
-                    DateTimeWithZone.create(-1, (int) timeZoneOffsetSeconds, zoneShortName);
-                });
+                () -> DateTimeWithZone.create(-1, (int) timeZoneOffsetSeconds, zoneShortName));
 
         // Offset out of range.
         assertThrows(
                 IllegalArgumentException.class,
-                () -> {
-                    DateTimeWithZone.create(timeSinceEpochMillis, 18 * 60 * 60 + 1, zoneShortName);
-                });
+                () -> DateTimeWithZone.create(timeSinceEpochMillis, 18 * 60 * 60 + 1,
+                        zoneShortName));
         assertThrows(
                 IllegalArgumentException.class,
-                () -> {
-                    DateTimeWithZone.create(timeSinceEpochMillis, -18 * 60 * 60 - 1, zoneShortName);
-                });
+                () -> DateTimeWithZone.create(timeSinceEpochMillis, -18 * 60 * 60 - 1,
+                        zoneShortName));
 
         // Null short name.
         assertThrows(
                 NullPointerException.class,
-                () -> {
-                    DateTimeWithZone.create(timeSinceEpochMillis, (int) timeZoneOffsetSeconds,
-                            null);
-                });
+                () -> DateTimeWithZone.create(timeSinceEpochMillis, (int) timeZoneOffsetSeconds,
+                        null));
 
         // Empty short name.
         assertThrows(
                 IllegalArgumentException.class,
-                () -> {
-                    DateTimeWithZone.create(timeSinceEpochMillis, (int) timeZoneOffsetSeconds, "");
-                });
+                () -> DateTimeWithZone.create(timeSinceEpochMillis, (int) timeZoneOffsetSeconds,
+                        ""));
     }
 
     @Test
@@ -131,20 +122,15 @@
         // Negative time.
         assertThrows(
                 IllegalArgumentException.class,
-                () -> {
-                    DateTimeWithZone.create(-1, timeZone);
-                });
+                () -> DateTimeWithZone.create(-1, timeZone));
 
         // Null time zone.
         assertThrows(
                 NullPointerException.class,
-                () -> {
-                    DateTimeWithZone.create(123, null);
-                });
+                () -> DateTimeWithZone.create(123, null));
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 26)
     public void create_withZonedDateTime() {
         ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-05-14T19:57:00-07:00[US/Pacific]");
         DateTimeWithZone dateTimeWithZone = DateTimeWithZone.create(zonedDateTime);
@@ -153,7 +139,6 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 26)
     public void create_withZonedDateTime_argumentChecks() {
         // Null date time.
         assertThrows(
@@ -164,7 +149,6 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 26)
     public void equals() {
         TimeZone timeZone = TimeZone.getTimeZone("US/Pacific");
         long timeSinceEpochMillis = System.currentTimeMillis();
@@ -183,7 +167,6 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 26)
     public void notEquals_differentTimeSinceEpoch() {
         TimeZone timeZone = TimeZone.getTimeZone("US/Pacific");
         long timeSinceEpochMillis = System.currentTimeMillis();
@@ -203,7 +186,6 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 26)
     public void notEquals_differentTimeZoneOffsetSeconds() {
         TimeZone timeZone = TimeZone.getTimeZone("US/Pacific");
         long timeSinceEpochMillis = System.currentTimeMillis();
@@ -223,7 +205,6 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 26)
     public void notEquals_differentTimeZone() {
         TimeZone timeZone = TimeZone.getTimeZone("US/Pacific");
         long timeSinceEpochMillis = System.currentTimeMillis();
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/DistanceSpanTest.java b/car/app/app/src/test/java/androidx/car/app/model/DistanceSpanTest.java
similarity index 90%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/DistanceSpanTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/DistanceSpanTest.java
index a05bed7..704770b 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/DistanceSpanTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/DistanceSpanTest.java
@@ -18,15 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link DistanceSpan}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class DistanceSpanTest {
     private final Distance mDistance =
             Distance.create(/* displayDistance= */ 10, Distance.UNIT_KILOMETERS);
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/DistanceTest.java b/car/app/app/src/test/java/androidx/car/app/model/DistanceTest.java
similarity index 93%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/DistanceTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/DistanceTest.java
index 91c7028..4ebe6fb 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/DistanceTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/DistanceTest.java
@@ -24,15 +24,14 @@
 
 import static org.junit.Assert.assertThrows;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link Distance}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class DistanceTest {
 
     private static final double DISPLAY_DISTANCE = 1.2d;
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/DurationSpanTest.java b/car/app/app/src/test/java/androidx/car/app/model/DurationSpanTest.java
similarity index 88%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/DurationSpanTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/DurationSpanTest.java
index e43f2e1..cce7162 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/DurationSpanTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/DurationSpanTest.java
@@ -18,15 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link DurationSpan}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class DurationSpanTest {
     @Test
     public void constructor() {
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/ForegroundCarColorSpanTest.java b/car/app/app/src/test/java/androidx/car/app/model/ForegroundCarColorSpanTest.java
similarity index 91%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/ForegroundCarColorSpanTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/ForegroundCarColorSpanTest.java
index 4f320f0..a3b92a1 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/ForegroundCarColorSpanTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ForegroundCarColorSpanTest.java
@@ -23,15 +23,14 @@
 
 import static org.junit.Assert.assertThrows;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link CarIconSpan}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class ForegroundCarColorSpanTest {
     @Test
     public void constructor() {
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/GridItemTest.java b/car/app/app/src/test/java/androidx/car/app/model/GridItemTest.java
similarity index 69%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/GridItemTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/GridItemTest.java
index 68e0a3f..da6e46d 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/GridItemTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/GridItemTest.java
@@ -28,29 +28,34 @@
 import android.os.RemoteException;
 
 import androidx.car.app.OnDoneCallback;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link GridItem}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class GridItemTest {
 
     @Test
     public void create_defaultValues() {
-        GridItem gridItem = GridItem.builder().setImage(BACK).build();
+        GridItem gridItem = GridItem.builder().setTitle("Title").setImage(BACK).build();
 
         assertThat(BACK).isEqualTo(gridItem.getImage());
         assertThat(gridItem.getImageType()).isEqualTo(GridItem.IMAGE_TYPE_LARGE);
-        assertThat(gridItem.getTitle()).isNull();
+        assertThat(gridItem.getTitle()).isNotNull();
         assertThat(gridItem.getText()).isNull();
     }
 
     @Test
+    public void create_isLoading() {
+        GridItem gridItem = GridItem.builder().setTitle("Title").setLoading(true).build();
+        assertThat(gridItem.isLoading()).isTrue();
+    }
+
+    @Test
     public void title_charSequence() {
         String title = "foo";
         GridItem gridItem = GridItem.builder().setTitle(title).setImage(BACK).build();
@@ -59,6 +64,17 @@
     }
 
     @Test
+    public void title_throwsIfNotSet() {
+        // Not set
+        assertThrows(IllegalStateException.class, () -> GridItem.builder().setImage(BACK).build());
+
+        // Not set
+        assertThrows(
+                IllegalArgumentException.class, () -> GridItem.builder().setTitle("").setImage(
+                        BACK).build());
+    }
+
+    @Test
     public void text_charSequence() {
         String text = "foo";
         GridItem gridItem = GridItem.builder().setTitle("title").setText(text).setImage(
@@ -75,7 +91,14 @@
     }
 
     @Test
-    public void create_noImage_throwsException() {
+    public void setIsLoading_contentsSet_throws() {
+        assertThrows(
+                IllegalStateException.class,
+                () -> GridItem.builder().setLoading(true).setTitle("foo").setImage(BACK).build());
+    }
+
+    @Test
+    public void create_noImage_throws() {
         assertThrows(IllegalStateException.class, () -> GridItem.builder().setTitle("foo").build());
     }
 
@@ -110,55 +133,21 @@
 
     @Test
     public void notEquals_differentImage() {
-        GridItem gridItem = GridItem.builder().setImage(BACK).build();
+        GridItem gridItem = GridItem.builder().setTitle("Title").setImage(BACK).build();
 
-        assertThat(GridItem.builder().setImage(ALERT).build()).isNotEqualTo(gridItem);
-    }
-
-    @Test
-    public void notEquals_differentToggle() {
-        Toggle toggle1 = Toggle.builder(isChecked -> {
-        }).setChecked(true).build();
-        Toggle toggle2 = Toggle.builder(isChecked -> {
-        }).setChecked(false).build();
-        GridItem gridItem = GridItem.builder().setImage(BACK).setToggle(toggle1).build();
-
-        assertThat(GridItem.builder().setImage(BACK).setToggle(toggle2).build()).isNotEqualTo(
+        assertThat(GridItem.builder().setImage(ALERT).setTitle("Title").build()).isNotEqualTo(
                 gridItem);
     }
 
     @Test
-    @UiThreadTest
     public void clickListener() throws RemoteException {
         OnClickListener onClickListener = mock(OnClickListener.class);
         GridItem gridItem =
-                GridItem.builder().setImage(BACK).setOnClickListener(onClickListener).build();
+                GridItem.builder().setTitle("Title").setImage(BACK).setOnClickListener(
+                        onClickListener).build();
         OnDoneCallback onDoneCallback = mock(OnDoneCallback.class);
         gridItem.getOnClickListener().onClick(onDoneCallback);
         verify(onClickListener).onClick();
         verify(onDoneCallback).onSuccess(null);
     }
-
-    @Test
-    public void setToggle() {
-        Toggle toggle = Toggle.builder(isChecked -> {
-        }).build();
-        GridItem gridItem = GridItem.builder().setImage(BACK).setToggle(toggle).build();
-        assertThat(toggle).isEqualTo(gridItem.getToggle());
-    }
-
-    @Test
-    public void setOnClickListenerAndToggle_throws() {
-        Toggle toggle = Toggle.builder(isChecked -> {
-        }).build();
-        assertThrows(
-                IllegalStateException.class,
-                () ->
-                        GridItem.builder()
-                                .setImage(BACK)
-                                .setOnClickListener(() -> {
-                                })
-                                .setToggle(toggle)
-                                .build());
-    }
 }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/GridTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/model/GridTemplateTest.java
similarity index 95%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/GridTemplateTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/GridTemplateTest.java
index 5548996..6078ee3 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/GridTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/GridTemplateTest.java
@@ -23,15 +23,15 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.car.app.TestUtils;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link GridTemplate}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class GridTemplateTest {
     @Test
     public void createInstance_emptyList_notLoading_throws() {
@@ -162,7 +162,7 @@
         ItemList itemList = ItemList.builder().build();
 
         GridTemplate template =
-                GridTemplate.builder().setTitle("Title").setSingleList(itemList).build();
+                GridTemplate.builder().setTitle("Title 1").setSingleList(itemList).build();
 
         assertThat(template)
                 .isNotEqualTo(
@@ -170,7 +170,8 @@
                                 .setTitle("Title")
                                 .setSingleList(
                                         ItemList.builder().addItem(
-                                                GridItem.builder().setImage(BACK).build()).build())
+                                                GridItem.builder().setTitle("Title 2").setImage(
+                                                        BACK).build()).build())
                                 .build());
     }
 
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/ItemListTest.java b/car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java
similarity index 96%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/ItemListTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java
index 90df854..e306d1b 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/ItemListTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java
@@ -35,9 +35,6 @@
 import androidx.car.app.WrappedRuntimeException;
 import androidx.car.app.model.ItemList.OnItemVisibilityChangedListener;
 import androidx.car.app.model.ItemList.OnSelectedListener;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -48,12 +45,14 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.Collections;
 
 /** Tests for {@link ItemListTest}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class ItemListTest {
     @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
@@ -85,8 +84,8 @@
 
     @Test
     public void createGridItems() {
-        GridItem gridItem1 = GridItem.builder().setImage(BACK).build();
-        GridItem gridItem2 = GridItem.builder().setImage(BACK).build();
+        GridItem gridItem1 = GridItem.builder().setTitle("title 1").setImage(BACK).build();
+        GridItem gridItem2 = GridItem.builder().setTitle("title 2").setImage(BACK).build();
         ItemList list = builder().addItem(gridItem1).addItem(gridItem2).build();
 
         assertThat(list.getItems()).containsExactly(gridItem1, gridItem2).inOrder();
@@ -114,7 +113,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void setSelectable() throws RemoteException {
         OnSelectedListener mockListener = mock(OnSelectedListener.class);
         ItemList itemList =
@@ -163,7 +161,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void setOnItemVisibilityChangeListener_triggerListener() {
         OnItemVisibilityChangedListener listener = mock(OnItemVisibilityChangedListener.class);
         ItemList list =
@@ -185,7 +182,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void setOnItemVisibilityChangeListener_triggerListenerWithFailure() {
         OnItemVisibilityChangedListener listener = mock(OnItemVisibilityChangedListener.class);
         ItemList list =
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/LatLngTest.java b/car/app/app/src/test/java/androidx/car/app/model/LatLngTest.java
similarity index 91%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/LatLngTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/LatLngTest.java
index c170d65..86b24de 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/LatLngTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/LatLngTest.java
@@ -18,15 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link LatLng}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class LatLngTest {
     @Test
     public void createInstance() {
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/ListTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/model/ListTemplateTest.java
similarity index 98%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/ListTemplateTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/ListTemplateTest.java
index 7bf4812..30d52b0 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/ListTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ListTemplateTest.java
@@ -20,15 +20,14 @@
 
 import static org.junit.Assert.assertThrows;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link ListTemplate}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class ListTemplateTest {
     @Test
     public void createInstance_emptyList_notLoading_Throws() {
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/MessageTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/model/MessageTemplateTest.java
similarity index 97%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/MessageTemplateTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/MessageTemplateTest.java
index 2d6a0d4..eb791af 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/MessageTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/MessageTemplateTest.java
@@ -27,17 +27,17 @@
 import android.util.Log;
 
 import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import com.google.common.collect.ImmutableList;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link MessageTemplate}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class MessageTemplateTest {
 
     private final String mTitle = "header";
@@ -82,7 +82,7 @@
         assertThat(template.getTitle().getText()).isEqualTo("header");
         assertThat(template.getIcon()).isNull();
         assertThat(template.getHeaderAction()).isNull();
-        assertThat(template.getActionList()).isNull();
+        assertThat(template.getActions()).isNull();
         assertThat(template.getDebugMessage()).isNull();
     }
 
@@ -121,7 +121,7 @@
                 Log.getStackTraceString(exception));
         assertThat(template.getIcon()).isEqualTo(icon);
         assertThat(template.getHeaderAction()).isEqualTo(Action.BACK);
-        assertThat(template.getActionList().getList()).containsExactly(action);
+        assertThat(template.getActions().getList()).containsExactly(action);
     }
 
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/MetadataTest.java b/car/app/app/src/test/java/androidx/car/app/model/MetadataTest.java
similarity index 92%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/MetadataTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/MetadataTest.java
index a67ff2d..e3310f11 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/MetadataTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/MetadataTest.java
@@ -18,15 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for the {@link Metadata} class. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class MetadataTest {
     @Test
     public void setAndGetPlace() {
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/ModelUtilsTest.java b/car/app/app/src/test/java/androidx/car/app/model/ModelUtilsTest.java
similarity index 79%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/ModelUtilsTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/ModelUtilsTest.java
index 4a82bff..bbf190f 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/ModelUtilsTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ModelUtilsTest.java
@@ -20,26 +20,24 @@
 
 import android.text.SpannableString;
 
-import androidx.car.app.test.R;
-import androidx.core.graphics.drawable.IconCompat;
+import androidx.car.app.TestUtils;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import com.google.common.collect.ImmutableList;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link PlaceListMapTemplate}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class ModelUtilsTest {
     @Test
     public void validateAllNonBrowsableRowsHaveDistances() {
-        DistanceSpan span =
-                DistanceSpan.create(
-                        Distance.create(/* displayDistance= */ 1, Distance.UNIT_KILOMETERS_P1));
+        DistanceSpan span = DistanceSpan.create(
+                Distance.create(/* displayDistance= */ 1, Distance.UNIT_KILOMETERS_P1));
         SpannableString stringWithDistance = new SpannableString("Test");
         stringWithDistance.setSpan(span, /* start= */ 0, /* end= */ 1, /* flags= */ 0);
         SpannableString stringWithInvalidDistance = new SpannableString("Test");
@@ -56,14 +54,12 @@
 
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        ModelUtils.validateAllNonBrowsableRowsHaveDistance(
-                                ImmutableList.of(rowWithDistance, rowWithInvalidDistance)));
+                () -> ModelUtils.validateAllNonBrowsableRowsHaveDistance(
+                        ImmutableList.of(rowWithDistance, rowWithInvalidDistance)));
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        ModelUtils.validateAllNonBrowsableRowsHaveDistance(
-                                ImmutableList.of(rowWithDistance, rowWithoutDistance)));
+                () -> ModelUtils.validateAllNonBrowsableRowsHaveDistance(
+                        ImmutableList.of(rowWithDistance, rowWithoutDistance)));
 
         // Positive cases
         ModelUtils.validateAllNonBrowsableRowsHaveDistance(ImmutableList.of());
@@ -102,14 +98,12 @@
 
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        ModelUtils.validateAllRowsHaveDistanceOrDuration(
-                                ImmutableList.of(rowWithDuration, rowWithInvalidDuration)));
+                () -> ModelUtils.validateAllRowsHaveDistanceOrDuration(
+                        ImmutableList.of(rowWithDuration, rowWithInvalidDuration)));
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        ModelUtils.validateAllRowsHaveDistanceOrDuration(
-                                ImmutableList.of(rowWithDuration, plainRow)));
+                () -> ModelUtils.validateAllRowsHaveDistanceOrDuration(
+                        ImmutableList.of(rowWithDuration, plainRow)));
 
         // Positive cases.
         ModelUtils.validateAllRowsHaveDistanceOrDuration(ImmutableList.of());
@@ -123,10 +117,8 @@
 
     @Test
     public void validateAllRowsHaveOnlySmallSizedImages() {
-        CarIcon carIcon =
-                CarIcon.of(
-                        IconCompat.createWithResource(
-                                ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1));
+        CarIcon carIcon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
         Row rowWithNoImage = Row.builder().setTitle("title1").build();
         Row rowWithSmallImage =
                 Row.builder().setTitle("title2").setImage(carIcon, Row.IMAGE_TYPE_SMALL).build();
@@ -139,9 +131,8 @@
                         ImmutableList.of(rowWithLargeImage)));
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        ModelUtils.validateAllRowsHaveOnlySmallImages(
-                                ImmutableList.of(rowWithNoImage, rowWithLargeImage)));
+                () -> ModelUtils.validateAllRowsHaveOnlySmallImages(
+                        ImmutableList.of(rowWithNoImage, rowWithLargeImage)));
 
         // Positive cases
         ModelUtils.validateAllRowsHaveOnlySmallImages(ImmutableList.of());
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/OnClickListenerWrapperTest.java b/car/app/app/src/test/java/androidx/car/app/model/OnClickListenerWrapperTest.java
similarity index 88%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/OnClickListenerWrapperTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/OnClickListenerWrapperTest.java
index 5db449f..3afb7ee 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/OnClickListenerWrapperTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/OnClickListenerWrapperTest.java
@@ -24,9 +24,6 @@
 import static org.mockito.Mockito.verify;
 
 import androidx.car.app.OnDoneCallback;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -34,9 +31,11 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class OnClickListenerWrapperTest {
     @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
@@ -45,7 +44,6 @@
     OnClickListener mMockOnClickListener;
 
     @Test
-    @UiThreadTest
     public void create() {
         OnClickListenerWrapper wrapper = OnClickListenerWrapperImpl.create(mMockOnClickListener);
         assertThat(wrapper.isParkedOnly()).isFalse();
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/PaneTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/model/PaneTemplateTest.java
similarity index 97%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/PaneTemplateTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/PaneTemplateTest.java
index f2ffb88..80f0bf3 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/PaneTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/PaneTemplateTest.java
@@ -21,15 +21,15 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.car.app.TestUtils;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link PaneTemplate}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class PaneTemplateTest {
 
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/PaneTest.java b/car/app/app/src/test/java/androidx/car/app/model/PaneTest.java
similarity index 95%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/PaneTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/PaneTest.java
index a7bd7aa..9fdb2f3 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/PaneTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/PaneTest.java
@@ -20,20 +20,19 @@
 
 import static org.junit.Assert.assertThrows;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import com.google.common.collect.ImmutableList;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.Arrays;
 import java.util.List;
 
 /** Tests for {@link Pane}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class PaneTest {
     @Test
     public void createEmptyRows_throws() {
@@ -78,7 +77,7 @@
         Pane pane =
                 Pane.builder().addRow(Row.builder().setTitle("Title").build()).setActions(
                         actions).build();
-        assertActions(pane.getActionList(), actions);
+        assertActions(pane.getActions(), actions);
     }
 
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/ParkedOnlyOnClickListenerTest.java b/car/app/app/src/test/java/androidx/car/app/model/ParkedOnlyOnClickListenerTest.java
similarity index 90%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/ParkedOnlyOnClickListenerTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/ParkedOnlyOnClickListenerTest.java
index 8b9d03a..d871996 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/ParkedOnlyOnClickListenerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ParkedOnlyOnClickListenerTest.java
@@ -24,9 +24,6 @@
 import android.os.RemoteException;
 
 import androidx.car.app.OnDoneCallback;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -34,10 +31,12 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link OnClickListenerWrapper}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class ParkedOnlyOnClickListenerTest {
     @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
@@ -46,7 +45,6 @@
     OnClickListener mMockOnClickListener;
 
     @Test
-    @UiThreadTest
     public void create() throws RemoteException {
         ParkedOnlyOnClickListener parkedOnlyOnClickListener =
                 ParkedOnlyOnClickListener.create(mMockOnClickListener);
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/PlaceListMapTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/model/PlaceListMapTemplateTest.java
similarity index 98%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/PlaceListMapTemplateTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/PlaceListMapTemplateTest.java
index 20ce3a7..d590b0b 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/PlaceListMapTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/PlaceListMapTemplateTest.java
@@ -25,15 +25,15 @@
 
 import androidx.car.app.TestUtils;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link PlaceListMapTemplate}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class PlaceListMapTemplateTest {
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private final DistanceSpan mDistanceSpan =
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/PlaceMarkerTest.java b/car/app/app/src/test/java/androidx/car/app/model/PlaceMarkerTest.java
similarity index 67%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/PlaceMarkerTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/PlaceMarkerTest.java
index 3e7cc36..41a25e1 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/PlaceMarkerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/PlaceMarkerTest.java
@@ -26,18 +26,18 @@
 import android.content.ContentResolver;
 import android.net.Uri;
 
-import androidx.car.app.test.R;
+import androidx.car.app.TestUtils;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link PlaceMarker}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class PlaceMarkerTest {
 
     @Test
@@ -48,17 +48,14 @@
 
     @Test
     public void setColor_withImageTypeIcon_throws() {
-        CarIcon icon =
-                CarIcon.of(
-                        IconCompat.createWithResource(
-                                ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1));
+        CarIcon icon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
         assertThrows(
                 IllegalStateException.class,
-                () ->
-                        PlaceMarker.builder()
-                                .setIcon(icon, PlaceMarker.TYPE_IMAGE)
-                                .setColor(CarColor.SECONDARY)
-                                .build());
+                () -> PlaceMarker.builder()
+                        .setIcon(icon, PlaceMarker.TYPE_IMAGE)
+                        .setColor(CarColor.SECONDARY)
+                        .build());
     }
 
     @Test
@@ -75,16 +72,13 @@
 
     @Test
     public void createInstance() {
-        CarIcon icon =
-                CarIcon.of(
-                        IconCompat.createWithResource(
-                                ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1));
-        PlaceMarker marker1 =
-                PlaceMarker.builder()
-                        .setIcon(icon, PlaceMarker.TYPE_ICON)
-                        .setLabel("foo")
-                        .setColor(CarColor.SECONDARY)
-                        .build();
+        CarIcon icon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
+        PlaceMarker marker1 = PlaceMarker.builder()
+                .setIcon(icon, PlaceMarker.TYPE_ICON)
+                .setLabel("foo")
+                .setColor(CarColor.SECONDARY)
+                .build();
         assertThat(marker1.getIcon()).isEqualTo(icon);
         assertThat(marker1.getIconType()).isEqualTo(PlaceMarker.TYPE_ICON);
         assertThat(marker1.getColor()).isEqualTo(CarColor.SECONDARY);
@@ -103,23 +97,19 @@
 
     @Test
     public void equals() {
-        CarIcon carIcon =
-                CarIcon.of(
-                        IconCompat.createWithResource(
-                                ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1));
-        PlaceMarker marker =
-                PlaceMarker.builder()
-                        .setIcon(carIcon, PlaceMarker.TYPE_ICON)
-                        .setLabel("foo")
-                        .setColor(CarColor.SECONDARY)
-                        .build();
+        CarIcon carIcon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
+        PlaceMarker marker = PlaceMarker.builder()
+                .setIcon(carIcon, PlaceMarker.TYPE_ICON)
+                .setLabel("foo")
+                .setColor(CarColor.SECONDARY)
+                .build();
 
-        assertThat(
-                PlaceMarker.builder()
-                        .setIcon(carIcon, PlaceMarker.TYPE_ICON)
-                        .setLabel("foo")
-                        .setColor(CarColor.SECONDARY)
-                        .build())
+        assertThat(PlaceMarker.builder()
+                .setIcon(carIcon, PlaceMarker.TYPE_ICON)
+                .setLabel("foo")
+                .setColor(CarColor.SECONDARY)
+                .build())
                 .isEqualTo(marker);
     }
 
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/PlaceTest.java b/car/app/app/src/test/java/androidx/car/app/model/PlaceTest.java
similarity index 93%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/PlaceTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/PlaceTest.java
index 664dd26..d4ef129 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/PlaceTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/PlaceTest.java
@@ -18,15 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for the {@link Place} class. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class PlaceTest {
     /** Tests basic setter and getter operations. */
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/RowTest.java b/car/app/app/src/test/java/androidx/car/app/model/RowTest.java
similarity index 93%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/RowTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/RowTest.java
index 73a62dd..69c140b 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/RowTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/RowTest.java
@@ -26,19 +26,17 @@
 import static org.mockito.Mockito.verify;
 
 import androidx.car.app.OnDoneCallback;
-import androidx.car.app.test.R;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.annotation.UiThreadTest;
+import androidx.car.app.TestUtils;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link Row}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class RowTest {
     @Test
     public void create_defaultValues() {
@@ -98,7 +96,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void clickListener() {
         OnClickListener onClickListener = mock(OnClickListener.class);
         Row row = Row.builder().setTitle("Title").setOnClickListener(onClickListener).build();
@@ -145,11 +142,8 @@
                 })
                 .setTitle("Title")
                 .addText("Text")
-                .setImage(
-                        CarIcon.of(
-                                IconCompat.createWithResource(
-                                        ApplicationProvider.getApplicationContext(),
-                                        R.drawable.ic_test_1)))
+                .setImage(TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                        "ic_test_1"))
                 .build();
     }
 
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/SearchTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/model/SearchTemplateTest.java
similarity index 82%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/SearchTemplateTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/SearchTemplateTest.java
index 9a754f2..c837406 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/SearchTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/SearchTemplateTest.java
@@ -27,12 +27,8 @@
 import android.os.RemoteException;
 
 import androidx.car.app.OnDoneCallback;
-import androidx.car.app.SearchListener;
 import androidx.car.app.TestUtils;
 import androidx.car.app.WrappedRuntimeException;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -40,22 +36,24 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link SearchTemplate}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class SearchTemplateTest {
     @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
 
     @Mock
-    SearchListener mMockSearchListener;
+    SearchTemplate.SearchCallback mMockSearchCallback;
 
     @Test
     public void createInstance_isLoading_hasList_Throws() {
         assertThrows(
                 IllegalArgumentException.class,
-                () -> SearchTemplate.builder(mMockSearchListener)
+                () -> SearchTemplate.builder(mMockSearchCallback)
                         .setLoading(true)
                         .setItemList(ItemList.builder().build())
                         .build());
@@ -65,12 +63,12 @@
     public void addList_selectable_throws() {
         assertThrows(
                 IllegalArgumentException.class,
-                () -> SearchTemplate.builder(mMockSearchListener)
+                () -> SearchTemplate.builder(mMockSearchCallback)
                         .setItemList(TestUtils.createItemList(6, true))
                         .build());
 
         // Positive cases.
-        SearchTemplate.builder(mMockSearchListener)
+        SearchTemplate.builder(mMockSearchCallback)
                 .setItemList(TestUtils.createItemList(6, false))
                 .build();
     }
@@ -84,12 +82,12 @@
                 Row.builder().setTitle("Title").addText("text1").addText("text2").build();
         assertThrows(
                 IllegalArgumentException.class,
-                () -> SearchTemplate.builder(mMockSearchListener)
+                () -> SearchTemplate.builder(mMockSearchCallback)
                         .setItemList(ItemList.builder().addItem(rowExceedsMaxTexts).build())
                         .build());
 
         // Positive cases.
-        SearchTemplate.builder(mMockSearchListener)
+        SearchTemplate.builder(mMockSearchCallback)
                 .setItemList(ItemList.builder().addItem(rowMeetingMaxTexts).build())
                 .build();
     }
@@ -103,19 +101,19 @@
                 Row.builder().setTitle("Title").addText("text1").addText("text2").build();
         assertThrows(
                 IllegalArgumentException.class,
-                () -> SearchTemplate.builder(mMockSearchListener)
+                () -> SearchTemplate.builder(mMockSearchCallback)
                         .setItemList(ItemList.builder().addItem(rowWithToggle).build())
                         .build());
 
         // Positive cases.
-        SearchTemplate.builder(mMockSearchListener)
+        SearchTemplate.builder(mMockSearchCallback)
                 .setItemList(ItemList.builder().addItem(rowMeetingRestrictions).build())
                 .build();
     }
 
     @Test
     public void buildEmpty_nullValues() {
-        SearchTemplate searchTemplate = SearchTemplate.builder(mMockSearchListener).build();
+        SearchTemplate searchTemplate = SearchTemplate.builder(mMockSearchCallback).build();
 
         assertThat(searchTemplate.getInitialSearchText()).isNull();
         assertThat(searchTemplate.getSearchHint()).isNull();
@@ -124,7 +122,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void buildWithValues() throws RemoteException {
         String initialSearchText = "searchTemplate for this!!";
         String searchHint = "This is not a hint";
@@ -133,7 +130,7 @@
         ActionStrip actionStrip = ActionStrip.builder().addAction(Action.BACK).build();
 
         SearchTemplate searchTemplate =
-                SearchTemplate.builder(mMockSearchListener)
+                SearchTemplate.builder(mMockSearchCallback)
                         .setHeaderAction(Action.BACK)
                         .setActionStrip(actionStrip)
                         .setInitialSearchText(initialSearchText)
@@ -149,13 +146,12 @@
         assertThat(searchTemplate.getHeaderAction()).isEqualTo(Action.BACK);
 
         String searchText = "foo";
-        searchTemplate.getSearchListener().onSearchSubmitted(searchText, onDoneCallback);
-        verify(mMockSearchListener).onSearchSubmitted(searchText);
+        searchTemplate.getSearchCallback().onSearchSubmitted(searchText, onDoneCallback);
+        verify(mMockSearchCallback).onSearchSubmitted(searchText);
         verify(onDoneCallback).onSuccess(null);
     }
 
     @Test
-    @UiThreadTest
     public void buildWithValues_failureOnSearchSubmitted() throws RemoteException {
         String initialSearchText = "searchTemplate for this!!";
         String searchHint = "This is not a hint";
@@ -164,7 +160,7 @@
         ActionStrip actionStrip = ActionStrip.builder().addAction(Action.BACK).build();
 
         SearchTemplate searchTemplate =
-                SearchTemplate.builder(mMockSearchListener)
+                SearchTemplate.builder(mMockSearchCallback)
                         .setHeaderAction(Action.BACK)
                         .setActionStrip(actionStrip)
                         .setInitialSearchText(initialSearchText)
@@ -182,16 +178,16 @@
         String searchText = "foo";
         String testExceptionMessage = "Test exception";
         doThrow(new RuntimeException(testExceptionMessage)).when(
-                mMockSearchListener).onSearchSubmitted(searchText);
+                mMockSearchCallback).onSearchSubmitted(searchText);
         OnDoneCallback onDoneCallback = mock(OnDoneCallback.class);
 
         try {
-            searchTemplate.getSearchListener().onSearchSubmitted(searchText, onDoneCallback);
+            searchTemplate.getSearchCallback().onSearchSubmitted(searchText, onDoneCallback);
         } catch (WrappedRuntimeException e) {
             assertThat(e.getMessage()).contains(testExceptionMessage);
         }
 
-        verify(mMockSearchListener).onSearchSubmitted(searchText);
+        verify(mMockSearchCallback).onSearchSubmitted(searchText);
         verify(onDoneCallback).onFailure(any());
     }
 
@@ -199,7 +195,7 @@
     public void createInstance_setHeaderAction_invalidActionThrows() {
         assertThrows(
                 IllegalArgumentException.class,
-                () -> SearchTemplate.builder(mMockSearchListener)
+                () -> SearchTemplate.builder(mMockSearchCallback)
                         .setHeaderAction(
                                 Action.builder().setTitle("Action").setOnClickListener(
                                         () -> {
@@ -209,7 +205,7 @@
     @Test
     public void equals() {
         SearchTemplate template =
-                SearchTemplate.builder(mMockSearchListener)
+                SearchTemplate.builder(mMockSearchCallback)
                         .setHeaderAction(Action.BACK)
                         .setActionStrip(ActionStrip.builder().addAction(Action.BACK).build())
                         .setInitialSearchText("foo")
@@ -221,7 +217,7 @@
 
         assertThat(template)
                 .isEqualTo(
-                        SearchTemplate.builder(mMockSearchListener)
+                        SearchTemplate.builder(mMockSearchCallback)
                                 .setHeaderAction(Action.BACK)
                                 .setActionStrip(
                                         ActionStrip.builder().addAction(Action.BACK).build())
@@ -236,22 +232,22 @@
     @Test
     public void notEquals_differentHeaderAction() {
         SearchTemplate template =
-                SearchTemplate.builder(mMockSearchListener).setHeaderAction(Action.BACK).build();
+                SearchTemplate.builder(mMockSearchCallback).setHeaderAction(Action.BACK).build();
         assertThat(template)
                 .isNotEqualTo(
-                        SearchTemplate.builder(mMockSearchListener).setHeaderAction(
+                        SearchTemplate.builder(mMockSearchCallback).setHeaderAction(
                                 Action.APP_ICON).build());
     }
 
     @Test
     public void notEquals_differentActionStrip() {
         SearchTemplate template =
-                SearchTemplate.builder(mMockSearchListener)
+                SearchTemplate.builder(mMockSearchCallback)
                         .setActionStrip(ActionStrip.builder().addAction(Action.BACK).build())
                         .build();
         assertThat(template)
                 .isNotEqualTo(
-                        SearchTemplate.builder(mMockSearchListener)
+                        SearchTemplate.builder(mMockSearchCallback)
                                 .setActionStrip(
                                         ActionStrip.builder().addAction(Action.APP_ICON).build())
                                 .build());
@@ -260,40 +256,40 @@
     @Test
     public void notEquals_differentInitialSearchText() {
         SearchTemplate template =
-                SearchTemplate.builder(mMockSearchListener).setInitialSearchText("foo").build();
+                SearchTemplate.builder(mMockSearchCallback).setInitialSearchText("foo").build();
         assertThat(template)
                 .isNotEqualTo(
-                        SearchTemplate.builder(mMockSearchListener).setInitialSearchText(
+                        SearchTemplate.builder(mMockSearchCallback).setInitialSearchText(
                                 "bar").build());
     }
 
     @Test
     public void notEquals_differentSearchHint() {
         SearchTemplate template =
-                SearchTemplate.builder(mMockSearchListener).setSearchHint("foo").build();
+                SearchTemplate.builder(mMockSearchCallback).setSearchHint("foo").build();
         assertThat(template)
-                .isNotEqualTo(SearchTemplate.builder(mMockSearchListener).setSearchHint(
+                .isNotEqualTo(SearchTemplate.builder(mMockSearchCallback).setSearchHint(
                         "bar").build());
     }
 
     @Test
     public void notEquals_differentKeyboardEnabled() {
         SearchTemplate template =
-                SearchTemplate.builder(mMockSearchListener).setShowKeyboardByDefault(true).build();
+                SearchTemplate.builder(mMockSearchCallback).setShowKeyboardByDefault(true).build();
         assertThat(template)
                 .isNotEqualTo(
-                        SearchTemplate.builder(mMockSearchListener).setShowKeyboardByDefault(
+                        SearchTemplate.builder(mMockSearchCallback).setShowKeyboardByDefault(
                                 false).build());
     }
 
     @Test
     public void notEquals_differentItemList() {
         SearchTemplate template =
-                SearchTemplate.builder(mMockSearchListener).setItemList(
+                SearchTemplate.builder(mMockSearchCallback).setItemList(
                         ItemList.builder().build()).build();
         assertThat(template)
                 .isNotEqualTo(
-                        SearchTemplate.builder(mMockSearchListener)
+                        SearchTemplate.builder(mMockSearchCallback)
                                 .setItemList(
                                         ItemList.builder().addItem(
                                                 Row.builder().setTitle("Title").build()).build())
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/SectionedItemListTest.java b/car/app/app/src/test/java/androidx/car/app/model/SectionedItemListTest.java
similarity index 94%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/SectionedItemListTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/SectionedItemListTest.java
index 69f5124..b80e38a 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/SectionedItemListTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/SectionedItemListTest.java
@@ -18,15 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link ItemListTest}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class SectionedItemListTest {
 
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/TemplateWrapperTest.java b/car/app/app/src/test/java/androidx/car/app/model/TemplateWrapperTest.java
similarity index 95%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/TemplateWrapperTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/TemplateWrapperTest.java
index 3cbb446..f8ff387 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/TemplateWrapperTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/TemplateWrapperTest.java
@@ -18,15 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link TemplateWrapper}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class TemplateWrapperTest {
     @Test
     public void createInstance() {
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/ToggleTest.java b/car/app/app/src/test/java/androidx/car/app/model/ToggleTest.java
similarity index 93%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/ToggleTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/ToggleTest.java
index dad2cbd..d4d3575 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/ToggleTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ToggleTest.java
@@ -26,9 +26,6 @@
 import androidx.car.app.OnDoneCallback;
 import androidx.car.app.WrappedRuntimeException;
 import androidx.car.app.model.Toggle.OnCheckedChangeListener;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -36,10 +33,12 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link Toggle}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class ToggleTest {
     @Rule
     public MockitoRule mocks = MockitoJUnit.rule();
@@ -54,7 +53,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void build_checkedChange_sendsCheckedChangeCall() {
         Toggle toggle = Toggle.builder(mMockOnCheckedChangeListener).setChecked(true).build();
         OnDoneCallback onDoneCallback = mock(OnDoneCallback.class);
@@ -65,7 +63,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void build_checkedChange_sendsCheckedChangeCallWithFailure() {
         String testExceptionMessage = "Test exception";
         doThrow(new RuntimeException(testExceptionMessage)).when(
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/constraints/ActionsConstraintsTest.java b/car/app/app/src/test/java/androidx/car/app/model/constraints/ActionsConstraintsTest.java
similarity index 63%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/constraints/ActionsConstraintsTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/constraints/ActionsConstraintsTest.java
index ab577b5..0ac564e 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/constraints/ActionsConstraintsTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/constraints/ActionsConstraintsTest.java
@@ -24,20 +24,18 @@
 import androidx.car.app.model.Action;
 import androidx.car.app.model.ActionStrip;
 import androidx.car.app.model.CarIcon;
-import androidx.car.app.test.R;
-import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.Collections;
 
 /** Tests for {@link ActionsConstraints}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class ActionsConstraintsTest {
     @Test
     public void createEmpty() {
@@ -51,23 +49,21 @@
     public void create_requiredExceedsMaxAllowedActions() {
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        ActionsConstraints.builder()
-                                .setMaxActions(1)
-                                .addRequiredActionType(Action.TYPE_BACK)
-                                .addRequiredActionType(Action.TYPE_CUSTOM)
-                                .build());
+                () -> ActionsConstraints.builder()
+                        .setMaxActions(1)
+                        .addRequiredActionType(Action.TYPE_BACK)
+                        .addRequiredActionType(Action.TYPE_CUSTOM)
+                        .build());
     }
 
     @Test
     public void create_requiredAlsoDisallowed() {
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        ActionsConstraints.builder()
-                                .addRequiredActionType(Action.TYPE_BACK)
-                                .addDisallowedActionType(Action.TYPE_BACK)
-                                .build());
+                () -> ActionsConstraints.builder()
+                        .addRequiredActionType(Action.TYPE_BACK)
+                        .addDisallowedActionType(Action.TYPE_BACK)
+                        .build());
     }
 
     @Test
@@ -94,10 +90,8 @@
                         .addDisallowedActionType(Action.TYPE_BACK)
                         .build();
 
-        CarIcon carIcon =
-                CarIcon.of(
-                        IconCompat.createWithResource(
-                                ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1));
+        CarIcon carIcon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
         Action actionWithIcon = TestUtils.createAction(null, carIcon);
         Action actionWithTitle = TestUtils.createAction("Title", carIcon);
 
@@ -115,39 +109,35 @@
         // Missing required type.
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        constraints.validateOrThrow(
-                                ActionStrip.builder().addAction(
-                                        Action.APP_ICON).build().getActions()));
+                () -> constraints.validateOrThrow(
+                        ActionStrip.builder().addAction(
+                                Action.APP_ICON).build().getActions()));
 
         // Disallowed type
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        constraints.validateOrThrow(
-                                ActionStrip.builder().addAction(Action.BACK).build().getActions()));
+                () -> constraints.validateOrThrow(
+                        ActionStrip.builder().addAction(Action.BACK).build().getActions()));
 
         // Over max allowed actions
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        constraints.validateOrThrow(
-                                ActionStrip.builder()
-                                        .addAction(Action.APP_ICON)
-                                        .addAction(actionWithIcon)
-                                        .addAction(actionWithTitle)
-                                        .build()
-                                        .getActions()));
+                () -> constraints.validateOrThrow(
+                        ActionStrip.builder()
+                                .addAction(Action.APP_ICON)
+                                .addAction(actionWithIcon)
+                                .addAction(actionWithTitle)
+                                .build()
+                                .getActions()));
 
         // Over max allowed actions with title
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        constraints.validateOrThrow(
-                                ActionStrip.builder()
-                                        .addAction(actionWithTitle)
-                                        .addAction(actionWithTitle)
-                                        .build()
-                                        .getActions()));
+                () -> constraints.validateOrThrow(
+                        ActionStrip.builder()
+                                .addAction(actionWithTitle)
+                                .addAction(actionWithTitle)
+                                .build()
+                                .getActions()));
     }
 }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/constraints/RowConstraintsTest.java b/car/app/app/src/test/java/androidx/car/app/model/constraints/RowConstraintsTest.java
similarity index 66%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/constraints/RowConstraintsTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/constraints/RowConstraintsTest.java
index 9b4d96e..5bc88c5 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/constraints/RowConstraintsTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/constraints/RowConstraintsTest.java
@@ -18,21 +18,20 @@
 
 import static org.junit.Assert.assertThrows;
 
+import androidx.car.app.TestUtils;
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.Row;
 import androidx.car.app.model.Toggle;
-import androidx.car.app.test.R;
-import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link RowConstraints}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class RowConstraintsTest {
     @Test
     public void validate_clickListener() {
@@ -43,10 +42,9 @@
 
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        constraints.validateOrThrow(
-                                Row.builder().setTitle("Title)").setOnClickListener(() -> {
-                                }).build()));
+                () -> constraints.validateOrThrow(
+                        Row.builder().setTitle("Title)").setOnClickListener(() -> {
+                        }).build()));
 
         // Positive cases
         constraints.validateOrThrow(Row.builder().setTitle("Title").build());
@@ -62,13 +60,12 @@
 
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        constraints.validateOrThrow(
-                                Row.builder()
-                                        .setTitle("Title)")
-                                        .setToggle(Toggle.builder(isChecked -> {
-                                        }).build())
-                                        .build()));
+                () -> constraints.validateOrThrow(
+                        Row.builder()
+                                .setTitle("Title)")
+                                .setToggle(Toggle.builder(isChecked -> {
+                                }).build())
+                                .build()));
 
         // Positive cases
         constraints.validateOrThrow(Row.builder().setTitle("Title").build());
@@ -81,16 +78,13 @@
     public void validate_images() {
         RowConstraints constraints = RowConstraints.builder().setImageAllowed(false).build();
         RowConstraints allowConstraints = RowConstraints.builder().setImageAllowed(true).build();
-        CarIcon carIcon =
-                CarIcon.of(
-                        IconCompat.createWithResource(
-                                ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1));
+        CarIcon carIcon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
 
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        constraints.validateOrThrow(
-                                Row.builder().setTitle("Title)").setImage(carIcon).build()));
+                () -> constraints.validateOrThrow(
+                        Row.builder().setTitle("Title)").setImage(carIcon).build()));
 
         // Positive cases
         constraints.validateOrThrow(Row.builder().setTitle("Title").build());
@@ -103,14 +97,13 @@
 
         assertThrows(
                 IllegalArgumentException.class,
-                () ->
-                        constraints.validateOrThrow(
-                                Row.builder()
-                                        .setTitle("Title)")
-                                        .addText("text1")
-                                        .addText("text2")
-                                        .addText("text3")
-                                        .build()));
+                () -> constraints.validateOrThrow(
+                        Row.builder()
+                                .setTitle("Title)")
+                                .addText("text1")
+                                .addText("text2")
+                                .addText("text3")
+                                .build()));
 
         // Positive cases
         constraints.validateOrThrow(
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/model/constraints/RowListConstraintsTest.java b/car/app/app/src/test/java/androidx/car/app/model/constraints/RowListConstraintsTest.java
similarity index 85%
rename from car/app/app/src/androidTest/java/androidx/car/app/model/constraints/RowListConstraintsTest.java
rename to car/app/app/src/test/java/androidx/car/app/model/constraints/RowListConstraintsTest.java
index 56b5273..7e775b5 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/model/constraints/RowListConstraintsTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/constraints/RowListConstraintsTest.java
@@ -19,26 +19,24 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.car.app.TestUtils;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link RowListConstraints}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class RowListConstraintsTest {
     @Test
     public void validate_itemList_noSelectable() {
         RowListConstraints disallowConstraints =
                 RowListConstraints.builder()
-                        .setRowListType(RowListConstraints.DEFAULT_LIST)
                         .setAllowSelectableLists(false)
                         .build();
         RowListConstraints allowConstraints =
                 RowListConstraints.builder()
-                        .setRowListType(RowListConstraints.DEFAULT_LIST)
                         .setAllowSelectableLists(true)
                         .build();
 
@@ -55,12 +53,10 @@
     public void validate_sectionItemList_noSelectable() {
         RowListConstraints disallowConstraints =
                 RowListConstraints.builder()
-                        .setRowListType(RowListConstraints.DEFAULT_LIST)
                         .setAllowSelectableLists(false)
                         .build();
         RowListConstraints allowConstraints =
                 RowListConstraints.builder()
-                        .setRowListType(RowListConstraints.DEFAULT_LIST)
                         .setAllowSelectableLists(true)
                         .build();
 
@@ -77,7 +73,6 @@
     public void validate_pane_maxActions() {
         RowListConstraints constraints =
                 RowListConstraints.builder()
-                        .setRowListType(RowListConstraints.DEFAULT_LIST)
                         .setMaxActions(2)
                         .build();
 
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/NavigationManagerTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/NavigationManagerTest.java
similarity index 77%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/NavigationManagerTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/NavigationManagerTest.java
index 051358e..3571000 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/NavigationManagerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/NavigationManagerTest.java
@@ -37,9 +37,8 @@
 import androidx.car.app.navigation.model.TravelEstimate;
 import androidx.car.app.navigation.model.Trip;
 import androidx.car.app.serialization.Bundleable;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
+import androidx.car.app.testing.TestCarContext;
+import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -47,19 +46,22 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
+import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
 /** Tests for {@link NavigationManager}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class NavigationManagerTest {
     @Mock
     private ICarHost mMockCarHost;
     @Mock
     private INavigationHost.Stub mMockNavHost;
     @Mock
-    private NavigationManagerListener mNavigationListener;
+    private NavigationManagerCallback mNavigationListener;
 
     private final HostDispatcher mHostDispatcher = new HostDispatcher();
     private NavigationManager mNavigationManager;
@@ -88,10 +90,12 @@
                     .build();
 
     @Before
-    @UiThreadTest
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
 
+        TestCarContext testCarContext =
+                TestCarContext.createCarContext(ApplicationProvider.getApplicationContext());
+
         INavigationHost navHostStub =
                 new INavigationHost.Stub() {
                     @Override
@@ -113,15 +117,14 @@
 
         mHostDispatcher.setCarHost(mMockCarHost);
 
-        mNavigationManager = NavigationManager.create(mHostDispatcher);
+        mNavigationManager = NavigationManager.create(testCarContext, mHostDispatcher);
     }
 
     @Test
-    @UiThreadTest
     public void navigationStarted_sendState_navigationEnded() throws RemoteException {
         InOrder inOrder = inOrder(mMockNavHost);
 
-        mNavigationManager.setListener(mNavigationListener);
+        mNavigationManager.setNavigationManagerCallback(mNavigationListener);
         mNavigationManager.navigationStarted();
         inOrder.verify(mMockNavHost).navigationStarted();
 
@@ -133,16 +136,14 @@
     }
 
     @Test
-    @UiThreadTest
     public void navigationStarted_noListenerSet() throws RemoteException {
         assertThrows(IllegalStateException.class, () -> mNavigationManager.navigationStarted());
     }
 
     @Test
-    @UiThreadTest
     public void navigationStarted_multiple() throws RemoteException {
 
-        mNavigationManager.setListener(mNavigationListener);
+        mNavigationManager.setNavigationManagerCallback(mNavigationListener);
         mNavigationManager.navigationStarted();
 
         mNavigationManager.navigationStarted();
@@ -150,7 +151,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void navigationEnded_multiple_not_started() throws RemoteException {
         mNavigationManager.navigationEnded();
         mNavigationManager.navigationEnded();
@@ -164,44 +164,52 @@
     }
 
     @Test
-    @UiThreadTest
-    public void stopNavigation_notNavigating() throws RemoteException {
-        mNavigationManager.setListener(mNavigationListener);
-        mNavigationManager.getIInterface().stopNavigation(mock(IOnDoneCallback.class));
-        verify(mNavigationListener, never()).stopNavigation();
+    public void onStopNavigation_notNavigating() throws RemoteException {
+        mNavigationManager.setNavigationManagerCallback(mNavigationListener);
+        mNavigationManager.getIInterface().onStopNavigation(mock(IOnDoneCallback.class));
+        verify(mNavigationListener, never()).onStopNavigation();
     }
 
     @Test
-    @UiThreadTest
-    public void stopNavigation_navigating_restart() throws RemoteException {
+    public void onStopNavigation_navigating_restart() throws RemoteException {
         InOrder inOrder = inOrder(mMockNavHost, mNavigationListener);
 
-        mNavigationManager.setListener(mNavigationListener);
+        mNavigationManager.setNavigationManagerCallback(new SynchronousExecutor(),
+                mNavigationListener);
         mNavigationManager.navigationStarted();
         inOrder.verify(mMockNavHost).navigationStarted();
 
-        mNavigationManager.getIInterface().stopNavigation(mock(IOnDoneCallback.class));
-        inOrder.verify(mNavigationListener).stopNavigation();
+        mNavigationManager.getIInterface().onStopNavigation(mock(IOnDoneCallback.class));
+
+        inOrder.verify(mNavigationListener).onStopNavigation();
 
         mNavigationManager.navigationStarted();
         inOrder.verify(mMockNavHost).navigationStarted();
     }
 
     @Test
-    @UiThreadTest
     public void onAutoDriveEnabled_callsListener() {
-        mNavigationManager.setListener(mNavigationListener);
+        mNavigationManager.setNavigationManagerCallback(new SynchronousExecutor(),
+                mNavigationListener);
         mNavigationManager.onAutoDriveEnabled();
 
         verify(mNavigationListener).onAutoDriveEnabled();
     }
 
     @Test
-    @UiThreadTest
     public void onAutoDriveEnabledBeforeRegisteringListener_callsListener() {
         mNavigationManager.onAutoDriveEnabled();
-        mNavigationManager.setListener(mNavigationListener);
+        mNavigationManager.setNavigationManagerCallback(new SynchronousExecutor(),
+                mNavigationListener);
 
         verify(mNavigationListener).onAutoDriveEnabled();
     }
+
+    static class SynchronousExecutor implements Executor {
+        @Override
+        public void execute(Runnable r) {
+            r.run();
+        }
+    }
+
 }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/DestinationTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/DestinationTest.java
similarity index 96%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/DestinationTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/DestinationTest.java
index ba52c24..0663a13 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/DestinationTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/DestinationTest.java
@@ -25,15 +25,15 @@
 
 import androidx.car.app.model.CarIcon;
 import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link Destination}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class DestinationTest {
 
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/LaneDirectionTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/LaneDirectionTest.java
similarity index 92%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/LaneDirectionTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/LaneDirectionTest.java
index 3f17b37..3d691ec 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/LaneDirectionTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/LaneDirectionTest.java
@@ -20,15 +20,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link LaneDirection}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class LaneDirectionTest {
 
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/LaneTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/LaneTest.java
similarity index 94%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/LaneTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/LaneTest.java
index 8d4a1197..aee2408 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/LaneTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/LaneTest.java
@@ -21,15 +21,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link Lane}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class LaneTest {
 
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/ManeuverTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/ManeuverTest.java
similarity index 98%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/ManeuverTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/ManeuverTest.java
index c5d2636..5064bda 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/ManeuverTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/ManeuverTest.java
@@ -33,15 +33,15 @@
 
 import androidx.car.app.model.CarIcon;
 import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link Maneuver}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class ManeuverTest {
 
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/MessageInfoTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/MessageInfoTest.java
similarity index 95%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/MessageInfoTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/MessageInfoTest.java
index 1be5851..81063ca 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/MessageInfoTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/MessageInfoTest.java
@@ -25,15 +25,15 @@
 
 import androidx.car.app.model.CarIcon;
 import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link MessageInfoTest}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class MessageInfoTest {
 
     @Test
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/NavigationTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/NavigationTemplateTest.java
similarity index 98%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/NavigationTemplateTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/NavigationTemplateTest.java
index 2cbd5c4..8e3b04a 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/NavigationTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/NavigationTemplateTest.java
@@ -29,17 +29,17 @@
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.Distance;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.concurrent.TimeUnit;
 
 /** Tests for {@link NavigationTemplate}. */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class NavigationTemplateTest {
     private final ActionStrip mActionStrip =
             ActionStrip.builder().addAction(TestUtils.createAction("test", null)).build();
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/PlaceListNavigationTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/PlaceListNavigationTemplateTest.java
similarity index 98%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/PlaceListNavigationTemplateTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/PlaceListNavigationTemplateTest.java
index 844c094..cd5aa98 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/PlaceListNavigationTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/PlaceListNavigationTemplateTest.java
@@ -38,15 +38,15 @@
 import androidx.car.app.model.Row;
 import androidx.car.app.model.Toggle;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link PlaceListNavigationTemplate}. */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class PlaceListNavigationTemplateTest {
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private final DistanceSpan mDistanceSpan =
@@ -60,7 +60,7 @@
                 () -> PlaceListNavigationTemplate.builder().setTitle("Title").build());
 
         // Positive case
-        PlaceListNavigationTemplate.builder().setTitle("Title").setIsLoading(true).build();
+        PlaceListNavigationTemplate.builder().setTitle("Title").setLoading(true).build();
     }
 
     @Test
@@ -70,7 +70,7 @@
                 () ->
                         PlaceListNavigationTemplate.builder()
                                 .setTitle("Title")
-                                .setIsLoading(true)
+                                .setLoading(true)
                                 .setItemList(ItemList.builder().build())
                                 .build());
     }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplateTest.java
similarity index 95%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplateTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplateTest.java
index 57a4723..dcf4c11 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplateTest.java
@@ -36,19 +36,16 @@
 import androidx.car.app.model.ItemList;
 import androidx.car.app.model.OnClickListener;
 import androidx.car.app.model.Row;
-import androidx.car.app.test.R;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link RoutePreviewNavigationTemplate}. */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class RoutePreviewNavigationTemplateTest {
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private static final DistanceSpan DISTANCE =
@@ -62,7 +59,7 @@
                 () -> RoutePreviewNavigationTemplate.builder().setTitle("Title").build());
 
         // Positive case
-        RoutePreviewNavigationTemplate.builder().setTitle("Title").setIsLoading(true).build();
+        RoutePreviewNavigationTemplate.builder().setTitle("Title").setLoading(true).build();
     }
 
     @Test
@@ -71,7 +68,7 @@
                 IllegalStateException.class,
                 () -> RoutePreviewNavigationTemplate.builder()
                         .setTitle("Title")
-                        .setIsLoading(true)
+                        .setLoading(true)
                         .setItemList(
                                 TestUtils.createItemListWithDistanceSpan(2, true, DISTANCE))
                         .build());
@@ -127,13 +124,13 @@
     public void noHeaderTitleOrAction_throws() {
         assertThrows(
                 IllegalStateException.class,
-                () -> RoutePreviewNavigationTemplate.builder().setIsLoading(true).build());
+                () -> RoutePreviewNavigationTemplate.builder().setLoading(true).build());
 
         // Positive cases.
-        RoutePreviewNavigationTemplate.builder().setTitle("Title").setIsLoading(true).build();
+        RoutePreviewNavigationTemplate.builder().setTitle("Title").setLoading(true).build();
         RoutePreviewNavigationTemplate.builder()
                 .setHeaderAction(Action.BACK)
-                .setIsLoading(true)
+                .setLoading(true)
                 .build();
     }
 
@@ -185,7 +182,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void setOnNavigateAction() {
         OnClickListener mockListener = mock(OnClickListener.class);
         RoutePreviewNavigationTemplate template =
@@ -216,7 +212,7 @@
                         .build());
 
         // Positive case
-        RoutePreviewNavigationTemplate.builder().setTitle("Title").setIsLoading(true).build();
+        RoutePreviewNavigationTemplate.builder().setTitle("Title").setLoading(true).build();
     }
 
     @Test
@@ -234,13 +230,13 @@
                         .build());
 
         // Positive case
-        RoutePreviewNavigationTemplate.builder().setTitle("Title").setIsLoading(true).build();
+        RoutePreviewNavigationTemplate.builder().setTitle("Title").setLoading(true).build();
     }
 
     @Test
     public void createInstance_navigateActionNoTitle_throws() {
-        CarIcon carIcon = CarIcon.of(IconCompat.createWithResource(
-                ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1));
+        CarIcon carIcon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
         assertThrows(
                 IllegalArgumentException.class,
                 () -> RoutePreviewNavigationTemplate.builder()
@@ -271,9 +267,8 @@
         Row rowWithTime = Row.builder().setTitle(title).build();
         Row rowWithoutTime = Row.builder().setTitle("Google Bve").build();
         Action navigateAction = Action.builder()
-                .setIcon(CarIcon.of(IconCompat.createWithResource(
-                        ApplicationProvider.getApplicationContext(),
-                        R.drawable.ic_test_1)))
+                .setIcon(TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                        "ic_test_1"))
                 .setTitle("Navigate")
                 .setOnClickListener(() -> {
                 })
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/RoutingInfoTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/RoutingInfoTest.java
similarity index 97%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/RoutingInfoTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/RoutingInfoTest.java
index 58ea716..8cd974c 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/RoutingInfoTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/RoutingInfoTest.java
@@ -26,15 +26,15 @@
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.Distance;
 import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link RoutingInfoTest}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class RoutingInfoTest {
 
     private final Maneuver mManeuver =
@@ -54,7 +54,7 @@
         assertThrows(
                 IllegalStateException.class,
                 () -> RoutingInfo.builder()
-                        .setIsLoading(true)
+                        .setLoading(true)
                         .setCurrentStep(mCurrentStep, mCurrentDistance)
                         .build());
     }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/StepTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/StepTest.java
similarity index 97%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/StepTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/StepTest.java
index bc8a79d..3c0f0c8 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/StepTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/StepTest.java
@@ -24,15 +24,15 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.car.app.model.CarIcon;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link Step}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class StepTest {
     @Test
     public void createInstance() {
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/TravelEstimateTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/TravelEstimateTest.java
similarity index 98%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/TravelEstimateTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/TravelEstimateTest.java
index 7b51591..ca589ec 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/TravelEstimateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/TravelEstimateTest.java
@@ -27,12 +27,11 @@
 import androidx.car.app.model.CarColor;
 import androidx.car.app.model.DateTimeWithZone;
 import androidx.car.app.model.Distance;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SdkSuppress;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.time.Duration;
 import java.time.ZonedDateTime;
@@ -41,8 +40,8 @@
 import java.util.concurrent.TimeUnit;
 
 /** Tests for {@link TravelEstimate}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class TravelEstimateTest {
     private final DateTimeWithZone mArrivalTime =
             createDateTimeWithZone("2020-04-14T15:57:00", "US/Pacific");
@@ -66,7 +65,6 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 26)
     public void create_duration() {
         ZonedDateTime arrivalTime = ZonedDateTime.parse("2020-05-14T19:57:00-07:00[US/Pacific]");
         Duration remainingTime = Duration.ofHours(10);
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/TripTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/TripTest.java
similarity index 93%
rename from car/app/app/src/androidTest/java/androidx/car/app/navigation/model/TripTest.java
rename to car/app/app/src/test/java/androidx/car/app/navigation/model/TripTest.java
index 4c936e3..7fb0d3c 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/navigation/model/TripTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/TripTest.java
@@ -26,17 +26,17 @@
 
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.Distance;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.concurrent.TimeUnit;
 
 /** Tests for {@link Trip}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class TripTest {
 
     private final Step mStep =
@@ -71,7 +71,7 @@
                         .addDestinationTravelEstimate(mDestinationTravelEstimate)
                         .addStepTravelEstimate(mStepTravelEstimate)
                         .setCurrentRoad(ROAD)
-                        .setIsLoading(false)
+                        .setLoading(false)
                         .build();
 
         assertThat(trip.getDestinations()).hasSize(1);
@@ -112,7 +112,7 @@
                         .addDestination(mDestination)
                         .addDestinationTravelEstimate(mDestinationTravelEstimate)
                         .setCurrentRoad(ROAD)
-                        .setIsLoading(true)
+                        .setLoading(true)
                         .build();
 
         assertThat(trip.getDestinations()).hasSize(1);
@@ -129,17 +129,17 @@
     public void createInstance_loading_with_steps() {
         assertThrows(
                 IllegalArgumentException.class,
-                () -> Trip.builder().addStep(mStep).setIsLoading(true).build());
+                () -> Trip.builder().addStep(mStep).setLoading(true).build());
         assertThrows(
                 IllegalArgumentException.class,
-                () -> Trip.builder().addStepTravelEstimate(mStepTravelEstimate).setIsLoading(
+                () -> Trip.builder().addStepTravelEstimate(mStepTravelEstimate).setLoading(
                         true).build());
         assertThrows(
                 IllegalArgumentException.class,
                 () -> Trip.builder()
                         .addStep(mStep)
                         .addStepTravelEstimate(mStepTravelEstimate)
-                        .setIsLoading(true)
+                        .setLoading(true)
                         .build());
     }
 }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/notification/CarAppExtenderTest.java b/car/app/app/src/test/java/androidx/car/app/notification/CarAppExtenderTest.java
similarity index 92%
rename from car/app/app/src/androidTest/java/androidx/car/app/notification/CarAppExtenderTest.java
rename to car/app/app/src/test/java/androidx/car/app/notification/CarAppExtenderTest.java
index 707e112..db35c5a 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/notification/CarAppExtenderTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/notification/CarAppExtenderTest.java
@@ -27,22 +27,21 @@
 import android.os.Bundle;
 
 import androidx.annotation.NonNull;
-import androidx.car.app.test.R;
+import androidx.car.app.TestUtils;
 import androidx.core.app.NotificationCompat;
 import androidx.core.app.NotificationManagerCompat;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SdkSuppress;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.List;
 
 /** Tests for {@link CarAppExtender}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public final class CarAppExtenderTest {
     private static final String NOTIFICATION_CHANNEL_ID = "test carextender channel id";
     private static final String INTENT_PRIMARY_ACTION =
@@ -76,8 +75,8 @@
         assertThat(carAppExtender.isExtended()).isFalse();
         assertThat(carAppExtender.getContentTitle()).isNull();
         assertThat(carAppExtender.getContentText()).isNull();
-        assertThat(carAppExtender.getSmallIconResId()).isEqualTo(0);
-        assertThat(carAppExtender.getLargeIconBitmap()).isNull();
+        assertThat(carAppExtender.getSmallIcon()).isEqualTo(0);
+        assertThat(carAppExtender.getLargeIcon()).isNull();
         assertThat(carAppExtender.getContentIntent()).isNull();
         assertThat(carAppExtender.getDeleteIntent()).isNull();
         assertThat(carAppExtender.getActions()).isEmpty();
@@ -129,22 +128,23 @@
 
     @Test
     public void notification_extended_setSmallIcon() {
-        int resId = R.drawable.ic_test_1;
+        int resId = TestUtils.getTestDrawableResId(mContext, "ic_test_1");
         NotificationCompat.Builder builder =
                 new NotificationCompat.Builder(mContext, NOTIFICATION_CHANNEL_ID)
                         .extend(CarAppExtender.builder().setSmallIcon(resId).build());
 
-        assertThat(new CarAppExtender(builder.build()).getSmallIconResId()).isEqualTo(resId);
+        assertThat(new CarAppExtender(builder.build()).getSmallIcon()).isEqualTo(resId);
     }
 
     @Test
     public void notification_extended_setLargeIcon() {
-        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_test_2);
+        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),
+                TestUtils.getTestDrawableResId(mContext, "ic_test_2"));
         NotificationCompat.Builder builder =
                 new NotificationCompat.Builder(mContext, NOTIFICATION_CHANNEL_ID)
                         .extend(CarAppExtender.builder().setLargeIcon(bitmap).build());
 
-        assertThat(new CarAppExtender(builder.build()).getLargeIconBitmap()).isEqualTo(bitmap);
+        assertThat(new CarAppExtender(builder.build()).getLargeIcon()).isEqualTo(bitmap);
     }
 
     @Test
@@ -179,14 +179,13 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 23)
     public void notification_extended_addActions() {
-        int icon1 = R.drawable.ic_test_1;
+        int icon1 = TestUtils.getTestDrawableResId(mContext, "ic_test_1");
         CharSequence title1 = "FirstAction";
         Intent intent1 = new Intent(INTENT_PRIMARY_ACTION);
         PendingIntent actionIntent1 = PendingIntent.getBroadcast(mContext, 0, intent1, 0);
 
-        int icon2 = R.drawable.ic_test_2;
+        int icon2 = TestUtils.getTestDrawableResId(mContext, "ic_test_2");
         CharSequence title2 = "SecondAction";
         Intent intent2 = new Intent(INTENT_SECONDARY_ACTION);
         PendingIntent actionIntent2 = PendingIntent.getBroadcast(mContext, 0, intent2, 0);
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/serialization/BundlerTest.java b/car/app/app/src/test/java/androidx/car/app/serialization/BundlerTest.java
similarity index 96%
rename from car/app/app/src/androidTest/java/androidx/car/app/serialization/BundlerTest.java
rename to car/app/app/src/test/java/androidx/car/app/serialization/BundlerTest.java
index f006161..6bdf76b 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/serialization/BundlerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/serialization/BundlerTest.java
@@ -32,6 +32,7 @@
 
 import androidx.annotation.Nullable;
 import androidx.car.app.OnDoneCallback;
+import androidx.car.app.TestUtils;
 import androidx.car.app.model.Action;
 import androidx.car.app.model.ActionStrip;
 import androidx.car.app.model.CarIcon;
@@ -46,17 +47,14 @@
 import androidx.car.app.model.Row;
 import androidx.car.app.serialization.Bundler.CycleDetectedBundlerException;
 import androidx.car.app.serialization.Bundler.TracedBundlerException;
-import androidx.car.app.test.R;
 import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SdkSuppress;
-import androidx.test.filters.SmallTest;
 
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
@@ -70,8 +68,8 @@
 import java.util.Set;
 
 /** Tests for {@link Bundler}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 public class BundlerTest {
     private static final String TAG_CLASS_NAME = "tag_class_name";
     private static final String TAG_CLASS_TYPE = "tag_class_type";
@@ -160,7 +158,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void binderSerialization() throws BundlerException, RemoteException {
         OnClickListener clickListener = mock(OnClickListener.class);
 
@@ -289,7 +286,8 @@
     @Test
     public void imageSerialization_resource() throws BundlerException {
         Context context = ApplicationProvider.getApplicationContext();
-        IconCompat image = IconCompat.createWithResource(context, R.drawable.ic_test_1);
+        IconCompat image = IconCompat.createWithResource(context,
+                TestUtils.getTestDrawableResId(mContext, "ic_test_1"));
         Bundle bundle = Bundler.toBundle(image);
 
         assertThat(CarIcon.of((IconCompat) Bundler.fromBundle(bundle))).isEqualTo(
@@ -307,7 +305,6 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 23)
     public void imageSerialization_Icon() throws BundlerException {
         Bitmap bitmap = Bitmap.createBitmap(48, 48, Bitmap.Config.ARGB_8888);
         try {
@@ -332,10 +329,7 @@
         String row1Subtitle = "row1subtitle";
 
         LatLng latLng2 = LatLng.create(4522.234, 34.234);
-        CarIcon carIcon =
-                CarIcon.of(
-                        IconCompat.createWithResource(
-                                ApplicationProvider.getApplicationContext(), R.drawable.ic_test_1));
+        CarIcon carIcon = TestUtils.getTestCarIcon(mContext, "ic_test_1");
         PlaceMarker marker2 = PlaceMarker.builder().setIcon(carIcon, PlaceMarker.TYPE_ICON).build();
         String row2Title = "row2";
         String row2Subtitle = "row2subtitle";
@@ -530,12 +524,14 @@
         assertThrows(BundlerException.class, () -> Bundler.fromBundle(bundle));
     }
 
-    @Test
-    public void classMissingDefaultConstructorSerialization_throwsBundlerException() {
-        assertThrows(
-                BundlerException.class,
-                () -> Bundler.toBundle(new TestClassMissingDefaultConstructor(1)));
-    }
+    //TODO(rampara): Investigate why default constructor is still found when running with
+    // robolectric.
+//    @Test
+//    public void classMissingDefaultConstructorSerialization_throwsBundlerException() {
+//        assertThrows(
+//                BundlerException.class,
+//                () -> Bundler.toBundle(new TestClassMissingDefaultConstructor(1)));
+//    }
 
     @Test
     public void arraySerialization_throwsBundlerException() {
@@ -551,8 +547,7 @@
 
     @Test
     public void imageCompat_dejetify() throws BundlerException {
-        CarIcon image = CarIcon.of(IconCompat.createWithResource(mContext, R.drawable.ic_test_1));
-
+        CarIcon image = TestUtils.getTestCarIcon(mContext, "ic_test_1");
         Bundle bundle = Bundler.toBundle(image);
 
         // Get the field for the image, and re-write it with a "jetified" key.
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/CarAppServiceController.java b/car/app/app/src/test/java/androidx/car/app/testing/CarAppServiceController.java
similarity index 75%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/CarAppServiceController.java
rename to car/app/app/src/test/java/androidx/car/app/testing/CarAppServiceController.java
index 709f742..1edbdd4 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/testing/CarAppServiceController.java
+++ b/car/app/app/src/test/java/androidx/car/app/testing/CarAppServiceController.java
@@ -24,9 +24,11 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.car.app.AppInfo;
 import androidx.car.app.CarAppService;
 import androidx.car.app.HostInfo;
 import androidx.car.app.ICarApp;
+import androidx.car.app.Session;
 import androidx.lifecycle.Lifecycle.State;
 
 import java.lang.reflect.Field;
@@ -35,11 +37,11 @@
 /**
  * A controller that allows testing of a {@link CarAppService}.
  *
- * <p>This contoller allows:
+ * <p>This controller allows:
  *
  * <ul>
  *   <li>Sending different {@link Intent}s to the {@link CarAppService}'s {@link
- *       CarAppService#onCreateScreen} and {@link CarAppService#onNewIntent} methods.
+ *       Session#onCreateScreen} and {@link Session#onNewIntent} methods.
  *   <li>Moving a {@link CarAppService} through its different {@link State}s.
  * </ul>
  */
@@ -50,15 +52,18 @@
 
     /** Creates a {@link CarAppServiceController} to control the provided {@link CarAppService}. */
     public static CarAppServiceController of(
-            @NonNull TestCarContext testCarContext, @NonNull CarAppService carAppService) {
+            @NonNull TestCarContext testCarContext,
+            @NonNull Session session, @NonNull CarAppService carAppService) {
         return new CarAppServiceController(
-                requireNonNull(carAppService), requireNonNull(testCarContext));
+                requireNonNull(carAppService), requireNonNull(session),
+                requireNonNull(testCarContext));
     }
 
     /**
      * Initializes the {@link CarAppService} that is being controlled.
      *
-     * <p>This will send an empty {@link Intent} to {@link CarAppService#onCreateScreen}.
+     * <p>This will send an empty {@link Intent} to the {@link Session} returned from
+     * {@link CarAppService#onCreateSession}.
      */
     public CarAppServiceController create() {
         return create(
@@ -69,7 +74,7 @@
     /**
      * Initializes the {@link CarAppService} that is being controlled.
      *
-     * <p>This will send the provided {@link Intent} to {@link CarAppService#onCreateScreen}.
+     * <p>This will send the provided {@link Intent} to {@link Session#onCreateScreen}.
      */
     public CarAppServiceController create(@NonNull Intent intent) {
         Objects.requireNonNull(intent);
@@ -103,7 +108,7 @@
     /**
      * Starts the {@link CarAppService} that is being controlled.
      *
-     * @see CarAppService#getLifecycle
+     * @see Session#getLifecycle
      */
     public CarAppServiceController start() {
         try {
@@ -118,7 +123,7 @@
     /**
      * Resumes the {@link CarAppService} that is being controlled.
      *
-     * @see CarAppService#getLifecycle
+     * @see Session#getLifecycle
      */
     public CarAppServiceController resume() {
         try {
@@ -133,7 +138,7 @@
     /**
      * Pauses the {@link CarAppService} that is being controlled.
      *
-     * @see CarAppService#getLifecycle
+     * @see Session#getLifecycle
      */
     public CarAppServiceController pause() {
         try {
@@ -148,7 +153,7 @@
     /**
      * Stops the {@link CarAppService} that is being controlled.
      *
-     * @see CarAppService#getLifecycle
+     * @see Session#getLifecycle
      */
     public CarAppServiceController stop() {
         try {
@@ -162,11 +167,10 @@
     /**
      * Destroys the {@link CarAppService} that is being controlled.
      *
-     * @see CarAppService#getLifecycle
+     * @see Session#getLifecycle
      */
     public CarAppServiceController destroy() {
         mCarAppService.onUnbind(new Intent());
-        mCarAppService.onCarAppFinished();
         mCarAppService.onDestroy();
         return this;
     }
@@ -182,6 +186,17 @@
         }
     }
 
+    public void setAppInfo(@Nullable AppInfo appInfo) {
+        try {
+            Field appInfoField = CarAppService.class.getDeclaredField("mAppInfo");
+            appInfoField.setAccessible(true);
+            appInfoField.set(mCarAppService, appInfo);
+        } catch (ReflectiveOperationException e) {
+            throw new IllegalStateException(
+                    "Failed to set CarAppService appInfo value for testing", e);
+        }
+    }
+
     /** Retrieves the {@link CarAppService} that is being controlled. */
     @NonNull
     public CarAppService get() {
@@ -189,19 +204,24 @@
     }
 
     private CarAppServiceController(
-            CarAppService carAppService, @NonNull TestCarContext testCarContext) {
+            CarAppService carAppService,
+            @NonNull Session session, @NonNull TestCarContext testCarContext) {
         this.mCarAppService = carAppService;
         this.mTestCarContext = testCarContext;
 
-        // Use reflection to inject the TestCarContext into the Screen.
+        // Use reflection to inject the Session and TestCarContext into the CarAppService.
         try {
-            Field registry = CarAppService.class.getDeclaredField("mRegistry");
-            registry.setAccessible(true);
-            registry.set(carAppService, testCarContext.getLifecycleOwner().mRegistry);
+            Field currentSession = CarAppService.class.getDeclaredField("mCurrentSession");
+            currentSession.setAccessible(true);
+            currentSession.set(carAppService, session);
 
-            Field carContext = CarAppService.class.getDeclaredField("mCarContext");
+            Field registry = Session.class.getDeclaredField("mRegistry");
+            registry.setAccessible(true);
+            registry.set(session, testCarContext.getLifecycleOwner().mRegistry);
+
+            Field carContext = Session.class.getDeclaredField("mCarContext");
             carContext.setAccessible(true);
-            carContext.set(carAppService, testCarContext);
+            carContext.set(session, testCarContext);
         } catch (ReflectiveOperationException e) {
             throw new IllegalStateException(
                     "Failed to set internal CarAppService values for testing", e);
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/FakeHost.java b/car/app/app/src/test/java/androidx/car/app/testing/FakeHost.java
similarity index 96%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/FakeHost.java
rename to car/app/app/src/test/java/androidx/car/app/testing/FakeHost.java
index 1376932..2ceefdb 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/testing/FakeHost.java
+++ b/car/app/app/src/test/java/androidx/car/app/testing/FakeHost.java
@@ -30,7 +30,7 @@
 import androidx.car.app.CarContext;
 import androidx.car.app.IAppHost;
 import androidx.car.app.ICarHost;
-import androidx.car.app.ISurfaceListener;
+import androidx.car.app.ISurfaceCallback;
 import androidx.car.app.Screen;
 import androidx.car.app.navigation.INavigationHost;
 import androidx.car.app.serialization.Bundleable;
@@ -73,7 +73,7 @@
 
         Bundle extras = new Bundle(1);
         extras.putBinder(
-                CarContext.START_CAR_APP_BINDER_KEY,
+                CarContext.EXTRA_START_CAR_APP_BINDER_KEY,
                 mTestCarContext.getStartCarAppStub().asBinder());
         Intent extraData = new Intent().putExtras(extras);
 
@@ -131,7 +131,7 @@
         }
 
         @Override
-        public void setSurfaceListener(@Nullable ISurfaceListener listener) {
+        public void setSurfaceCallback(@Nullable ISurfaceCallback callback) {
             // No-op.
         }
     }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/ScreenController.java b/car/app/app/src/test/java/androidx/car/app/testing/ScreenController.java
similarity index 100%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/ScreenController.java
rename to car/app/app/src/test/java/androidx/car/app/testing/ScreenController.java
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/TestAppManager.java b/car/app/app/src/test/java/androidx/car/app/testing/TestAppManager.java
similarity index 86%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/TestAppManager.java
rename to car/app/app/src/test/java/androidx/car/app/testing/TestAppManager.java
index 9a5b1f0..a843265 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/testing/TestAppManager.java
+++ b/car/app/app/src/test/java/androidx/car/app/testing/TestAppManager.java
@@ -23,7 +23,7 @@
 import androidx.car.app.AppManager;
 import androidx.car.app.HostDispatcher;
 import androidx.car.app.Screen;
-import androidx.car.app.SurfaceListener;
+import androidx.car.app.SurfaceCallback;
 import androidx.car.app.model.Template;
 
 import java.util.ArrayList;
@@ -35,35 +35,35 @@
  * <p>This class will track the following usages of the {@link AppManager} throughout your test:
  *
  * <ul>
- *   <li>All {@link SurfaceListener}s set via calling {@link AppManager#setSurfaceListener}.
+ *   <li>All {@link SurfaceCallback}s set via calling {@link AppManager#setSurfaceCallback}.
  *   <li>The {@link Template}s returned from {@link Screen#onGetTemplate} due to invalidate calls
  *       via {@link AppManager#invalidate}.
  *   <li>All toasts shown via calling {@link AppManager#showToast}.
  * </ul>
  */
 public class TestAppManager extends AppManager {
-    private final List<SurfaceListener> mSurfaceListeners = new ArrayList<>();
+    private final List<SurfaceCallback> mSurfaceCallbacks = new ArrayList<>();
     private final List<CharSequence> mToastsShown = new ArrayList<>();
     private final List<Pair<Screen, Template>> mTemplatesReturned = new ArrayList<>();
 
     /** Resets the values tracked by this {@link TestAppManager} and all {@link ScreenController}
      * s. */
     public void reset() {
-        mSurfaceListeners.clear();
+        mSurfaceCallbacks.clear();
         mToastsShown.clear();
         mTemplatesReturned.clear();
     }
 
     /**
-     * Retrieves all the {@link SurfaceListener}s set via {@link AppManager#setSurfaceListener}.
+     * Retrieves all the {@link SurfaceCallback}s set via {@link AppManager#setSurfaceCallback}.
      *
      * <p>The listeners are stored in order of calls.
      *
      * <p>The listeners will be stored until {@link #reset} is called.
      */
     @NonNull
-    public List<SurfaceListener> getSurfaceListeners() {
-        return mSurfaceListeners;
+    public List<SurfaceCallback> getSurfaceListeners() {
+        return mSurfaceCallbacks;
     }
 
     /**
@@ -93,8 +93,8 @@
     }
 
     @Override
-    public void setSurfaceListener(@Nullable SurfaceListener surfaceListener) {
-        mSurfaceListeners.add(surfaceListener);
+    public void setSurfaceCallback(@Nullable SurfaceCallback surfaceCallback) {
+        mSurfaceCallbacks.add(surfaceCallback);
     }
 
     @Override
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/TestCarContext.java b/car/app/app/src/test/java/androidx/car/app/testing/TestCarContext.java
similarity index 98%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/TestCarContext.java
rename to car/app/app/src/test/java/androidx/car/app/testing/TestCarContext.java
index 4f09fbd..19e5091 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/testing/TestCarContext.java
+++ b/car/app/app/src/test/java/androidx/car/app/testing/TestCarContext.java
@@ -86,7 +86,7 @@
         } else if (serviceClass.isInstance(mTestNavigationManager)) {
             serviceName = NAVIGATION_SERVICE;
         } else if (serviceClass.isInstance(mTestScreenManager)) {
-            serviceName = SCREEN_MANAGER_SERVICE;
+            serviceName = SCREEN_SERVICE;
         } else {
             serviceName = getCarServiceName(serviceClass);
         }
@@ -107,7 +107,7 @@
                 return mTestAppManager;
             case CarContext.NAVIGATION_SERVICE:
                 return mTestNavigationManager;
-            case CarContext.SCREEN_MANAGER_SERVICE:
+            case CarContext.SCREEN_SERVICE:
                 return mTestScreenManager;
             default:
                 // Fall out
@@ -226,7 +226,7 @@
         this.mFakeHost = new FakeHost(this);
         this.mTestLifecycleOwner = testLifecycleOwner;
         this.mTestAppManager = new TestAppManager(this, hostDispatcher);
-        this.mTestNavigationManager = new TestNavigationManager(hostDispatcher);
+        this.mTestNavigationManager = new TestNavigationManager(this, hostDispatcher);
         this.mTestScreenManager = new TestScreenManager(this);
     }
 }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/TestLifecycleOwner.java b/car/app/app/src/test/java/androidx/car/app/testing/TestLifecycleOwner.java
similarity index 100%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/TestLifecycleOwner.java
rename to car/app/app/src/test/java/androidx/car/app/testing/TestLifecycleOwner.java
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/TestOnDoneCallbackStub.java b/car/app/app/src/test/java/androidx/car/app/testing/TestOnDoneCallbackStub.java
similarity index 100%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/TestOnDoneCallbackStub.java
rename to car/app/app/src/test/java/androidx/car/app/testing/TestOnDoneCallbackStub.java
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/TestScreenManager.java b/car/app/app/src/test/java/androidx/car/app/testing/TestScreenManager.java
similarity index 94%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/TestScreenManager.java
rename to car/app/app/src/test/java/androidx/car/app/testing/TestScreenManager.java
index 89e1445..85f375e 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/testing/TestScreenManager.java
+++ b/car/app/app/src/test/java/androidx/car/app/testing/TestScreenManager.java
@@ -17,7 +17,7 @@
 package androidx.car.app.testing;
 
 import androidx.annotation.NonNull;
-import androidx.car.app.OnScreenResultCallback;
+import androidx.car.app.OnScreenResultListener;
 import androidx.car.app.Screen;
 import androidx.car.app.ScreenManager;
 import androidx.lifecycle.Lifecycle.State;
@@ -92,9 +92,9 @@
 
     @Override
     public void pushForResult(
-            @NonNull Screen screen, @NonNull OnScreenResultCallback onScreenResultCallback) {
+            @NonNull Screen screen, @NonNull OnScreenResultListener onScreenResultListener) {
         mScreensPushed.add(screen);
-        super.pushForResult(screen, onScreenResultCallback);
+        super.pushForResult(screen, onScreenResultListener);
     }
 
     @Override
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/model/ControllerUtil.java b/car/app/app/src/test/java/androidx/car/app/testing/model/ControllerUtil.java
similarity index 100%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/model/ControllerUtil.java
rename to car/app/app/src/test/java/androidx/car/app/testing/model/ControllerUtil.java
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/model/LaneController.java b/car/app/app/src/test/java/androidx/car/app/testing/model/LaneController.java
similarity index 100%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/model/LaneController.java
rename to car/app/app/src/test/java/androidx/car/app/testing/model/LaneController.java
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/navigation/TestNavigationManager.java b/car/app/app/src/test/java/androidx/car/app/testing/navigation/TestNavigationManager.java
similarity index 81%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/navigation/TestNavigationManager.java
rename to car/app/app/src/test/java/androidx/car/app/testing/navigation/TestNavigationManager.java
index db99643..e1b8afa 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/testing/navigation/TestNavigationManager.java
+++ b/car/app/app/src/test/java/androidx/car/app/testing/navigation/TestNavigationManager.java
@@ -22,8 +22,9 @@
 import androidx.annotation.Nullable;
 import androidx.car.app.HostDispatcher;
 import androidx.car.app.navigation.NavigationManager;
-import androidx.car.app.navigation.NavigationManagerListener;
+import androidx.car.app.navigation.NavigationManagerCallback;
 import androidx.car.app.navigation.model.Trip;
+import androidx.car.app.testing.TestCarContext;
 import androidx.car.app.testing.navigation.model.TripController;
 
 import java.util.ArrayList;
@@ -37,7 +38,8 @@
  *
  * <ul>
  *   <li>All the {@link Trip}s sent via {@link NavigationManager#updateTrip}.
- *   <li>All the {@link NavigationManagerListener}s set via {@link NavigationManager#setListener}.
+ *   <li>All the {@link NavigationManagerCallback}s set via
+ *   {@link NavigationManager#setNavigationManagerCallback}.
  *   <li>Count of times that the navigation was started via {@link
  *       NavigationManager#navigationStarted()}.
  *   <li>Count of times that the navigation was ended via {@link NavigationManager#navigationEnded}.
@@ -45,7 +47,7 @@
  */
 public class TestNavigationManager extends NavigationManager {
     private final List<TripController> mTripsSent = new ArrayList<>();
-    private final List<NavigationManagerListener> mListenersSet = new ArrayList<>();
+    private final List<NavigationManagerCallback> mListenersSet = new ArrayList<>();
     private int mNavigationStartedCount;
     private int mNavigationEndedCount;
 
@@ -70,15 +72,15 @@
     }
 
     /**
-     * Retrieves all the {@link NavigationManagerListener}s added via {@link
-     * NavigationManager#setListener(NavigationManagerListener)}.
+     * Retrieves all the {@link NavigationManagerCallback}s added via {@link
+     * NavigationManager#setNavigationManagerCallback(NavigationManagerCallback)}.
      *
      * <p>The listeners are stored in order of calls.
      *
      * <p>The listeners will be stored until {@link #reset} is called.
      */
     @NonNull
-    public List<NavigationManagerListener> getNavigationManagerListenersSet() {
+    public List<NavigationManagerCallback> getNavigationManagerCallbacksSet() {
         return mListenersSet;
     }
 
@@ -105,9 +107,9 @@
     }
 
     @Override
-    public void setListener(@Nullable NavigationManagerListener listener) {
+    public void setNavigationManagerCallback(@Nullable NavigationManagerCallback listener) {
         mListenersSet.add(listener);
-        super.setListener(listener);
+        super.setNavigationManagerCallback(listener);
     }
 
     @Override
@@ -122,7 +124,7 @@
         super.navigationEnded();
     }
 
-    public TestNavigationManager(HostDispatcher hostDispatcher) {
-        super(hostDispatcher);
+    public TestNavigationManager(TestCarContext testCarContext, HostDispatcher hostDispatcher) {
+        super(testCarContext, hostDispatcher);
     }
 }
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/navigation/model/DestinationController.java b/car/app/app/src/test/java/androidx/car/app/testing/navigation/model/DestinationController.java
similarity index 100%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/navigation/model/DestinationController.java
rename to car/app/app/src/test/java/androidx/car/app/testing/navigation/model/DestinationController.java
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/navigation/model/StepController.java b/car/app/app/src/test/java/androidx/car/app/testing/navigation/model/StepController.java
similarity index 100%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/navigation/model/StepController.java
rename to car/app/app/src/test/java/androidx/car/app/testing/navigation/model/StepController.java
diff --git a/car/app/app/src/androidTest/java/androidx/car/app/testing/navigation/model/TripController.java b/car/app/app/src/test/java/androidx/car/app/testing/navigation/model/TripController.java
similarity index 98%
rename from car/app/app/src/androidTest/java/androidx/car/app/testing/navigation/model/TripController.java
rename to car/app/app/src/test/java/androidx/car/app/testing/navigation/model/TripController.java
index c6e9e10..692d408 100644
--- a/car/app/app/src/androidTest/java/androidx/car/app/testing/navigation/model/TripController.java
+++ b/car/app/app/src/test/java/androidx/car/app/testing/navigation/model/TripController.java
@@ -40,7 +40,7 @@
  *   <li>The {@link TravelEstimate}s set via {@link Trip.Builder#addDestinationTravelEstimate}.
  *   <li>The {@link TravelEstimate}s set via {@link Trip.Builder#addStepTravelEstimate}.
  *   <li>The current road set via {@link Trip.Builder#setCurrentRoad}.
- *   <li>The loading state set via {@link Trip.Builder#setIsLoading}.
+ *   <li>The loading state set via {@link Trip.Builder#setLoading}.
  * </ul>
  */
 public class TripController {
diff --git a/car/app/app/src/test/java/androidx/car/app/versioning/CarAppApiLevelsTest.java b/car/app/app/src/test/java/androidx/car/app/versioning/CarAppApiLevelsTest.java
new file mode 100644
index 0000000..2d5f8ec
--- /dev/null
+++ b/car/app/app/src/test/java/androidx/car/app/versioning/CarAppApiLevelsTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.car.app.versioning;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public class CarAppApiLevelsTest {
+    @Test
+    public void isValid_apiLowerThanOldest_notValid() {
+        assertThat(CarAppApiLevels.isValid(CarAppApiLevels.getOldest() - 1)).isFalse();
+    }
+
+    @Test
+    public void isValid_apiHigherThanLatest_notValid() {
+        assertThat(CarAppApiLevels.isValid(CarAppApiLevels.getOldest() + 1)).isFalse();
+    }
+}
diff --git a/car/app/app/src/androidTest/res/drawable-hdpi/banana.png b/car/app/app/src/test/res/drawable-hdpi/banana.png
similarity index 100%
rename from car/app/app/src/androidTest/res/drawable-hdpi/banana.png
rename to car/app/app/src/test/res/drawable-hdpi/banana.png
Binary files differ
diff --git a/car/app/app/src/androidTest/res/drawable-ldpi/banana.png b/car/app/app/src/test/res/drawable-ldpi/banana.png
similarity index 100%
rename from car/app/app/src/androidTest/res/drawable-ldpi/banana.png
rename to car/app/app/src/test/res/drawable-ldpi/banana.png
Binary files differ
diff --git a/car/app/app/src/androidTest/res/drawable-mdpi/banana.png b/car/app/app/src/test/res/drawable-mdpi/banana.png
similarity index 100%
rename from car/app/app/src/androidTest/res/drawable-mdpi/banana.png
rename to car/app/app/src/test/res/drawable-mdpi/banana.png
Binary files differ
diff --git a/car/app/app/src/androidTest/res/drawable-xhdpi/banana.png b/car/app/app/src/test/res/drawable-xhdpi/banana.png
similarity index 100%
rename from car/app/app/src/androidTest/res/drawable-xhdpi/banana.png
rename to car/app/app/src/test/res/drawable-xhdpi/banana.png
Binary files differ
diff --git a/car/app/app/src/androidTest/res/drawable-xxhdpi/banana.png b/car/app/app/src/test/res/drawable-xxhdpi/banana.png
similarity index 100%
rename from car/app/app/src/androidTest/res/drawable-xxhdpi/banana.png
rename to car/app/app/src/test/res/drawable-xxhdpi/banana.png
Binary files differ
diff --git a/car/app/app/src/androidTest/res/drawable/banana.png b/car/app/app/src/test/res/drawable/banana.png
similarity index 100%
rename from car/app/app/src/androidTest/res/drawable/banana.png
rename to car/app/app/src/test/res/drawable/banana.png
Binary files differ
diff --git a/car/app/app/src/androidTest/res/drawable/ic_test_1.xml b/car/app/app/src/test/res/drawable/ic_test_1.xml
similarity index 100%
rename from car/app/app/src/androidTest/res/drawable/ic_test_1.xml
rename to car/app/app/src/test/res/drawable/ic_test_1.xml
diff --git a/car/app/app/src/androidTest/res/drawable/ic_test_2.xml b/car/app/app/src/test/res/drawable/ic_test_2.xml
similarity index 100%
rename from car/app/app/src/androidTest/res/drawable/ic_test_2.xml
rename to car/app/app/src/test/res/drawable/ic_test_2.xml
diff --git a/car/app/app/src/test/resources/robolectric.properties b/car/app/app/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..ce87047
--- /dev/null
+++ b/car/app/app/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# Robolectric currently doesn't support API 30, so we have to explicitly specify 29 as the target
+# sdk for now. Remove when no longer necessary.
+sdk=29
diff --git a/collection/README.md b/collection/README.md
index 51cefdc..b4d31f1 100644
--- a/collection/README.md
+++ b/collection/README.md
@@ -7,7 +7,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/collection)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/collection/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/collection/)
 
 [Reference documentation](https://developer.android.com/reference/androidx/classes.html)
 
diff --git a/collection/collection/src/test/java/androidx/collection/IndexBasedArrayIteratorTest.java b/collection/collection/src/test/java/androidx/collection/IndexBasedArrayIteratorTest.java
new file mode 100644
index 0000000..feafa5c
--- /dev/null
+++ b/collection/collection/src/test/java/androidx/collection/IndexBasedArrayIteratorTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.collection;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public class IndexBasedArrayIteratorTest {
+
+    @Test
+    public void iterateAll() {
+        Iterator<String> iterator = new ArraySet<>(setOf("a", "b", "c")).iterator();
+        assertThat(toList(iterator)).containsExactly("a", "b", "c");
+    }
+
+    @Test
+    public void iterateEmptyList() {
+        Iterator<String> iterator = new ArraySet<String>().iterator();
+        assertThat(iterator.hasNext()).isFalse();
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void iterateEmptyListThrowsUponNext() {
+        Iterator<String> iterator = new ArraySet<String>().iterator();
+        iterator.next();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void removeSameItemTwice() {
+        Iterator<String> iterator = new ArraySet<>(listOf("a", "b", "c")).iterator();
+        iterator.next(); // move to next
+        iterator.remove();
+        iterator.remove();
+    }
+
+
+    @Test
+    public void removeLast() {
+        removeViaIterator(
+                /* original= */ setOf("a", "b", "c"),
+                /* toBeRemoved= */ setOf("c"),
+                /* expected= */ setOf("a", "b"));
+    }
+
+    @Test
+    public void removeFirst() {
+        removeViaIterator(
+                /* original= */ setOf("a", "b", "c"),
+                /* toBeRemoved= */ setOf("a"),
+                /* expected= */ setOf("b", "c"));
+    }
+
+    @Test
+    public void removeMid() {
+        removeViaIterator(
+                /* original= */ setOf("a", "b", "c"),
+                /* toBeRemoved= */ setOf("b"),
+                /* expected= */ setOf("a", "c"));
+    }
+
+    @Test
+    public void removeConsecutive() {
+        removeViaIterator(
+                /* original= */ setOf("a", "b", "c", "d"),
+                /* toBeRemoved= */ setOf("b", "c"),
+                /* expected= */ setOf("a", "d"));
+    }
+
+    @Test
+    public void removeLastTwo() {
+        removeViaIterator(
+                /* original= */ setOf("a", "b", "c", "d"),
+                /* toBeRemoved= */ setOf("c", "d"),
+                /* expected= */ setOf("a", "b"));
+    }
+
+    @Test
+    public void removeFirstTwo() {
+        removeViaIterator(
+                /* original= */ setOf("a", "b", "c", "d"),
+                /* toBeRemoved= */ setOf("a", "b"),
+                /* expected= */ setOf("c", "d"));
+    }
+
+    @Test
+    public void removeMultiple() {
+        removeViaIterator(
+                /* original= */ setOf("a", "b", "c", "d"),
+                /* toBeRemoved= */ setOf("a", "c"),
+                /* expected= */ setOf("b", "d"));
+    }
+
+    private static void removeViaIterator(
+            Set<String> original,
+            Set<String> toBeRemoved,
+            Set<String> expected) {
+        ArraySet<String> subject = new ArraySet<>(original);
+        Iterator<String> iterator = subject.iterator();
+        while (iterator.hasNext()) {
+            String next = iterator.next();
+            if (toBeRemoved.contains(next)) {
+                iterator.remove();
+            }
+        }
+        assertThat(subject).containsExactlyElementsIn(expected);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <V> List<V> listOf(V... values) {
+        List<V> list = new ArrayList<>();
+        for (V value : values) {
+            list.add(value);
+        }
+        return list;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <V> Set<V> setOf(V... values) {
+        Set<V> set = new HashSet<>();
+        for (V value : values) {
+            set.add(value);
+        }
+        return set;
+    }
+
+    private static <V> List<V> toList(Iterator<V> iterator) {
+        List<V> list = new ArrayList<>();
+        while (iterator.hasNext()) {
+            list.add(iterator.next());
+        }
+        return list;
+    }
+}
diff --git a/collection/collection/src/test/java/androidx/collection/IndexBasedArrayIteratorTest.kt b/collection/collection/src/test/java/androidx/collection/IndexBasedArrayIteratorTest.kt
deleted file mode 100644
index e9326f2..0000000
--- a/collection/collection/src/test/java/androidx/collection/IndexBasedArrayIteratorTest.kt
+++ /dev/null
@@ -1,121 +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.collection
-
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class IndexBasedArrayIteratorTest {
-
-    @Test
-    fun iterateAll() {
-        val iterator = ArraySet(setOf("a", "b", "c")).iterator()
-        assertThat(iterator.asSequence().toList()).containsExactly("a", "b", "c")
-    }
-
-    @Test
-    fun iterateEmptyList() {
-        val iterator = ArraySet<String>().iterator()
-        assertThat(iterator.hasNext()).isFalse()
-
-        assertThat(
-            runCatching {
-                iterator.next()
-            }.exceptionOrNull()
-        ).isInstanceOf(NoSuchElementException::class.java)
-    }
-
-    @Test
-    fun removeSameItemTwice() {
-        val iterator = ArraySet(listOf("a", "b", "c")).iterator()
-        iterator.next() // move to next
-        iterator.remove()
-        assertThat(
-            runCatching {
-                iterator.remove()
-            }.exceptionOrNull()
-        ).isInstanceOf(IllegalStateException::class.java)
-    }
-
-    @Test
-    fun removeLast() = removeViaIterator(
-        original = setOf("a", "b", "c"),
-        toBeRemoved = setOf("c"),
-        expected = setOf("a", "b")
-    )
-
-    @Test
-    fun removeFirst() = removeViaIterator(
-        original = setOf("a", "b", "c"),
-        toBeRemoved = setOf("a"),
-        expected = setOf("b", "c")
-    )
-
-    @Test
-    fun removeMid() = removeViaIterator(
-        original = setOf("a", "b", "c"),
-        toBeRemoved = setOf("b"),
-        expected = setOf("a", "c")
-    )
-
-    @Test
-    fun removeConsecutive() = removeViaIterator(
-        original = setOf("a", "b", "c", "d"),
-        toBeRemoved = setOf("b", "c"),
-        expected = setOf("a", "d")
-    )
-
-    @Test
-    fun removeLastTwo() = removeViaIterator(
-        original = setOf("a", "b", "c", "d"),
-        toBeRemoved = setOf("c", "d"),
-        expected = setOf("a", "b")
-    )
-
-    @Test
-    fun removeFirstTwo() = removeViaIterator(
-        original = setOf("a", "b", "c", "d"),
-        toBeRemoved = setOf("a", "b"),
-        expected = setOf("c", "d")
-    )
-
-    @Test
-    fun removeMultiple() = removeViaIterator(
-        original = setOf("a", "b", "c", "d"),
-        toBeRemoved = setOf("a", "c"),
-        expected = setOf("b", "d")
-    )
-
-    private fun removeViaIterator(
-        original: Set<String>,
-        toBeRemoved: Set<String>,
-        expected: Set<String>
-    ) {
-        val subject = ArraySet(original)
-        val iterator = subject.iterator()
-        while (iterator.hasNext()) {
-            val next = iterator.next()
-            if (next in toBeRemoved) {
-                iterator.remove()
-            }
-        }
-        assertThat(subject).containsExactlyElementsIn(expected)
-    }
-}
diff --git a/compose/README.md b/compose/README.md
index 0a2c653..6b2ad38 100644
--- a/compose/README.md
+++ b/compose/README.md
@@ -5,14 +5,14 @@
 ## Syntax
 Jetpack Compose uses composable functions instead of XML layouts to define UI components. You can
 see this in action in the demos, like `androidx.compose.material.demos.ButtonDemo.kt`. More
-information can be found in the [compiler README](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/compose/compiler/README.md).
+information can be found in the [compiler README](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/compiler/README.md).
 
 ## Compiler
 Composable functions are built using a custom Kotlin compiler plugin. More information about the
-compiler plugin is available in [this README](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/compose/compiler/README.md).
+compiler plugin is available in [this README](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/compiler/README.md).
 
 ## Getting started
-To try out Jetpack Compose you need to set up the toolchain for AndroidX development. Follow the process [here](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/README.md) to check out the code.
+To try out Jetpack Compose you need to set up the toolchain for AndroidX development. Follow the process [here](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/README.md) to check out the code.
 
 To start the required version of Android Studio, you need to run the `ANDROIDX_PROJECTS=COMPOSE ./gradlew studio`
 
@@ -36,11 +36,11 @@
 Library code for Jetpack Compose lives under the `frameworks/support/compose` directory. Additionally, sample code can be found within each module in the `integration-tests` subdirectories.
 
 ## Feedback
-To provide feedback or report bugs, please refer to the main [AndroidX contribution guide](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/README.md) and report your bugs [here](https://issuetracker.google.com/issues/new?component=612128)
+To provide feedback or report bugs, please refer to the main [AndroidX contribution guide](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/README.md) and report your bugs [here](https://issuetracker.google.com/issues/new?component=612128)
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/compose)
 
-[Browse source](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:compose/)
+[Browse source](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/)
 
 [Existing open bugs](https://issuetracker.google.com/issues?q=componentid:612128%20status:open)
 
diff --git a/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/ComplexInteractions.kt b/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/ComplexInteractions.kt
index 223f3c9..5a44235 100644
--- a/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/ComplexInteractions.kt
+++ b/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/ComplexInteractions.kt
@@ -30,7 +30,7 @@
 import androidx.compose.integration.demos.common.ComposableDemo
 import androidx.compose.integration.demos.common.DemoCategory
 import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants
+import androidx.compose.material.ButtonDefaults
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
@@ -120,7 +120,7 @@
         }
     Button(
         onClick = { state.value = !state.value },
-        colors = ButtonConstants.defaultButtonColors(backgroundColor = color)
+        colors = ButtonDefaults.buttonColors(backgroundColor = color)
     ) {
         Text("Click me")
     }
diff --git a/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/FocusInteropAndroidInCompose.kt b/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/FocusInteropAndroidInCompose.kt
index ca16cf3..91c4bd6 100644
--- a/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/FocusInteropAndroidInCompose.kt
+++ b/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/FocusInteropAndroidInCompose.kt
@@ -19,7 +19,6 @@
 import android.annotation.SuppressLint
 import android.widget.EditText
 import android.widget.LinearLayout
-import android.widget.RelativeLayout
 import android.widget.TextView
 import androidx.compose.foundation.layout.Arrangement.SpaceEvenly
 import androidx.compose.foundation.layout.Column
@@ -29,17 +28,14 @@
 import androidx.compose.material.Text
 import androidx.compose.material.TextField
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment.Companion.CenterVertically
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.platform.setContent
+import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 
-@OptIn(ExperimentalFocus::class)
 @Composable
 fun EditTextInteropDemo() {
     Column {
@@ -73,8 +69,8 @@
                                 }
                             )
                             addView(
-                                RelativeLayout(it).apply {
-                                    setContent(Recomposer.current()) {
+                                ComposeView(it).apply {
+                                    setContent {
                                         val text = remember { mutableStateOf("") }
                                         TextField(text.value, onValueChange = { text.value = it })
                                     }
diff --git a/compose/animation/animation-core/api/api_lint.ignore b/compose/animation/animation-core/api/api_lint.ignore
index 8682399..1c23773 100644
--- a/compose/animation/animation-core/api/api_lint.ignore
+++ b/compose/animation/animation-core/api/api_lint.ignore
@@ -1,10 +1,4 @@
 // Baseline format: 1.0
-ArrayReturn: androidx.compose.animation.core.TransitionDefinition#snapTransition(kotlin.Pair<? extends T,? extends T>[], T) parameter #0:
-    Method parameter should be Collection<Pair> (or subclass) instead of raw array; was `kotlin.Pair<? extends T,? extends T>[]`
-ArrayReturn: androidx.compose.animation.core.TransitionDefinition#transition(kotlin.Pair<? extends T,? extends T>[], kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.TransitionSpec<T>,kotlin.Unit>) parameter #0:
-    Method parameter should be Collection<Pair> (or subclass) instead of raw array; was `kotlin.Pair<? extends T,? extends T>[]`
-
-
 AutoBoxing: androidx.compose.animation.core.CubicBezierEasing#invoke(float):
     Must avoid boxed primitives (`java.lang.Float`)
 AutoBoxing: androidx.compose.animation.core.DecayAnimation#getTargetValue():
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index 2603144..41d1e7f 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -55,7 +55,7 @@
   public final class AnimationConstants {
     field public static final int DefaultDurationMillis = 300; // 0x12c
     field public static final androidx.compose.animation.core.AnimationConstants INSTANCE;
-    field public static final int Infinite = 2147483647; // 0x7fffffff
+    field @Deprecated public static final int Infinite = 2147483647; // 0x7fffffff
   }
 
   public enum AnimationEndReason {
@@ -115,6 +115,7 @@
     method public static androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> AnimationState-SZyJPdc(float initialValue, optional float initialVelocity, optional long lastFrameTime, optional long endTime, optional boolean isRunning);
     method public static androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> copy-mN48zRs(androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D>, optional float value, optional float velocity, optional long lastFrameTime, optional long endTime, optional boolean isRunning);
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.AnimationState<T,V> copy-upwry94(androidx.compose.animation.core.AnimationState<T,V>, optional T? value, optional V? velocityVector, optional long lastFrameTime, optional long endTime, optional boolean isRunning);
+    method public static <T, V extends androidx.compose.animation.core.AnimationVector> V createZeroVectorFrom(androidx.compose.animation.core.TwoWayConverter<T,V>, T? value);
     method public static float getVelocity(androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D>);
     method public static float getVelocity(androidx.compose.animation.core.AnimationScope<java.lang.Float,androidx.compose.animation.core.AnimationVector1D>);
     method public static boolean isFinished(androidx.compose.animation.core.AnimationState<?,?>);
@@ -235,7 +236,7 @@
     method public void dispatchTime$metalava_module(long frameTimeMillis);
   }
 
-  public interface DurationBasedAnimationSpec<T> extends androidx.compose.animation.core.AnimationSpec<T> {
+  public interface DurationBasedAnimationSpec<T> extends androidx.compose.animation.core.FiniteAnimationSpec<T> {
     method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
   }
 
@@ -257,6 +258,10 @@
     property public float absVelocityThreshold;
   }
 
+  public interface FiniteAnimationSpec<T> extends androidx.compose.animation.core.AnimationSpec<T> {
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+  }
+
   public interface FloatAnimationSpec extends androidx.compose.animation.core.AnimationSpec<java.lang.Float> {
     method public long getDurationMillis(float start, float end, float startVelocity);
     method public default float getEndVelocity(float start, float end, float startVelocity);
@@ -309,6 +314,15 @@
     property public final int duration;
   }
 
+  public final class InfiniteRepeatableSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+    ctor public InfiniteRepeatableSpec(androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, androidx.compose.animation.core.RepeatMode repeatMode);
+    method public androidx.compose.animation.core.DurationBasedAnimationSpec<T> getAnimation();
+    method public androidx.compose.animation.core.RepeatMode getRepeatMode();
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+    property public final androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation;
+    property public final androidx.compose.animation.core.RepeatMode repeatMode;
+  }
+
   public final class IntPropKey implements androidx.compose.animation.core.PropKey<java.lang.Integer,androidx.compose.animation.core.AnimationVector1D> {
     ctor public IntPropKey(String label);
     ctor public IntPropKey();
@@ -394,8 +408,6 @@
 
   public final class PropKeyKt {
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TwoWayConverter<T,V> TwoWayConverter(kotlin.jvm.functions.Function1<? super T,? extends V> convertToVector, kotlin.jvm.functions.Function1<? super V,? extends T> convertFromVector);
-    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> getFloatToVectorConverter();
-    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<java.lang.Integer,androidx.compose.animation.core.AnimationVector1D> getIntToVectorConverter();
     method public static androidx.compose.animation.core.TwoWayConverter<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(kotlin.jvm.internal.FloatCompanionObject);
     method public static androidx.compose.animation.core.TwoWayConverter<java.lang.Integer,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(kotlin.jvm.internal.IntCompanionObject);
   }
@@ -405,18 +417,18 @@
     enum_constant public static final androidx.compose.animation.core.RepeatMode Reverse;
   }
 
-  @androidx.compose.runtime.Immutable public final class RepeatableSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+  @androidx.compose.runtime.Immutable public final class RepeatableSpec<T> implements androidx.compose.animation.core.FiniteAnimationSpec<T> {
     ctor public RepeatableSpec(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, androidx.compose.animation.core.RepeatMode repeatMode);
     method public androidx.compose.animation.core.DurationBasedAnimationSpec<T> getAnimation();
     method public int getIterations();
     method public androidx.compose.animation.core.RepeatMode getRepeatMode();
-    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
     property public final androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation;
     property public final int iterations;
     property public final androidx.compose.animation.core.RepeatMode repeatMode;
   }
 
-  @androidx.compose.runtime.Immutable public final class SnapSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+  @androidx.compose.runtime.Immutable public final class SnapSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
     ctor public SnapSpec(int delay);
     ctor public SnapSpec();
     method public int getDelay();
@@ -443,7 +455,7 @@
   public final class SpringSimulationKt {
   }
 
-  @androidx.compose.runtime.Immutable public final class SpringSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+  @androidx.compose.runtime.Immutable public final class SpringSpec<T> implements androidx.compose.animation.core.FiniteAnimationSpec<T> {
     ctor public SpringSpec(float dampingRatio, float stiffness, T? visibilityThreshold);
     ctor public SpringSpec();
     method public float getDampingRatio();
@@ -489,6 +501,25 @@
   public final class ToolingGlueKt {
   }
 
+  public final class Transition<S> {
+    method public S! getCurrentState();
+    method public S! getTargetState();
+    method public androidx.compose.animation.core.Transition.States<S> getTransitionStates();
+    method public boolean isRunning();
+    property public final S! currentState;
+    property public final boolean isRunning;
+    property public final S! targetState;
+    property public final androidx.compose.animation.core.Transition.States<S> transitionStates;
+  }
+
+  public static final class Transition.States<S> {
+    ctor public Transition.States(S? initialState, S? targetState);
+    method public S! getInitialState();
+    method public S! getTargetState();
+    property public final S! initialState;
+    property public final S! targetState;
+  }
+
   public final class TransitionAnimation<T> implements androidx.compose.animation.core.TransitionState {
     ctor public TransitionAnimation(androidx.compose.animation.core.TransitionDefinition<T> def, androidx.compose.animation.core.AnimationClockObservable clock, T? initState, String? label);
     method public operator <T, V extends androidx.compose.animation.core.AnimationVector> T! get(androidx.compose.animation.core.PropKey<T,V> propKey);
@@ -520,14 +551,30 @@
 
   public final class TransitionDefinitionKt {
     method public static <T> androidx.compose.animation.core.TransitionAnimation<T> createAnimation(androidx.compose.animation.core.TransitionDefinition<T>, androidx.compose.animation.core.AnimationClockObservable clock, optional T? initState);
+    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.AnimationSpec<T> infiniteRepeatable(androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
     method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.KeyframesSpec<T> keyframes(kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T>,kotlin.Unit> init);
-    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.AnimationSpec<T> repeatable(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
-    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.AnimationSpec<T> snap(optional int delayMillis);
+    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.RepeatableSpec<T> repeatable(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
+    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.SnapSpec<T> snap(optional int delayMillis);
     method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.SpringSpec<T> spring(optional float dampingRatio, optional float stiffness, optional T? visibilityThreshold);
     method public static <T> androidx.compose.animation.core.TransitionDefinition<T> transitionDefinition(kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.TransitionDefinition<T>,kotlin.Unit> init);
     method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.TweenSpec<T> tween(optional int durationMillis, optional int delayMillis, optional kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> easing);
   }
 
+  public final class TransitionKt {
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.Bounds> animateBounds(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.Bounds>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.Bounds> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> animateDp(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.Dp>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.Dp> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<java.lang.Float> animateFloat(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>> transitionSpec, kotlin.jvm.functions.Function1<? super S,java.lang.Float> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<java.lang.Integer> animateInt(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Integer>> transitionSpec, kotlin.jvm.functions.Function1<? super S,java.lang.Integer> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.IntOffset> animateIntOffset(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.IntOffset> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.IntSize> animateIntSize(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.IntSize> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Offset> animateOffset(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Offset>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Offset> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.Position> animatePosition(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.Position>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.Position> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Rect> animateRect(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Rect>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Rect> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Size> animateSize(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Size>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Size> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S, T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> animateValue(androidx.compose.animation.core.Transition<S>, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<T>> transitionSpec, kotlin.jvm.functions.Function1<? super S,? extends T> targetValueByState);
+    method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.Transition<T> updateTransition(T? targetState, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onFinished);
+  }
+
   public final class TransitionSpec<S> {
     method public androidx.compose.animation.core.InterruptionHandling getInterruptionHandling();
     method public S? getNextState();
@@ -561,6 +608,17 @@
     property public abstract kotlin.jvm.functions.Function1<T,V> convertToVector;
   }
 
+  public final class VectorConvertersKt {
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Rect,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.geometry.Rect.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(androidx.compose.ui.unit.Dp.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Position,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.Position.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Size,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Size.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Bounds,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.unit.Bounds.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Offset.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntOffset.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntSize,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntSize.Companion);
+  }
+
   public interface VectorizedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
     method public long getDurationMillis(V start, V end, V startVelocity);
     method public default V getEndVelocity(V start, V end, V startVelocity);
@@ -571,7 +629,7 @@
   public final class VectorizedAnimationSpecKt {
   }
 
-  public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     method public int getDelayMillis();
     method public int getDurationMillis();
     method public default long getDurationMillis(V start, V end, V startVelocity);
@@ -579,13 +637,20 @@
     property public abstract int durationMillis;
   }
 
-  public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public interface VectorizedFiniteAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  }
+
+  public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedFloatAnimationSpec(androidx.compose.animation.core.FloatAnimationSpec anim);
     method public long getDurationMillis(V start, V end, V startVelocity);
     method public V getValue(long playTime, V start, V end, V startVelocity);
     method public V getVelocity(long playTime, V start, V end, V startVelocity);
   }
 
+  public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+    ctor public VectorizedInfiniteRepeatableSpec(androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
+  }
+
   public final class VectorizedKeyframesSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
     ctor public VectorizedKeyframesSpec(java.util.Map<java.lang.Integer,? extends kotlin.Pair<? extends V,? extends kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float>>> keyframes, int durationMillis, int delayMillis);
     method public int getDelayMillis();
@@ -596,7 +661,7 @@
     property public int durationMillis;
   }
 
-  public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
     method public long getDurationMillis(V start, V end, V startVelocity);
     method public V getValue(long playTime, V start, V end, V startVelocity);
@@ -614,7 +679,7 @@
     property public int durationMillis;
   }
 
-  public final class VectorizedSpringSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public final class VectorizedSpringSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedSpringSpec(float dampingRatio, float stiffness, V? visibilityThreshold);
     method public float getDampingRatio();
     method public float getStiffness();
@@ -635,5 +700,17 @@
     property public final kotlin.jvm.functions.Function1<java.lang.Float,java.lang.Float> easing;
   }
 
+  public final class VisibilityThresholdsKt {
+    method public static long getVisibilityThreshold(androidx.compose.ui.unit.IntOffset.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.geometry.Offset.Companion);
+    method public static int getVisibilityThreshold(kotlin.jvm.internal.IntCompanionObject);
+    method public static float getVisibilityThreshold(androidx.compose.ui.unit.Dp.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.unit.Position.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.geometry.Size.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.unit.IntSize.Companion);
+    method public static androidx.compose.ui.geometry.Rect getVisibilityThreshold(androidx.compose.ui.geometry.Rect.Companion);
+    method public static androidx.compose.ui.unit.Bounds getVisibilityThreshold(androidx.compose.ui.unit.Bounds.Companion);
+  }
+
 }
 
diff --git a/compose/animation/animation-core/api/public_plus_experimental_current.txt b/compose/animation/animation-core/api/public_plus_experimental_current.txt
index 2603144..41d1e7f 100644
--- a/compose/animation/animation-core/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation-core/api/public_plus_experimental_current.txt
@@ -55,7 +55,7 @@
   public final class AnimationConstants {
     field public static final int DefaultDurationMillis = 300; // 0x12c
     field public static final androidx.compose.animation.core.AnimationConstants INSTANCE;
-    field public static final int Infinite = 2147483647; // 0x7fffffff
+    field @Deprecated public static final int Infinite = 2147483647; // 0x7fffffff
   }
 
   public enum AnimationEndReason {
@@ -115,6 +115,7 @@
     method public static androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> AnimationState-SZyJPdc(float initialValue, optional float initialVelocity, optional long lastFrameTime, optional long endTime, optional boolean isRunning);
     method public static androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> copy-mN48zRs(androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D>, optional float value, optional float velocity, optional long lastFrameTime, optional long endTime, optional boolean isRunning);
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.AnimationState<T,V> copy-upwry94(androidx.compose.animation.core.AnimationState<T,V>, optional T? value, optional V? velocityVector, optional long lastFrameTime, optional long endTime, optional boolean isRunning);
+    method public static <T, V extends androidx.compose.animation.core.AnimationVector> V createZeroVectorFrom(androidx.compose.animation.core.TwoWayConverter<T,V>, T? value);
     method public static float getVelocity(androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D>);
     method public static float getVelocity(androidx.compose.animation.core.AnimationScope<java.lang.Float,androidx.compose.animation.core.AnimationVector1D>);
     method public static boolean isFinished(androidx.compose.animation.core.AnimationState<?,?>);
@@ -235,7 +236,7 @@
     method public void dispatchTime$metalava_module(long frameTimeMillis);
   }
 
-  public interface DurationBasedAnimationSpec<T> extends androidx.compose.animation.core.AnimationSpec<T> {
+  public interface DurationBasedAnimationSpec<T> extends androidx.compose.animation.core.FiniteAnimationSpec<T> {
     method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
   }
 
@@ -257,6 +258,10 @@
     property public float absVelocityThreshold;
   }
 
+  public interface FiniteAnimationSpec<T> extends androidx.compose.animation.core.AnimationSpec<T> {
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+  }
+
   public interface FloatAnimationSpec extends androidx.compose.animation.core.AnimationSpec<java.lang.Float> {
     method public long getDurationMillis(float start, float end, float startVelocity);
     method public default float getEndVelocity(float start, float end, float startVelocity);
@@ -309,6 +314,15 @@
     property public final int duration;
   }
 
+  public final class InfiniteRepeatableSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+    ctor public InfiniteRepeatableSpec(androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, androidx.compose.animation.core.RepeatMode repeatMode);
+    method public androidx.compose.animation.core.DurationBasedAnimationSpec<T> getAnimation();
+    method public androidx.compose.animation.core.RepeatMode getRepeatMode();
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+    property public final androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation;
+    property public final androidx.compose.animation.core.RepeatMode repeatMode;
+  }
+
   public final class IntPropKey implements androidx.compose.animation.core.PropKey<java.lang.Integer,androidx.compose.animation.core.AnimationVector1D> {
     ctor public IntPropKey(String label);
     ctor public IntPropKey();
@@ -394,8 +408,6 @@
 
   public final class PropKeyKt {
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TwoWayConverter<T,V> TwoWayConverter(kotlin.jvm.functions.Function1<? super T,? extends V> convertToVector, kotlin.jvm.functions.Function1<? super V,? extends T> convertFromVector);
-    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> getFloatToVectorConverter();
-    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<java.lang.Integer,androidx.compose.animation.core.AnimationVector1D> getIntToVectorConverter();
     method public static androidx.compose.animation.core.TwoWayConverter<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(kotlin.jvm.internal.FloatCompanionObject);
     method public static androidx.compose.animation.core.TwoWayConverter<java.lang.Integer,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(kotlin.jvm.internal.IntCompanionObject);
   }
@@ -405,18 +417,18 @@
     enum_constant public static final androidx.compose.animation.core.RepeatMode Reverse;
   }
 
-  @androidx.compose.runtime.Immutable public final class RepeatableSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+  @androidx.compose.runtime.Immutable public final class RepeatableSpec<T> implements androidx.compose.animation.core.FiniteAnimationSpec<T> {
     ctor public RepeatableSpec(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, androidx.compose.animation.core.RepeatMode repeatMode);
     method public androidx.compose.animation.core.DurationBasedAnimationSpec<T> getAnimation();
     method public int getIterations();
     method public androidx.compose.animation.core.RepeatMode getRepeatMode();
-    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
     property public final androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation;
     property public final int iterations;
     property public final androidx.compose.animation.core.RepeatMode repeatMode;
   }
 
-  @androidx.compose.runtime.Immutable public final class SnapSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+  @androidx.compose.runtime.Immutable public final class SnapSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
     ctor public SnapSpec(int delay);
     ctor public SnapSpec();
     method public int getDelay();
@@ -443,7 +455,7 @@
   public final class SpringSimulationKt {
   }
 
-  @androidx.compose.runtime.Immutable public final class SpringSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+  @androidx.compose.runtime.Immutable public final class SpringSpec<T> implements androidx.compose.animation.core.FiniteAnimationSpec<T> {
     ctor public SpringSpec(float dampingRatio, float stiffness, T? visibilityThreshold);
     ctor public SpringSpec();
     method public float getDampingRatio();
@@ -489,6 +501,25 @@
   public final class ToolingGlueKt {
   }
 
+  public final class Transition<S> {
+    method public S! getCurrentState();
+    method public S! getTargetState();
+    method public androidx.compose.animation.core.Transition.States<S> getTransitionStates();
+    method public boolean isRunning();
+    property public final S! currentState;
+    property public final boolean isRunning;
+    property public final S! targetState;
+    property public final androidx.compose.animation.core.Transition.States<S> transitionStates;
+  }
+
+  public static final class Transition.States<S> {
+    ctor public Transition.States(S? initialState, S? targetState);
+    method public S! getInitialState();
+    method public S! getTargetState();
+    property public final S! initialState;
+    property public final S! targetState;
+  }
+
   public final class TransitionAnimation<T> implements androidx.compose.animation.core.TransitionState {
     ctor public TransitionAnimation(androidx.compose.animation.core.TransitionDefinition<T> def, androidx.compose.animation.core.AnimationClockObservable clock, T? initState, String? label);
     method public operator <T, V extends androidx.compose.animation.core.AnimationVector> T! get(androidx.compose.animation.core.PropKey<T,V> propKey);
@@ -520,14 +551,30 @@
 
   public final class TransitionDefinitionKt {
     method public static <T> androidx.compose.animation.core.TransitionAnimation<T> createAnimation(androidx.compose.animation.core.TransitionDefinition<T>, androidx.compose.animation.core.AnimationClockObservable clock, optional T? initState);
+    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.AnimationSpec<T> infiniteRepeatable(androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
     method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.KeyframesSpec<T> keyframes(kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T>,kotlin.Unit> init);
-    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.AnimationSpec<T> repeatable(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
-    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.AnimationSpec<T> snap(optional int delayMillis);
+    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.RepeatableSpec<T> repeatable(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
+    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.SnapSpec<T> snap(optional int delayMillis);
     method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.SpringSpec<T> spring(optional float dampingRatio, optional float stiffness, optional T? visibilityThreshold);
     method public static <T> androidx.compose.animation.core.TransitionDefinition<T> transitionDefinition(kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.TransitionDefinition<T>,kotlin.Unit> init);
     method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.TweenSpec<T> tween(optional int durationMillis, optional int delayMillis, optional kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> easing);
   }
 
+  public final class TransitionKt {
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.Bounds> animateBounds(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.Bounds>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.Bounds> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> animateDp(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.Dp>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.Dp> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<java.lang.Float> animateFloat(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>> transitionSpec, kotlin.jvm.functions.Function1<? super S,java.lang.Float> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<java.lang.Integer> animateInt(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Integer>> transitionSpec, kotlin.jvm.functions.Function1<? super S,java.lang.Integer> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.IntOffset> animateIntOffset(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.IntOffset> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.IntSize> animateIntSize(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.IntSize> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Offset> animateOffset(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Offset>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Offset> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.Position> animatePosition(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.Position>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.Position> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Rect> animateRect(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Rect>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Rect> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Size> animateSize(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Size>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Size> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S, T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> animateValue(androidx.compose.animation.core.Transition<S>, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<T>> transitionSpec, kotlin.jvm.functions.Function1<? super S,? extends T> targetValueByState);
+    method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.Transition<T> updateTransition(T? targetState, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onFinished);
+  }
+
   public final class TransitionSpec<S> {
     method public androidx.compose.animation.core.InterruptionHandling getInterruptionHandling();
     method public S? getNextState();
@@ -561,6 +608,17 @@
     property public abstract kotlin.jvm.functions.Function1<T,V> convertToVector;
   }
 
+  public final class VectorConvertersKt {
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Rect,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.geometry.Rect.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(androidx.compose.ui.unit.Dp.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Position,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.Position.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Size,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Size.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Bounds,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.unit.Bounds.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Offset.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntOffset.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntSize,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntSize.Companion);
+  }
+
   public interface VectorizedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
     method public long getDurationMillis(V start, V end, V startVelocity);
     method public default V getEndVelocity(V start, V end, V startVelocity);
@@ -571,7 +629,7 @@
   public final class VectorizedAnimationSpecKt {
   }
 
-  public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     method public int getDelayMillis();
     method public int getDurationMillis();
     method public default long getDurationMillis(V start, V end, V startVelocity);
@@ -579,13 +637,20 @@
     property public abstract int durationMillis;
   }
 
-  public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public interface VectorizedFiniteAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  }
+
+  public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedFloatAnimationSpec(androidx.compose.animation.core.FloatAnimationSpec anim);
     method public long getDurationMillis(V start, V end, V startVelocity);
     method public V getValue(long playTime, V start, V end, V startVelocity);
     method public V getVelocity(long playTime, V start, V end, V startVelocity);
   }
 
+  public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+    ctor public VectorizedInfiniteRepeatableSpec(androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
+  }
+
   public final class VectorizedKeyframesSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
     ctor public VectorizedKeyframesSpec(java.util.Map<java.lang.Integer,? extends kotlin.Pair<? extends V,? extends kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float>>> keyframes, int durationMillis, int delayMillis);
     method public int getDelayMillis();
@@ -596,7 +661,7 @@
     property public int durationMillis;
   }
 
-  public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
     method public long getDurationMillis(V start, V end, V startVelocity);
     method public V getValue(long playTime, V start, V end, V startVelocity);
@@ -614,7 +679,7 @@
     property public int durationMillis;
   }
 
-  public final class VectorizedSpringSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public final class VectorizedSpringSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedSpringSpec(float dampingRatio, float stiffness, V? visibilityThreshold);
     method public float getDampingRatio();
     method public float getStiffness();
@@ -635,5 +700,17 @@
     property public final kotlin.jvm.functions.Function1<java.lang.Float,java.lang.Float> easing;
   }
 
+  public final class VisibilityThresholdsKt {
+    method public static long getVisibilityThreshold(androidx.compose.ui.unit.IntOffset.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.geometry.Offset.Companion);
+    method public static int getVisibilityThreshold(kotlin.jvm.internal.IntCompanionObject);
+    method public static float getVisibilityThreshold(androidx.compose.ui.unit.Dp.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.unit.Position.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.geometry.Size.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.unit.IntSize.Companion);
+    method public static androidx.compose.ui.geometry.Rect getVisibilityThreshold(androidx.compose.ui.geometry.Rect.Companion);
+    method public static androidx.compose.ui.unit.Bounds getVisibilityThreshold(androidx.compose.ui.unit.Bounds.Companion);
+  }
+
 }
 
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index 2603144..1855bc4 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -55,7 +55,7 @@
   public final class AnimationConstants {
     field public static final int DefaultDurationMillis = 300; // 0x12c
     field public static final androidx.compose.animation.core.AnimationConstants INSTANCE;
-    field public static final int Infinite = 2147483647; // 0x7fffffff
+    field @Deprecated public static final int Infinite = 2147483647; // 0x7fffffff
   }
 
   public enum AnimationEndReason {
@@ -115,6 +115,7 @@
     method public static androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> AnimationState-SZyJPdc(float initialValue, optional float initialVelocity, optional long lastFrameTime, optional long endTime, optional boolean isRunning);
     method public static androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> copy-mN48zRs(androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D>, optional float value, optional float velocity, optional long lastFrameTime, optional long endTime, optional boolean isRunning);
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.AnimationState<T,V> copy-upwry94(androidx.compose.animation.core.AnimationState<T,V>, optional T? value, optional V? velocityVector, optional long lastFrameTime, optional long endTime, optional boolean isRunning);
+    method public static <T, V extends androidx.compose.animation.core.AnimationVector> V createZeroVectorFrom(androidx.compose.animation.core.TwoWayConverter<T,V>, T? value);
     method public static float getVelocity(androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D>);
     method public static float getVelocity(androidx.compose.animation.core.AnimationScope<java.lang.Float,androidx.compose.animation.core.AnimationVector1D>);
     method public static boolean isFinished(androidx.compose.animation.core.AnimationState<?,?>);
@@ -235,7 +236,7 @@
     method public void dispatchTime$metalava_module(long frameTimeMillis);
   }
 
-  public interface DurationBasedAnimationSpec<T> extends androidx.compose.animation.core.AnimationSpec<T> {
+  public interface DurationBasedAnimationSpec<T> extends androidx.compose.animation.core.FiniteAnimationSpec<T> {
     method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
   }
 
@@ -257,6 +258,10 @@
     property public float absVelocityThreshold;
   }
 
+  public interface FiniteAnimationSpec<T> extends androidx.compose.animation.core.AnimationSpec<T> {
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+  }
+
   public interface FloatAnimationSpec extends androidx.compose.animation.core.AnimationSpec<java.lang.Float> {
     method public long getDurationMillis(float start, float end, float startVelocity);
     method public default float getEndVelocity(float start, float end, float startVelocity);
@@ -309,6 +314,15 @@
     property public final int duration;
   }
 
+  public final class InfiniteRepeatableSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+    ctor public InfiniteRepeatableSpec(androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, androidx.compose.animation.core.RepeatMode repeatMode);
+    method public androidx.compose.animation.core.DurationBasedAnimationSpec<T> getAnimation();
+    method public androidx.compose.animation.core.RepeatMode getRepeatMode();
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+    property public final androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation;
+    property public final androidx.compose.animation.core.RepeatMode repeatMode;
+  }
+
   public final class IntPropKey implements androidx.compose.animation.core.PropKey<java.lang.Integer,androidx.compose.animation.core.AnimationVector1D> {
     ctor public IntPropKey(String label);
     ctor public IntPropKey();
@@ -394,8 +408,6 @@
 
   public final class PropKeyKt {
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TwoWayConverter<T,V> TwoWayConverter(kotlin.jvm.functions.Function1<? super T,? extends V> convertToVector, kotlin.jvm.functions.Function1<? super V,? extends T> convertFromVector);
-    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> getFloatToVectorConverter();
-    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<java.lang.Integer,androidx.compose.animation.core.AnimationVector1D> getIntToVectorConverter();
     method public static androidx.compose.animation.core.TwoWayConverter<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(kotlin.jvm.internal.FloatCompanionObject);
     method public static androidx.compose.animation.core.TwoWayConverter<java.lang.Integer,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(kotlin.jvm.internal.IntCompanionObject);
   }
@@ -405,18 +417,18 @@
     enum_constant public static final androidx.compose.animation.core.RepeatMode Reverse;
   }
 
-  @androidx.compose.runtime.Immutable public final class RepeatableSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+  @androidx.compose.runtime.Immutable public final class RepeatableSpec<T> implements androidx.compose.animation.core.FiniteAnimationSpec<T> {
     ctor public RepeatableSpec(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, androidx.compose.animation.core.RepeatMode repeatMode);
     method public androidx.compose.animation.core.DurationBasedAnimationSpec<T> getAnimation();
     method public int getIterations();
     method public androidx.compose.animation.core.RepeatMode getRepeatMode();
-    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
     property public final androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation;
     property public final int iterations;
     property public final androidx.compose.animation.core.RepeatMode repeatMode;
   }
 
-  @androidx.compose.runtime.Immutable public final class SnapSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+  @androidx.compose.runtime.Immutable public final class SnapSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
     ctor public SnapSpec(int delay);
     ctor public SnapSpec();
     method public int getDelay();
@@ -443,7 +455,7 @@
   public final class SpringSimulationKt {
   }
 
-  @androidx.compose.runtime.Immutable public final class SpringSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+  @androidx.compose.runtime.Immutable public final class SpringSpec<T> implements androidx.compose.animation.core.FiniteAnimationSpec<T> {
     ctor public SpringSpec(float dampingRatio, float stiffness, T? visibilityThreshold);
     ctor public SpringSpec();
     method public float getDampingRatio();
@@ -489,6 +501,43 @@
   public final class ToolingGlueKt {
   }
 
+  public final class Transition<S> {
+    method @kotlin.PublishedApi internal boolean addAnimation(androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?> animation);
+    method public S! getCurrentState();
+    method public S! getTargetState();
+    method public androidx.compose.animation.core.Transition.States<S> getTransitionStates();
+    method public boolean isRunning();
+    method @kotlin.PublishedApi internal void removeAnimation(androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?> animation);
+    property public final S! currentState;
+    property public final boolean isRunning;
+    property public final S! targetState;
+    property public final androidx.compose.animation.core.Transition.States<S> transitionStates;
+  }
+
+  public static final class Transition.States<S> {
+    ctor public Transition.States(S? initialState, S? targetState);
+    method public S! getInitialState();
+    method public S! getTargetState();
+    property public final S! initialState;
+    property public final S! targetState;
+  }
+
+  @kotlin.PublishedApi internal final class Transition.TransitionAnimationState<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.runtime.State<T> {
+    ctor @kotlin.PublishedApi internal Transition.TransitionAnimationState(T? initialValue, V initialVelocityVector, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter);
+    method public T! getTargetValue();
+    method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
+    method public T! getValue();
+    method public V getVelocityVector();
+    method public boolean isFinished();
+    method @kotlin.PublishedApi internal void updateTargetValue(T? targetValue);
+    property public final boolean isFinished;
+    property public final T! targetValue;
+    property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
+    property public T! value;
+    property public final V velocityVector;
+    field @kotlin.PublishedApi internal androidx.compose.animation.core.FiniteAnimationSpec<T> animationSpec;
+  }
+
   public final class TransitionAnimation<T> implements androidx.compose.animation.core.TransitionState {
     ctor public TransitionAnimation(androidx.compose.animation.core.TransitionDefinition<T> def, androidx.compose.animation.core.AnimationClockObservable clock, T? initState, String? label);
     method public operator <T, V extends androidx.compose.animation.core.AnimationVector> T! get(androidx.compose.animation.core.PropKey<T,V> propKey);
@@ -520,14 +569,30 @@
 
   public final class TransitionDefinitionKt {
     method public static <T> androidx.compose.animation.core.TransitionAnimation<T> createAnimation(androidx.compose.animation.core.TransitionDefinition<T>, androidx.compose.animation.core.AnimationClockObservable clock, optional T? initState);
+    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.AnimationSpec<T> infiniteRepeatable(androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
     method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.KeyframesSpec<T> keyframes(kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T>,kotlin.Unit> init);
-    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.AnimationSpec<T> repeatable(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
-    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.AnimationSpec<T> snap(optional int delayMillis);
+    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.RepeatableSpec<T> repeatable(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
+    method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.SnapSpec<T> snap(optional int delayMillis);
     method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.SpringSpec<T> spring(optional float dampingRatio, optional float stiffness, optional T? visibilityThreshold);
     method public static <T> androidx.compose.animation.core.TransitionDefinition<T> transitionDefinition(kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.TransitionDefinition<T>,kotlin.Unit> init);
     method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.TweenSpec<T> tween(optional int durationMillis, optional int delayMillis, optional kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> easing);
   }
 
+  public final class TransitionKt {
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.Bounds> animateBounds(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.Bounds>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.Bounds> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> animateDp(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.Dp>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.Dp> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<java.lang.Float> animateFloat(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>> transitionSpec, kotlin.jvm.functions.Function1<? super S,java.lang.Float> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<java.lang.Integer> animateInt(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Integer>> transitionSpec, kotlin.jvm.functions.Function1<? super S,java.lang.Integer> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.IntOffset> animateIntOffset(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.IntOffset> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.IntSize> animateIntSize(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.IntSize> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Offset> animateOffset(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Offset>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Offset> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.Position> animatePosition(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.Position>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.Position> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Rect> animateRect(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Rect>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Rect> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Size> animateSize(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Size>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Size> targetValueByState);
+    method @androidx.compose.runtime.Composable public static inline <S, T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> animateValue(androidx.compose.animation.core.Transition<S>, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<T>> transitionSpec, kotlin.jvm.functions.Function1<? super S,? extends T> targetValueByState);
+    method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.Transition<T> updateTransition(T? targetState, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onFinished);
+  }
+
   public final class TransitionSpec<S> {
     method public androidx.compose.animation.core.InterruptionHandling getInterruptionHandling();
     method public S? getNextState();
@@ -561,6 +626,17 @@
     property public abstract kotlin.jvm.functions.Function1<T,V> convertToVector;
   }
 
+  public final class VectorConvertersKt {
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Rect,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.geometry.Rect.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(androidx.compose.ui.unit.Dp.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Position,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.Position.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Size,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Size.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Bounds,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.unit.Bounds.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Offset.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntOffset.Companion);
+    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntSize,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntSize.Companion);
+  }
+
   public interface VectorizedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
     method public long getDurationMillis(V start, V end, V startVelocity);
     method public default V getEndVelocity(V start, V end, V startVelocity);
@@ -571,7 +647,7 @@
   public final class VectorizedAnimationSpecKt {
   }
 
-  public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     method public int getDelayMillis();
     method public int getDurationMillis();
     method public default long getDurationMillis(V start, V end, V startVelocity);
@@ -579,13 +655,20 @@
     property public abstract int durationMillis;
   }
 
-  public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public interface VectorizedFiniteAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  }
+
+  public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedFloatAnimationSpec(androidx.compose.animation.core.FloatAnimationSpec anim);
     method public long getDurationMillis(V start, V end, V startVelocity);
     method public V getValue(long playTime, V start, V end, V startVelocity);
     method public V getVelocity(long playTime, V start, V end, V startVelocity);
   }
 
+  public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+    ctor public VectorizedInfiniteRepeatableSpec(androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
+  }
+
   public final class VectorizedKeyframesSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
     ctor public VectorizedKeyframesSpec(java.util.Map<java.lang.Integer,? extends kotlin.Pair<? extends V,? extends kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float>>> keyframes, int durationMillis, int delayMillis);
     method public int getDelayMillis();
@@ -596,7 +679,7 @@
     property public int durationMillis;
   }
 
-  public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
     method public long getDurationMillis(V start, V end, V startVelocity);
     method public V getValue(long playTime, V start, V end, V startVelocity);
@@ -614,7 +697,7 @@
     property public int durationMillis;
   }
 
-  public final class VectorizedSpringSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+  public final class VectorizedSpringSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedSpringSpec(float dampingRatio, float stiffness, V? visibilityThreshold);
     method public float getDampingRatio();
     method public float getStiffness();
@@ -635,5 +718,17 @@
     property public final kotlin.jvm.functions.Function1<java.lang.Float,java.lang.Float> easing;
   }
 
+  public final class VisibilityThresholdsKt {
+    method public static long getVisibilityThreshold(androidx.compose.ui.unit.IntOffset.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.geometry.Offset.Companion);
+    method public static int getVisibilityThreshold(kotlin.jvm.internal.IntCompanionObject);
+    method public static float getVisibilityThreshold(androidx.compose.ui.unit.Dp.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.unit.Position.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.geometry.Size.Companion);
+    method public static long getVisibilityThreshold(androidx.compose.ui.unit.IntSize.Companion);
+    method public static androidx.compose.ui.geometry.Rect getVisibilityThreshold(androidx.compose.ui.geometry.Rect.Companion);
+    method public static androidx.compose.ui.unit.Bounds getVisibilityThreshold(androidx.compose.ui.unit.Bounds.Companion);
+  }
+
 }
 
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index 16c9b69..781604b3 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -17,7 +17,6 @@
 
 import androidx.build.AndroidXUiPlugin
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
@@ -56,7 +55,10 @@
 
         androidTestImplementation(ANDROIDX_TEST_RULES)
         androidTestImplementation(ANDROIDX_TEST_RUNNER)
+        androidTestImplementation(ANDROIDX_TEST_CORE)
         androidTestImplementation(JUNIT)
+        androidTestImplementation(project(":compose:animation:animation"))
+        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
     }
 }
 
@@ -96,7 +98,10 @@
             androidAndroidTest.dependencies {
                 implementation(ANDROIDX_TEST_RULES)
                 implementation(ANDROIDX_TEST_RUNNER)
+                implementation(ANDROIDX_TEST_CORE)
                 implementation(JUNIT)
+                implementation project(":compose:animation:animation")
+                implementation project(":compose:ui:ui-test-junit4")
             }
         }
     }
@@ -119,3 +124,14 @@
         ]
     }
 }
+
+android {
+    defaultConfig {
+        minSdkVersion 21
+    }
+    tasks.withType(KotlinCompile).configureEach {
+        kotlinOptions {
+            useIR = true
+        }
+    }
+}
diff --git a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/SuspendAnimationSamples.kt b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/SuspendAnimationSamples.kt
index cc0dda0..43d0a9e 100644
--- a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/SuspendAnimationSamples.kt
+++ b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/SuspendAnimationSamples.kt
@@ -17,14 +17,13 @@
 package androidx.compose.animation.core.samples
 
 import androidx.annotation.Sampled
-import androidx.compose.animation.core.AnimationConstants
 import androidx.compose.animation.core.AnimationState
 import androidx.compose.animation.core.RepeatMode
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.animate
 import androidx.compose.animation.core.animateTo
+import androidx.compose.animation.core.infiniteRepeatable
 import androidx.compose.animation.core.isFinished
-import androidx.compose.animation.core.repeatable
 import androidx.compose.animation.core.spring
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.layout.Box
@@ -82,8 +81,7 @@
             animate(
                 initialValue = 1f,
                 targetValue = 0f,
-                animationSpec = repeatable(
-                    iterations = AnimationConstants.Infinite,
+                animationSpec = infiniteRepeatable(
                     animation = tween(1000),
                     repeatMode = RepeatMode.Reverse
                 )
diff --git a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionSamples.kt b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionSamples.kt
new file mode 100644
index 0000000..61845d2
--- /dev/null
+++ b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionSamples.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.animation.core.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.animation.animateColor
+import androidx.compose.animation.core.Transition
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.keyframes
+import androidx.compose.animation.core.snap
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.Button
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.gesture.pressIndicatorGestureFilter
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.unit.dp
+
+@Sampled
+@Composable
+fun GestureAnimationSample() {
+    // enum class ComponentState { Pressed, Released }
+    var useRed by remember { mutableStateOf(false) }
+    var toState by remember { mutableStateOf(ComponentState.Released) }
+    val modifier = Modifier.pressIndicatorGestureFilter(
+        onStart = { toState = ComponentState.Pressed },
+        onStop = { toState = ComponentState.Released },
+        onCancel = { toState = ComponentState.Released }
+    )
+
+    // Defines a transition of `ComponentState`, and updates the transition when the provided
+    // [targetState] changes. The transition will run all of the child animations towards the new
+    // [targetState] in response to the [targetState] change.
+    val transition: Transition<ComponentState> = updateTransition(targetState = toState)
+    // Defines a float animation as a child animation the transition. The current animation value
+    // can be read from the returned State<Float>.
+    val scale: Float by transition.animateFloat(
+        // Defines a transition spec that uses the same low-stiffness spring for *all*
+        // transitions of this float, no matter what the target is.
+        transitionSpec = { spring(stiffness = 50f) }
+    ) { state ->
+        // This code block declares a mapping from state to value.
+        if (state == ComponentState.Pressed) 3f else 1f
+    }
+
+    // Defines a color animation as a child animation of the transition.
+    val color: Color by transition.animateColor(
+        transitionSpec = { transitionStates ->
+            if (transitionStates.initialState == ComponentState.Pressed &&
+                transitionStates.targetState == ComponentState.Released
+            ) {
+                // Uses spring for the transition going from pressed to released
+                spring(stiffness = 50f)
+            } else {
+                // Uses tween for all the other transitions. (In this case there is
+                // only one other transition. i.e. released -> pressed.)
+                tween(durationMillis = 500)
+            }
+        }
+    ) { state ->
+        when (state) {
+            // Similar to the float animation, we need to declare the target values
+            // for each state. In this code block we can access theme colors.
+            ComponentState.Pressed -> MaterialTheme.colors.primary
+            // We can also have the target value depend on other mutableStates,
+            // such as `useRed` here. Whenever the target value changes, transition
+            // will automatically animate to the new value even if it has already
+            // arrived at its target state.
+            ComponentState.Released -> if (useRed) Color.Red else MaterialTheme.colors.secondary
+        }
+    }
+    Column {
+        Button(
+            modifier = Modifier.padding(10.dp).align(Alignment.CenterHorizontally),
+            onClick = { useRed = !useRed }
+        ) {
+            Text("Change Color")
+        }
+        Box(
+            modifier.fillMaxSize().wrapContentSize(Alignment.Center)
+                .size((100 * scale).dp).background(color)
+        )
+    }
+}
+
+private enum class ComponentState { Pressed, Released }
+private enum class ButtonStatus { Initial, Pressed, Released }
+
+@Sampled
+@Composable
+fun AnimateFloatSample() {
+    // enum class ButtonStatus {Initial, Pressed, Released}
+    @Composable
+    fun AnimateAlphaAndScale(
+        modifier: Modifier,
+        transition: Transition<ButtonStatus>
+    ) {
+        // Defines a float animation as a child animation of transition. This allows the
+        // transition to manage the states of this animation. The returned State<Float> from the
+        // [animateFloat] function is used here as a property delegate.
+        // This float animation will use the default [spring] for all transition destinations, as
+        // specified by the default `transitionSpec`.
+        val scale: Float by transition.animateFloat { state ->
+            if (state == ButtonStatus.Pressed) 1.2f else 1f
+        }
+
+        // Alternatively, we can specify different animation specs based on the initial state and
+        // target state of the a transition run using `transitionSpec`.
+        val alpha: Float by transition.animateFloat(
+            transitionSpec = {
+                if (it.initialState == ButtonStatus.Initial &&
+                    it.targetState == ButtonStatus.Pressed
+                ) {
+                    keyframes {
+                        durationMillis = 225
+                        0f at 0 // optional
+                        0.3f at 75
+                        0.2f at 225 // optional
+                    }
+                } else if (it.initialState == ButtonStatus.Pressed &&
+                    it.targetState == ButtonStatus.Released
+                ) {
+                    tween(durationMillis = 220)
+                } else {
+                    snap()
+                }
+            }
+        ) { state ->
+            // Same target value for Initial and Released states
+            if (state == ButtonStatus.Pressed) 0.2f else 0f
+        }
+
+        Box(modifier.graphicsLayer(alpha = alpha, scaleX = scale)) {
+            // content goes here
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/animation/animation-core/src/androidAndroidTest/AndroidManifest.xml b/compose/animation/animation-core/src/androidAndroidTest/AndroidManifest.xml
new file mode 100644
index 0000000..61f91c5
--- /dev/null
+++ b/compose/animation/animation-core/src/androidAndroidTest/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  -->
+<manifest package="androidx.compose.animation.core"/>
diff --git a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
new file mode 100644
index 0000000..6d4d2ac
--- /dev/null
+++ b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
@@ -0,0 +1,199 @@
+/*
+ * 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.animation.core
+
+import androidx.compose.animation.VectorConverter
+import androidx.compose.animation.animateColor
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import junit.framework.TestCase.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class TransitionTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private enum class AnimStates {
+        From,
+        To
+    }
+
+    @Test
+    fun transitionTest() {
+        val target = mutableStateOf(AnimStates.From)
+        val floatAnim1 = TargetBasedAnimation(
+            spring(dampingRatio = Spring.DampingRatioHighBouncy),
+            0f,
+            1f,
+            Float.VectorConverter
+        )
+        val floatAnim2 = TargetBasedAnimation(
+            spring(dampingRatio = Spring.DampingRatioLowBouncy, stiffness = Spring.StiffnessLow),
+            1f,
+            0f,
+            Float.VectorConverter
+        )
+
+        val colorAnim1 = TargetBasedAnimation(
+            tween(1000),
+            Color.Red,
+            Color.Green,
+            Color.VectorConverter(Color.Red.colorSpace)
+        )
+        val colorAnim2 = TargetBasedAnimation(
+            tween(1000),
+            Color.Green,
+            Color.Red,
+            Color.VectorConverter(Color.Red.colorSpace)
+        )
+
+        // Animate from 0f to 0f for 1000ms
+        val keyframes1 = keyframes<Float> {
+            durationMillis = 1000
+            0f at 0
+            200f at 400
+            1000f at 1000
+        }
+
+        val keyframes2 = keyframes<Float> {
+            durationMillis = 800
+            0f at 0
+            -500f at 400
+            -1000f at 800
+        }
+
+        val keyframesAnim1 = TargetBasedAnimation(
+            keyframes1,
+            0f,
+            0f,
+            Float.VectorConverter
+        )
+        val keyframesAnim2 = TargetBasedAnimation(
+            keyframes2,
+            0f,
+            0f,
+            Float.VectorConverter
+        )
+        val animFloat = mutableStateOf(-1f)
+        val animColor = mutableStateOf(Color.Gray)
+        val animFloatWithKeyframes = mutableStateOf(-1f)
+        rule.setContent {
+            val transition = updateTransition(target.value)
+            animFloat.value = transition.animateFloat(
+                {
+                    if (it.initialState == AnimStates.From && it.targetState == AnimStates.To) {
+                        spring(dampingRatio = Spring.DampingRatioHighBouncy)
+                    } else {
+                        spring(
+                            dampingRatio = Spring.DampingRatioLowBouncy,
+                            stiffness = Spring.StiffnessLow
+                        )
+                    }
+                }
+            ) {
+                when (it) {
+                    AnimStates.From -> 0f
+                    AnimStates.To -> 1f
+                }
+            }.value
+
+            animColor.value = transition.animateColor(
+                { tween(durationMillis = 1000) }
+            ) {
+                when (it) {
+                    AnimStates.From -> Color.Red
+                    AnimStates.To -> Color.Green
+                }
+            }.value
+
+            animFloatWithKeyframes.value = transition.animateFloat(
+                transitionSpec = {
+                    if (it.initialState == AnimStates.From && it.targetState == AnimStates.To) {
+                        keyframes1
+                    } else {
+                        keyframes2
+                    }
+                }
+            ) {
+                // Same values for all states, but different transitions from state to state.
+                0f
+            }.value
+
+            if (transition.isRunning) {
+                if (transition.targetState == AnimStates.To) {
+                    assertEquals(
+                        floatAnim1.getValue(transition.playTimeNanos / 1_000_000L),
+                        animFloat.value, 0.00001f
+                    )
+                    assertEquals(
+                        colorAnim1.getValue(transition.playTimeNanos / 1_000_000L),
+                        animColor.value
+                    )
+                    assertEquals(
+                        keyframesAnim1.getValue(transition.playTimeNanos / 1_000_000L),
+                        animFloatWithKeyframes.value, 0.00001f
+                    )
+
+                    assertEquals(AnimStates.To, transition.transitionStates.targetState)
+                    assertEquals(AnimStates.From, transition.transitionStates.initialState)
+                } else {
+                    assertEquals(
+                        floatAnim2.getValue(transition.playTimeNanos / 1_000_000L),
+                        animFloat.value, 0.00001f
+                    )
+                    assertEquals(
+                        colorAnim2.getValue(transition.playTimeNanos / 1_000_000L),
+                        animColor.value
+                    )
+                    assertEquals(
+                        keyframesAnim2.getValue(transition.playTimeNanos / 1_000_000L),
+                        animFloatWithKeyframes.value, 0.00001f
+                    )
+                    assertEquals(AnimStates.From, transition.transitionStates.targetState)
+                    assertEquals(AnimStates.To, transition.transitionStates.initialState)
+                }
+            }
+        }
+
+        assertEquals(0f, animFloat.value)
+        assertEquals(Color.Red, animColor.value)
+        rule.runOnIdle {
+            target.value = AnimStates.To
+        }
+        rule.waitForIdle()
+
+        assertEquals(1f, animFloat.value)
+        assertEquals(Color.Green, animColor.value)
+
+        // Animate back to the `from` state
+        rule.runOnIdle {
+            target.value = AnimStates.From
+        }
+        rule.waitForIdle()
+
+        assertEquals(0f, animFloat.value)
+        assertEquals(Color.Red, animColor.value)
+    }
+}
\ No newline at end of file
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
index a457db21..e04c380 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
@@ -185,8 +185,8 @@
         }
 
         lastFrameTime = timeMillis
-        value = anim.getValue(playtime)
         velocityVector = anim.getVelocityVector(playtime)
+        value = anim.getValue(playtime)
 
         checkFinished(playtime)
     }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
index 8c71f54..7f7b55a 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
@@ -28,8 +28,13 @@
     const val DefaultDurationMillis: Int = 300
 
     /**
-     * Used as a iterations count for [VectorizedRepeatableSpec] to create an infinity repeating animation.
+     * Used as a iterations count for [VectorizedRepeatableSpec] to create an infinity repeating
+     * animation.
      */
+    @Deprecated(
+        "Using Infinite to specify repeatable animation iterations has been " +
+            "deprecated. Please use [InfiniteRepeatableSpec] or [infiniteRepeatable] instead."
+    )
     const val Infinite: Int = Int.MAX_VALUE
 }
 
@@ -65,6 +70,19 @@
 }
 
 /**
+ * [FiniteAnimationSpec] is the interface that all non-infinite [AnimationSpec]s implement,
+ * including: [TweenSpec], [SpringSpec], [KeyframesSpec], [RepeatableSpec], [SnapSpec], etc. By
+ * definition, [InfiniteRepeatableSpec] __does not__ implement this interface.
+ *
+ * @see [InfiniteRepeatableSpec]
+ */
+interface FiniteAnimationSpec<T> : AnimationSpec<T> {
+    override fun <V : AnimationVector> vectorize(
+        converter: TwoWayConverter<T, V>
+    ): VectorizedFiniteAnimationSpec<V>
+}
+
+/**
  * Creates a TweenSpec configured with the given duration, delay, and easing curve.
  *
  * @param durationMillis duration of the [VectorizedTweenSpec] animation.
@@ -100,7 +118,7 @@
  *  [TweenSpec], and [SnapSpec]. These duration based specs can repeated when put into a
  *  [RepeatableSpec].
  */
-interface DurationBasedAnimationSpec<T> : AnimationSpec<T> {
+interface DurationBasedAnimationSpec<T> : FiniteAnimationSpec<T> {
     override fun <V : AnimationVector> vectorize(converter: TwoWayConverter<T, V>):
         VectorizedDurationBasedAnimationSpec<V>
 }
@@ -114,12 +132,13 @@
  * @param stiffness stiffness of the spring. [Spring.StiffnessMedium] by default.
  * @param visibilityThreshold specifies the visibility threshold
  */
+// TODO: annotate damping/stiffness with FloatRange
 @Immutable
 class SpringSpec<T>(
     val dampingRatio: Float = Spring.DampingRatioNoBouncy,
     val stiffness: Float = Spring.StiffnessMedium,
     val visibilityThreshold: T? = null
-) : AnimationSpec<T> {
+) : FiniteAnimationSpec<T> {
 
     override fun <V : AnimationVector> vectorize(converter: TwoWayConverter<T, V>) =
         VectorizedSpringSpec(dampingRatio, stiffness, converter.convert(visibilityThreshold))
@@ -146,14 +165,18 @@
 }
 
 /**
- * [RepeatableSpec] takes another [DurationBasedAnimationSpec] and plays it [iterations] times.
+ * [RepeatableSpec] takes another [DurationBasedAnimationSpec] and plays it [iterations] times. For
+ * creating infinitely repeating animation spec, consider using [InfiniteRepeatableSpec].
  *
  * __Note__: When repeating in the [RepeatMode.Reverse] mode, it's highly recommended to have an
- * __odd__ number of iterations, or [AnimationConstants.Infinite] iterations. Otherwise, the
- * animation may jump to the end value when it finishes the last iteration.
+ * __odd__ number of iterations. Otherwise, the animation may jump to the end value when it finishes
+ * the last iteration.
  *
- * @param iterations the count of iterations. Should be at least 1. [AnimationConstants.Infinite]
- *                   can be used to have an infinity repeating animation.
+ * @see repeatable
+ * @see InfiniteRepeatableSpec
+ * @see infiniteRepeatable
+ *
+ * @param iterations the count of iterations. Should be at least 1.
  * @param animation the [AnimationSpec] to be repeated
  * @param repeatMode whether animation should repeat by starting from the beginning (i.e.
  *                  [RepeatMode.Restart]) or from the end (i.e. [RepeatMode.Reverse])
@@ -163,10 +186,10 @@
     val iterations: Int,
     val animation: DurationBasedAnimationSpec<T>,
     val repeatMode: RepeatMode = RepeatMode.Restart
-) : AnimationSpec<T> {
+) : FiniteAnimationSpec<T> {
     override fun <V : AnimationVector> vectorize(
         converter: TwoWayConverter<T, V>
-    ): VectorizedAnimationSpec<V> {
+    ): VectorizedFiniteAnimationSpec<V> {
         return VectorizedRepeatableSpec(iterations, animation.vectorize(converter), repeatMode)
     }
 
@@ -185,6 +208,42 @@
 }
 
 /**
+ * [InfiniteRepeatableSpec] repeats the provided [animation] infinite amount of times. It will
+ * never naturally finish. This means the animation will only be stopped via some form of manual
+ * cancellation. When used with transition or other animation composables, the infinite animations
+ * will stop when the composable is removed from the compose tree.
+ *
+ * For non-infinite repeating animations, consider [RepeatableSpec].
+ *
+ * @param animation the [AnimationSpec] to be repeated
+ * @param repeatMode whether animation should repeat by starting from the beginning (i.e.
+ *                  [RepeatMode.Restart]) or from the end (i.e. [RepeatMode.Reverse])
+ * @see infiniteRepeatable
+ */
+// TODO: Consider supporting repeating spring specs
+class InfiniteRepeatableSpec<T>(
+    val animation: DurationBasedAnimationSpec<T>,
+    val repeatMode: RepeatMode = RepeatMode.Restart
+) : AnimationSpec<T> {
+    override fun <V : AnimationVector> vectorize(
+        converter: TwoWayConverter<T, V>
+    ): VectorizedAnimationSpec<V> {
+        return VectorizedInfiniteRepeatableSpec(animation.vectorize(converter), repeatMode)
+    }
+
+    override fun equals(other: Any?): Boolean =
+        if (other is RepeatableSpec<*>) {
+            other.animation == this.animation && other.repeatMode == this.repeatMode
+        } else {
+            false
+        }
+
+    override fun hashCode(): Int {
+        return animation.hashCode() * 31 + repeatMode.hashCode()
+    }
+}
+
+/**
  * Repeat mode for [RepeatableSpec] and [VectorizedRepeatableSpec].
  */
 enum class RepeatMode {
@@ -207,7 +266,7 @@
  *              starts. Defaults to 0.
  */
 @Immutable
-class SnapSpec<T>(val delay: Int = 0) : AnimationSpec<T> {
+class SnapSpec<T>(val delay: Int = 0) : DurationBasedAnimationSpec<T> {
     override fun <V : AnimationVector> vectorize(
         converter: TwoWayConverter<T, V>
     ): VectorizedDurationBasedAnimationSpec<V> = VectorizedSnapSpec(delay)
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationState.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationState.kt
index be201bb..7e82eb7 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationState.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationState.kt
@@ -54,7 +54,8 @@
     /**
      * Current velocity vector of the [AnimationState].
      */
-    var velocityVector: V = initialVelocityVector ?: typeConverter.createZeroVector(initialValue)
+    var velocityVector: V =
+        initialVelocityVector ?: typeConverter.createZeroVectorFrom(initialValue)
         internal set
 
     /**
@@ -214,12 +215,12 @@
         value, this.typeConverter, AnimationVector(velocity), lastFrameTime, endTime, isRunning
     )
 
-/*
- * Factory method for creating an [AnimationState] for Float [value].
+/**
+ * Factory method for creating an [AnimationState] for Float [initialValue].
  *
  * @param initialValue initial value of the [AnimationState]
  * @param initialVelocity initial velocity of the [AnimationState], 0 (i.e. no velocity) by default
- * @param lastFrameTime last frame time of the animation, [Uptime.UnSpecified] by default
+ * @param lastFrameTime last frame time of the animation, [Uptime.Unspecified] by default
  * @param endTime the time that the animation ended, [Uptime.Unspecified] by default.
  * @param isRunning whether the [AnimationState] is currently being updated by an animation.
  *                  False by default
@@ -243,5 +244,11 @@
     )
 }
 
-private fun <T, V : AnimationVector> TwoWayConverter<T, V>.createZeroVector(value: T) =
+/**
+ * Creates an AnimationVector with all the values set to 0 using the provided [TwoWayConverter]
+ * and the [value].
+ *
+ * @return a new AnimationVector instance of type [V].
+ */
+fun <T, V : AnimationVector> TwoWayConverter<T, V>.createZeroVectorFrom(value: T) =
     convertToVector(value).newInstance()
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt
index 9800ac3..4dd5e69 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.compose.animation.core
+@file:Suppress("RedundantOverride")
 
-import androidx.compose.ui.util.identityHashCode
+package androidx.compose.animation.core
 
 /**
  * [AnimationVector] class that is the base class of [AnimationVector1D], [AnimationVector2D],
@@ -122,7 +122,7 @@
     override fun equals(other: Any?): Boolean =
         other is AnimationVector1D && other.value == value
 
-    override fun hashCode(): Int = identityHashCode()
+    override fun hashCode(): Int = super.hashCode()
 }
 
 /**
@@ -174,7 +174,7 @@
     override fun equals(other: Any?): Boolean =
         other is AnimationVector2D && other.v1 == v1 && other.v2 == v2
 
-    override fun hashCode(): Int = identityHashCode()
+    override fun hashCode(): Int = super.hashCode()
 }
 
 /**
@@ -237,7 +237,7 @@
     override fun equals(other: Any?): Boolean =
         other is AnimationVector3D && other.v1 == v1 && other.v2 == v2 && other.v3 == v3
 
-    override fun hashCode(): Int = identityHashCode()
+    override fun hashCode(): Int = super.hashCode()
 }
 
 /**
@@ -312,5 +312,5 @@
             other.v3 == v3 &&
             other.v4 == v4
 
-    override fun hashCode(): Int = identityHashCode()
+    override fun hashCode(): Int = super.hashCode()
 }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PropKey.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PropKey.kt
index 7b55b2e..05991c4 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PropKey.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PropKey.kt
@@ -110,22 +110,6 @@
 
 /**
  * A [TwoWayConverter] that converts [Float] from and to [AnimationVector1D]
- * @see [Float.Companion.VectorConverter]
- */
-@Deprecated("", ReplaceWith("Float.VectorConverter"))
-val FloatToVectorConverter: TwoWayConverter<Float, AnimationVector1D> =
-    Float.VectorConverter
-
-/**
- * A [TwoWayConverter] that converts [Int] from and to [AnimationVector1D]
- * @see [Int.Companion.VectorConverter]
- */
-@Deprecated("", ReplaceWith("Int.VectorConverter"))
-val IntToVectorConverter: TwoWayConverter<Int, AnimationVector1D> =
-    Int.VectorConverter
-
-/**
- * A [TwoWayConverter] that converts [Float] from and to [AnimationVector1D]
  */
 val Float.Companion.VectorConverter: TwoWayConverter<Float, AnimationVector1D>
     get() = FloatToVector
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
new file mode 100644
index 0000000..c91c1dd
--- /dev/null
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -0,0 +1,624 @@
+/*
+ * 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.animation.core
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.collection.mutableVectorOf
+import androidx.compose.runtime.dispatch.withFrameNanos
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.unit.Bounds
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.Position
+import androidx.compose.ui.unit.Uptime
+import androidx.compose.ui.util.annotation.VisibleForTesting
+
+/**
+ * This sets up a [Transition], and updates it with the target provided by [targetState]. When
+ * [targetState] changes, [Transition] will run all of its child animations towards their
+ * target values specified for the new [targetState]. Child animations can be dynamically added
+ * using [Transition.animateFloat], [animateColor][ androidx.compose.animation.animateColor],
+ * [Transition.animateValue], etc.
+ *
+ * When all the animations in the transition have finished running, the provided [onFinished] will
+ * be invoked.
+ *
+ * @sample androidx.compose.animation.core.samples.GestureAnimationSample
+ *
+ * @return a [Transition] object, to which animations can be added.
+ * @see Transition
+ * @see animateFloat
+ * @see animateValue
+ * @see androidx.compose.animation.animateColor
+ */
+@Composable
+fun <T> updateTransition(
+    targetState: T,
+    onFinished: (T) -> Unit = {}
+): Transition<T> {
+    val listener = rememberUpdatedState(onFinished)
+    val transition = remember { Transition(targetState, listener) }
+    // This is needed because child animations rely on this target state and the state pair to
+    // update their animation specs
+    transition.updateTarget(targetState)
+    SideEffect {
+        transition.animateTo(targetState)
+    }
+    if (transition.isRunning || transition.startRequested) {
+        LaunchedEffect(transition) {
+            while (true) {
+                withFrameNanos {
+                    transition.onFrame(it)
+                }
+            }
+        }
+    }
+    return transition
+}
+
+/**
+ * [Transition] manages all the child animations on a state level. Child animations
+ * can be created in a declarative way using [animateFloat], [animateValue],
+ * [animateColor][androidx.compose.animation.animateColor] etc. When the [targetState] changes,
+ * [Transition] will automatically start or adjust course for all its child animations to animate
+ * to the new target values defined for each animation.
+ *
+ * After arriving at [targetState], [Transition] will be triggered to run if any child animation
+ * changes its target value (due to their dynamic target calculation logic, such as theme-dependent
+ * values).
+ *
+ * @sample androidx.compose.animation.core.samples.GestureAnimationSample
+ *
+ * @return a [Transition] object, to which animations can be added.
+ * @see updateTransition
+ * @see animateFloat
+ * @see animateValue
+ * @see androidx.compose.animation.animateColor
+ */
+// TODO: Support creating Transition outside of composition and support imperative use of Transition
+class Transition<S> internal constructor(
+    initialState: S,
+    private val onFinished: State<(S) -> Unit>
+) {
+    /**
+     * Current state of the transition. This will always be the initialState of the transition
+     * until the transition is finished. Once the transition is finished, [currentState] will be
+     * set to [targetState].
+     */
+    var currentState: S by mutableStateOf(initialState)
+        internal set
+
+    /**
+     * Target state of the transition. This will be read by all child animations to determine their
+     * most up-to-date target values.
+     */
+    var targetState: S by mutableStateOf(initialState)
+        internal set
+
+    /**
+     * [transitionStates] contains the initial state and the target state of the currently on-going
+     * transition.
+     */
+    var transitionStates: States<S> by mutableStateOf(States(initialState, initialState))
+        private set
+
+    /**
+     * Indicates whether there is any animation running in the transition.
+     */
+    val isRunning: Boolean
+        get() = startTime != Uptime.Unspecified
+
+    /**
+     * Play time in nano-seconds. [playTimeNanos] is always non-negative. It starts from 0L at the
+     * beginning of the transition and increment until all child animations have finished.
+     */
+    @VisibleForTesting
+    internal var playTimeNanos by mutableStateOf(0L)
+    internal var startRequested: Boolean by mutableStateOf(false)
+    private var startTime = Uptime.Unspecified
+    private val animations = mutableVectorOf<TransitionAnimationState<*, *>>()
+
+    // Target state that is currently being animated to
+    private var currentTargetState: S = initialState
+
+    internal fun onFrame(frameTimeNanos: Long) {
+        if (startTime == Uptime.Unspecified) {
+            startTime = Uptime(frameTimeNanos)
+        }
+        startRequested = false
+
+        // Update play time
+        playTimeNanos = frameTimeNanos - startTime.nanoseconds
+        var allFinished = true
+        // Pulse new playtime
+        animations.forEach {
+            if (!it.isFinished) {
+                it.onPlayTimeChanged(playTimeNanos)
+            }
+            // Check isFinished flag again after the animation pulse
+            if (!it.isFinished) {
+                allFinished = false
+            }
+        }
+        if (allFinished) {
+            startTime = Uptime.Unspecified
+            currentState = targetState
+            playTimeNanos = 0
+            onFinished.value(targetState)
+        }
+    }
+
+    @PublishedApi
+    internal fun addAnimation(animation: TransitionAnimationState<*, *>) =
+        animations.add(animation)
+
+    @PublishedApi
+    internal fun removeAnimation(animation: TransitionAnimationState<*, *>) {
+        animations.remove(animation)
+    }
+
+    // This target state should only be used to modify "mutableState"s, as it could potentially
+    // roll back. The
+    internal fun updateTarget(targetState: S) {
+        if (transitionStates.targetState != targetState) {
+            if (currentState == targetState) {
+                // Going backwards
+                transitionStates = States(this.targetState, targetState)
+            } else {
+                transitionStates = States(currentState, targetState)
+            }
+        }
+        this.targetState = targetState
+    }
+
+    internal fun animateTo(targetState: S) {
+        if (targetState != currentTargetState) {
+            if (isRunning) {
+                startTime = Uptime(startTime.nanoseconds + playTimeNanos)
+                playTimeNanos = 0
+            } else {
+                startRequested = true
+            }
+            currentTargetState = targetState
+            // If target state is changed, reset all the animations to be re-created in the
+            // next frame w/ their new target value. Child animations target values are updated in
+            // the side effect that may not have happened when this function in invoked.
+            animations.forEach { it.resetAnimation() }
+        }
+    }
+
+    // Called from children to start an animation
+    private fun requestStart() {
+        startRequested = true
+    }
+
+    // TODO: Consider making this public
+    @PublishedApi
+    internal inner class TransitionAnimationState<T, V : AnimationVector> @PublishedApi internal
+    constructor(
+        initialValue: T,
+        initialVelocityVector: V,
+        val typeConverter: TwoWayConverter<T, V>
+    ) : State<T> {
+
+        override var value by mutableStateOf(initialValue)
+            internal set
+
+        var targetValue: T = initialValue
+            internal set
+        var velocityVector: V = initialVelocityVector
+            internal set
+        var isFinished: Boolean by mutableStateOf(true)
+            private set
+        private var animation: Animation<T, V>? = null
+
+        @PublishedApi
+        internal var animationSpec: FiniteAnimationSpec<T> = spring()
+        private var offsetTimeNanos = 0L
+
+        internal fun onPlayTimeChanged(playTimeNanos: Long) {
+            val anim = animation ?: TargetBasedAnimation<T, V>(
+                animationSpec,
+                value,
+                targetValue,
+                typeConverter,
+                velocityVector
+            ).also { animation = it }
+            val playTimeMillis = (playTimeNanos - offsetTimeNanos) / 1_000_000L
+            value = anim.getValue(playTimeMillis)
+            velocityVector = anim.getVelocityVector(playTimeMillis)
+            if (anim.isFinished(playTimeMillis)) {
+                isFinished = true
+                offsetTimeNanos = 0
+            }
+        }
+
+        internal fun resetAnimation() {
+            animation = null
+            offsetTimeNanos = 0
+            isFinished = false
+        }
+
+        @PublishedApi
+        // This gets called from a side effect.
+        internal fun updateTargetValue(targetValue: T) {
+            if (this.targetValue != targetValue) {
+                this.targetValue = targetValue
+                isFinished = false
+                animation = null
+                offsetTimeNanos = playTimeNanos
+                requestStart()
+            }
+        }
+    }
+
+    /**
+     * [States] holds [initialState] and [targetState], which are the beginning and end of a
+     * transition. These states will be used to obtain the animation spec that will be used for this
+     * transition from the child animations.
+     */
+    class States<S>(val initialState: S, val targetState: S)
+}
+
+/**
+ * Creates an animation of type [T] as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition]. [typeConverter] will be used to convert
+ * between type [T] and [AnimationVector] so that the animation system knows how to animate it.
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ * @see updateTransition
+ * @see animateFloat
+ * @see androidx.compose.animation.animateColor
+ */
+@Composable
+inline fun <S, T, V : AnimationVector> Transition<S>.animateValue(
+    typeConverter: TwoWayConverter<T, V>,
+    noinline transitionSpec: @Composable (Transition.States<S>) -> FiniteAnimationSpec<T> =
+        { spring() },
+    targetValueByState: @Composable (state: S) -> T
+): State<T> {
+    val targetValue = targetValueByState(targetState)
+    val transitionAnimation = remember {
+        TransitionAnimationState(
+            targetValue,
+            typeConverter.createZeroVectorFrom(targetValue),
+            typeConverter
+        )
+    }
+    val spec = transitionSpec(transitionStates)
+
+    SideEffect {
+        transitionAnimation.animationSpec = spec
+        transitionAnimation.updateTargetValue(targetValue)
+    }
+
+    DisposableEffect(transitionAnimation) {
+        addAnimation(transitionAnimation)
+        onDispose {
+            removeAnimation(transitionAnimation)
+        }
+    }
+    return transitionAnimation
+}
+
+// TODO: Remove noinline when b/174814083 is fixed.
+/**
+ * Creates a Float animation as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @sample androidx.compose.animation.core.samples.AnimateFloatSample
+ *
+ * @return A [State] object, the value of which is updated by animation
+ * @see updateTransition
+ * @see animateValue
+ * @see androidx.compose.animation.animateColor
+ */
+@Composable
+inline fun <S> Transition<S>.animateFloat(
+    noinline transitionSpec:
+        @Composable (Transition.States<S>) -> FiniteAnimationSpec<Float> = { spring() },
+    targetValueByState: @Composable (state: S) -> Float
+): State<Float> =
+    animateValue(Float.VectorConverter, transitionSpec, targetValueByState)
+
+/**
+ * Creates a [Dp] animation as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ */
+@Composable
+inline fun <S> Transition<S>.animateDp(
+    noinline transitionSpec: @Composable (Transition.States<S>) -> FiniteAnimationSpec<Dp> = {
+        spring(visibilityThreshold = Dp.VisibilityThreshold)
+    },
+    targetValueByState: @Composable (state: S) -> Dp
+): State<Dp> =
+    animateValue(Dp.VectorConverter, transitionSpec, targetValueByState)
+
+/**
+ * Creates an [Offset] animation as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ */
+@Composable
+inline fun <S> Transition<S>.animateOffset(
+    noinline transitionSpec: @Composable (Transition.States<S>) -> FiniteAnimationSpec<Offset> = {
+        spring(visibilityThreshold = Offset.VisibilityThreshold)
+    },
+    targetValueByState: @Composable (state: S) -> Offset
+): State<Offset> =
+    animateValue(Offset.VectorConverter, transitionSpec, targetValueByState)
+
+/**
+ * Creates a [Position] animation as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ */
+@Composable
+inline fun <S> Transition<S>.animatePosition(
+    noinline transitionSpec: @Composable (Transition.States<S>) -> FiniteAnimationSpec<Position> = {
+        spring(visibilityThreshold = Position.VisibilityThreshold)
+    },
+    targetValueByState: @Composable (state: S) -> Position
+): State<Position> =
+    animateValue(Position.VectorConverter, transitionSpec, targetValueByState)
+
+/**
+ * Creates a [Size] animation as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ */
+@Composable
+inline fun <S> Transition<S>.animateSize(
+    noinline transitionSpec: @Composable (Transition.States<S>) -> FiniteAnimationSpec<Size> = {
+        spring(visibilityThreshold = Size.VisibilityThreshold)
+    },
+    targetValueByState: @Composable (state: S) -> Size
+): State<Size> =
+    animateValue(Size.VectorConverter, transitionSpec, targetValueByState)
+
+/**
+ * Creates a [IntOffset] animation as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ */
+@Composable
+inline fun <S> Transition<S>.animateIntOffset(
+    noinline transitionSpec: @Composable (Transition.States<S>) -> FiniteAnimationSpec<IntOffset> =
+        { spring(visibilityThreshold = IntOffset(1, 1)) },
+    targetValueByState: @Composable (state: S) -> IntOffset
+): State<IntOffset> =
+    animateValue(IntOffset.VectorConverter, transitionSpec, targetValueByState)
+
+/**
+ * Creates a [Int] animation as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ */
+@Composable
+inline fun <S> Transition<S>.animateInt(
+    noinline transitionSpec: @Composable (Transition.States<S>) -> FiniteAnimationSpec<Int> = {
+        spring(visibilityThreshold = 1)
+    },
+    targetValueByState: @Composable (state: S) -> Int
+): State<Int> =
+    animateValue(Int.VectorConverter, transitionSpec, targetValueByState)
+
+/**
+ * Creates a [IntSize] animation as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ */
+@Composable
+inline fun <S> Transition<S>.animateIntSize(
+    noinline transitionSpec: @Composable (Transition.States<S>) -> FiniteAnimationSpec<IntSize> = {
+        spring(visibilityThreshold = IntSize(1, 1))
+    },
+    targetValueByState: @Composable (state: S) -> IntSize
+): State<IntSize> =
+    animateValue(IntSize.VectorConverter, transitionSpec, targetValueByState)
+
+/**
+ * Creates a [Bounds] animation as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ */
+@Composable
+inline fun <S> Transition<S>.animateBounds(
+    noinline transitionSpec: @Composable (Transition.States<S>) -> FiniteAnimationSpec<Bounds> = {
+        spring(visibilityThreshold = Bounds.VisibilityThreshold)
+    },
+    targetValueByState: @Composable (state: S) -> Bounds
+): State<Bounds> =
+    animateValue(Bounds.VectorConverter, transitionSpec, targetValueByState)
+
+/**
+ * Creates a [Rect] animation as a part of the given [Transition]. This means the states
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ */
+@Composable
+inline fun <S> Transition<S>.animateRect(
+    noinline transitionSpec: @Composable (Transition.States<S>) -> FiniteAnimationSpec<Rect> =
+        { spring(visibilityThreshold = Rect.VisibilityThreshold) },
+    targetValueByState: @Composable (state: S) -> Rect
+): State<Rect> =
+    animateValue(Rect.VectorConverter, transitionSpec, targetValueByState)
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionDefinition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionDefinition.kt
index 91b09ba..bc77654 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionDefinition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionDefinition.kt
@@ -17,7 +17,6 @@
 package androidx.compose.animation.core
 
 import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
-import androidx.compose.animation.core.AnimationConstants.Infinite
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.util.fastFirstOrNull
 
@@ -149,11 +148,11 @@
  * [TweenSpec], [KeyframesSpec]) the amount of iterations specified by [iterations].
  *
  * The iteration count describes the amount of times the animation will run.
- * 1 means no repeat. Use [Infinite] to create an infinity repeating animation.
+ * 1 means no repeat. Recommend [infiniteRepeatable] for creating an infinity repeating animation.
  *
  * __Note__: When repeating in the [RepeatMode.Reverse] mode, it's highly recommended to have an
- * __odd__ number of iterations, or [AnimationConstants.Infinite] iterations. Otherwise, the
- * animation may jump to the end value when it finishes the last iteration.
+ * __odd__ number of iterations. Otherwise, the animation may jump to the end value when it finishes
+ * the last iteration.
  *
  * @param iterations the total count of iterations, should be greater than 1 to repeat.
  * @param animation animation that will be repeated
@@ -165,16 +164,33 @@
     iterations: Int,
     animation: DurationBasedAnimationSpec<T>,
     repeatMode: RepeatMode = RepeatMode.Restart
-): AnimationSpec<T> =
+): RepeatableSpec<T> =
     RepeatableSpec(iterations, animation, repeatMode)
 
 /**
+ * Creates a [InfiniteRepeatableSpec] that plays a [DurationBasedAnimationSpec] (e.g.
+ * [TweenSpec], [KeyframesSpec]) infinite amount of iterations.
+ *
+ * For non-infinitely repeating animations, consider [repeatable].
+ *
+ * @param animation animation that will be repeated
+ * @param repeatMode whether animation should repeat by starting from the beginning (i.e.
+ *                  [RepeatMode.Restart]) or from the end (i.e. [RepeatMode.Reverse])
+ */
+@Stable
+fun <T> infiniteRepeatable(
+    animation: DurationBasedAnimationSpec<T>,
+    repeatMode: RepeatMode = RepeatMode.Restart
+): AnimationSpec<T> =
+    InfiniteRepeatableSpec(animation, repeatMode)
+
+/**
  * Creates a Snap animation for immediately switching the animating value to the end value.
  *
  * @param delayMillis the number of milliseconds to wait before the animation runs. 0 by default.
  */
 @Stable
-fun <T> snap(delayMillis: Int = 0): AnimationSpec<T> = SnapSpec(delayMillis)
+fun <T> snap(delayMillis: Int = 0) = SnapSpec<T>(delayMillis)
 
 /**
  * [TransitionDefinition] contains all the animation related configurations that will be used in
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt
new file mode 100644
index 0000000..585df63
--- /dev/null
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.animation.core
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.unit.Bounds
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.Position
+import androidx.compose.ui.unit.dp
+import kotlin.math.roundToInt
+
+/**
+ * A type converter that converts a [Rect] to a [AnimationVector4D], and vice versa.
+ */
+val Rect.Companion.VectorConverter: TwoWayConverter<Rect, AnimationVector4D>
+    get() = RectToVector
+
+/**
+ * A type converter that converts a [Dp] to a [AnimationVector1D], and vice versa.
+ */
+val Dp.Companion.VectorConverter: TwoWayConverter<Dp, AnimationVector1D>
+    get() = DpToVector
+
+/**
+ * A type converter that converts a [Position] to a [AnimationVector2D], and vice versa.
+ */
+val Position.Companion.VectorConverter: TwoWayConverter<Position, AnimationVector2D>
+    get() = PositionToVector
+
+/**
+ * A type converter that converts a [Size] to a [AnimationVector2D], and vice versa.
+ */
+val Size.Companion.VectorConverter: TwoWayConverter<Size, AnimationVector2D>
+    get() = SizeToVector
+
+/**
+ * A type converter that converts a [Bounds] to a [AnimationVector4D], and vice versa.
+ */
+val Bounds.Companion.VectorConverter: TwoWayConverter<Bounds, AnimationVector4D>
+    get() = BoundsToVector
+
+/**
+ * A type converter that converts a [Offset] to a [AnimationVector2D], and vice versa.
+ */
+val Offset.Companion.VectorConverter: TwoWayConverter<Offset, AnimationVector2D>
+    get() = OffsetToVector
+
+/**
+ * A type converter that converts a [IntOffset] to a [AnimationVector2D], and vice versa.
+ */
+val IntOffset.Companion.VectorConverter: TwoWayConverter<IntOffset, AnimationVector2D>
+    get() = IntOffsetToVector
+
+/**
+ * A type converter that converts a [IntSize] to a [AnimationVector2D], and vice versa.
+ */
+val IntSize.Companion.VectorConverter: TwoWayConverter<IntSize, AnimationVector2D>
+    get() = IntSizeToVector
+
+/**
+ * A type converter that converts a [Dp] to a [AnimationVector1D], and vice versa.
+ */
+private val DpToVector: TwoWayConverter<Dp, AnimationVector1D> = TwoWayConverter(
+    convertToVector = { AnimationVector1D(it.value) },
+    convertFromVector = { Dp(it.value) }
+)
+
+/**
+ * A type converter that converts a [Position] to a [AnimationVector2D], and vice versa.
+ */
+private val PositionToVector: TwoWayConverter<Position, AnimationVector2D> =
+    TwoWayConverter(
+        convertToVector = { AnimationVector2D(it.x.value, it.y.value) },
+        convertFromVector = { Position(it.v1.dp, it.v2.dp) }
+    )
+
+/**
+ * A type converter that converts a [Size] to a [AnimationVector2D], and vice versa.
+ */
+private val SizeToVector: TwoWayConverter<Size, AnimationVector2D> =
+    TwoWayConverter(
+        convertToVector = { AnimationVector2D(it.width, it.height) },
+        convertFromVector = { Size(it.v1, it.v2) }
+    )
+
+/**
+ * A type converter that converts a [Bounds] to a [AnimationVector4D], and vice versa.
+ */
+private val BoundsToVector: TwoWayConverter<Bounds, AnimationVector4D> =
+    TwoWayConverter(
+        convertToVector = {
+            AnimationVector4D(it.left.value, it.top.value, it.right.value, it.bottom.value)
+        },
+        convertFromVector = { Bounds(it.v1.dp, it.v2.dp, it.v3.dp, it.v4.dp) }
+    )
+
+/**
+ * A type converter that converts a [Offset] to a [AnimationVector2D], and vice versa.
+ */
+private val OffsetToVector: TwoWayConverter<Offset, AnimationVector2D> =
+    TwoWayConverter(
+        convertToVector = { AnimationVector2D(it.x, it.y) },
+        convertFromVector = { Offset(it.v1, it.v2) }
+    )
+
+/**
+ * A type converter that converts a [IntOffset] to a [AnimationVector2D], and vice versa.
+ */
+private val IntOffsetToVector: TwoWayConverter<IntOffset, AnimationVector2D> =
+    TwoWayConverter(
+        convertToVector = { AnimationVector2D(it.x.toFloat(), it.y.toFloat()) },
+        convertFromVector = { IntOffset(it.v1.roundToInt(), it.v2.roundToInt()) }
+    )
+
+/**
+ * A type converter that converts a [IntSize] to a [AnimationVector2D], and vice versa.
+ */
+private val IntSizeToVector: TwoWayConverter<IntSize, AnimationVector2D> =
+    TwoWayConverter(
+        { AnimationVector2D(it.width.toFloat(), it.height.toFloat()) },
+        { IntSize(it.v1.roundToInt(), it.v2.roundToInt()) }
+    )
+
+/**
+ * A type converter that converts a [Rect] to a [AnimationVector4D], and vice versa.
+ */
+private val RectToVector: TwoWayConverter<Rect, AnimationVector4D> =
+    TwoWayConverter(
+        convertToVector = {
+            AnimationVector4D(it.left, it.top, it.right, it.bottom)
+        },
+        convertFromVector = {
+            Rect(it.v1, it.v2, it.v3, it.v4)
+        }
+    )
\ No newline at end of file
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
index fd8d44d..bd1e235 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
@@ -17,7 +17,6 @@
 package androidx.compose.animation.core
 
 import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
-import androidx.compose.animation.core.AnimationConstants.Infinite
 import kotlin.math.min
 
 /**
@@ -107,13 +106,23 @@
 }
 
 /**
+ * All the finite [VectorizedAnimationSpec]s implement this interface, including:
+ * [VectorizedKeyframesSpec], [VectorizedTweenSpec], [VectorizedRepeatableSpec],
+ * [VectorizedSnapSpec], [VectorizedSpringSpec], etc. The [VectorizedAnimationSpec] that does
+ * __not__ implement this is: [InfiniteRepeatableSpec].
+ */
+interface VectorizedFiniteAnimationSpec<V : AnimationVector> : VectorizedAnimationSpec<V>
+
+/**
  * Base class for [VectorizedAnimationSpec]s that are based on a fixed [durationMillis].
  */
-interface VectorizedDurationBasedAnimationSpec<V : AnimationVector> : VectorizedAnimationSpec<V> {
+interface VectorizedDurationBasedAnimationSpec<V : AnimationVector> :
+    VectorizedFiniteAnimationSpec<V> {
     /**
      * duration is the amount of time while animation is not yet finished.
      */
     val durationMillis: Int
+
     /**
      * delay defines the amount of time that animation can be delayed.
      */
@@ -265,19 +274,41 @@
         get() = 0
 }
 
+private const val InfiniteIterations: Int = Int.MAX_VALUE
+
 /**
  * This animation takes another [VectorizedDurationBasedAnimationSpec] and plays it
- * [iterations] times.
+ * __infinite__ times.
  *
- * @param iterations the count of iterations. Should be at least 1. [Infinite] can
- *                   be used to have an infinity repeating animation.
  * @param animation the [VectorizedAnimationSpec] describing each repetition iteration.
+ * @param repeatMode whether animation should repeat by starting from the beginning (i.e.
+ *                  [RepeatMode.Restart]) or from the end (i.e. [RepeatMode.Reverse])
+ */
+class VectorizedInfiniteRepeatableSpec<V : AnimationVector>(
+    private val animation: VectorizedDurationBasedAnimationSpec<V>,
+    private val repeatMode: RepeatMode = RepeatMode.Restart
+) : VectorizedAnimationSpec<V> by
+    VectorizedRepeatableSpec<V>(InfiniteIterations, animation, repeatMode)
+
+/**
+ * This animation takes another [VectorizedDurationBasedAnimationSpec] and plays it
+ * [iterations] times. For infinitely repeating animation spec, [VectorizedInfiniteRepeatableSpec]
+ * is recommended.
+ *
+ * __Note__: When repeating in the [RepeatMode.Reverse] mode, it's highly recommended to have an
+ * __odd__ number of iterations. Otherwise, the animation may jump to the end value when it finishes
+ * the last iteration.
+ *
+ * @param iterations the count of iterations. Should be at least 1.
+ * @param animation the [VectorizedAnimationSpec] describing each repetition iteration.
+ * @param repeatMode whether animation should repeat by starting from the beginning (i.e.
+ *                  [RepeatMode.Restart]) or from the end (i.e. [RepeatMode.Reverse])
  */
 class VectorizedRepeatableSpec<V : AnimationVector>(
     private val iterations: Int,
     private val animation: VectorizedDurationBasedAnimationSpec<V>,
     private val repeatMode: RepeatMode = RepeatMode.Restart
-) : VectorizedAnimationSpec<V> {
+) : VectorizedFiniteAnimationSpec<V> {
 
     init {
         if (iterations < 1) {
@@ -345,15 +376,18 @@
      * Stiffness constant for extremely stiff spring
      */
     const val StiffnessHigh = 10_000f
+
     /**
      * Stiffness constant for medium stiff spring. This is the default stiffness for spring
      * force.
      */
     const val StiffnessMedium = 1500f
+
     /**
      * Stiffness constant for a spring with low stiffness.
      */
     const val StiffnessLow = 200f
+
     /**
      * Stiffness constant for a spring with very low stiffness.
      */
@@ -364,23 +398,27 @@
      * (i.e. damping ratio < 1), the lower the damping ratio, the more bouncy the spring.
      */
     const val DampingRatioHighBouncy = 0.2f
+
     /**
      * Damping ratio for a medium bouncy spring. This is also the default damping ratio for
      * spring force. Note for under-damped springs (i.e. damping ratio < 1), the lower the
      * damping ratio, the more bouncy the spring.
      */
     const val DampingRatioMediumBouncy = 0.5f
+
     /**
      * Damping ratio for a spring with low bounciness. Note for under-damped springs
      * (i.e. damping ratio < 1), the lower the damping ratio, the higher the bounciness.
      */
     const val DampingRatioLowBouncy = 0.75f
+
     /**
      * Damping ratio for a spring with no bounciness. This damping ratio will create a
      * critically damped spring that returns to equilibrium within the shortest amount of time
      * without oscillating.
      */
     const val DampingRatioNoBouncy = 1f
+
     /**
      * Default cutoff for rounding off physics based animations
      */
@@ -401,7 +439,7 @@
     val dampingRatio: Float,
     val stiffness: Float,
     anims: Animations
-) : VectorizedAnimationSpec<V> by VectorizedFloatAnimationSpec<V>(anims) {
+) : VectorizedFiniteAnimationSpec<V> by VectorizedFloatAnimationSpec<V>(anims) {
 
     /**
      * Creates a [VectorizedSpringSpec] that uses the same spring constants (i.e. [dampingRatio] and
@@ -482,7 +520,7 @@
  */
 class VectorizedFloatAnimationSpec<V : AnimationVector> internal constructor(
     private val anims: Animations
-) : VectorizedAnimationSpec<V> {
+) : VectorizedFiniteAnimationSpec<V> {
     private lateinit var valueVector: V
     private lateinit var velocityVector: V
     private lateinit var endVelocityVector: V
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt
new file mode 100644
index 0000000..cc50e01
--- /dev/null
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.animation.core
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.unit.Bounds
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.Position
+import androidx.compose.ui.unit.dp
+
+private const val DpVisibilityThreshold = 0.1f
+private const val PxVisibilityThreshold = 0.5f
+
+private val boundsVisibilityThreshold = Bounds(
+    Dp.VisibilityThreshold,
+    Dp.VisibilityThreshold,
+    Dp.VisibilityThreshold,
+    Dp.VisibilityThreshold
+)
+
+private val rectVisibilityThreshold = Rect(
+    PxVisibilityThreshold,
+    PxVisibilityThreshold,
+    PxVisibilityThreshold,
+    PxVisibilityThreshold
+)
+
+/**
+ * Visibility threshold for [IntOffset]. This defines the amount of value change that is
+ * considered to be no longer visible. The animation system uses this to signal to some default
+ * [spring] animations to stop when the value is close enough to the target.
+ */
+val IntOffset.Companion.VisibilityThreshold: IntOffset
+    get() = IntOffset(1, 1)
+
+/**
+ * Visibility threshold for [Offset]. This defines the amount of value change that is
+ * considered to be no longer visible. The animation system uses this to signal to some default
+ * [spring] animations to stop when the value is close enough to the target.
+ */
+val Offset.Companion.VisibilityThreshold: Offset
+    get() = Offset(PxVisibilityThreshold, PxVisibilityThreshold)
+
+/**
+ * Visibility threshold for [Int]. This defines the amount of value change that is
+ * considered to be no longer visible. The animation system uses this to signal to some default
+ * [spring] animations to stop when the value is close enough to the target.
+ */
+val Int.Companion.VisibilityThreshold: Int
+    get() = 1
+
+/**
+ * Visibility threshold for [Dp]. This defines the amount of value change that is
+ * considered to be no longer visible. The animation system uses this to signal to some default
+ * [spring] animations to stop when the value is close enough to the target.
+ */
+val Dp.Companion.VisibilityThreshold: Dp
+    get() = DpVisibilityThreshold.dp
+
+/**
+ * Visibility threshold for [Position]. This defines the amount of value change that is
+ * considered to be no longer visible. The animation system uses this to signal to some default
+ * [spring] animations to stop when the value is close enough to the target.
+ */
+val Position.Companion.VisibilityThreshold: Position
+    get() = Position(Dp.VisibilityThreshold, Dp.VisibilityThreshold)
+
+/**
+ * Visibility threshold for [Size]. This defines the amount of value change that is
+ * considered to be no longer visible. The animation system uses this to signal to some default
+ * [spring] animations to stop when the value is close enough to the target.
+ */
+val Size.Companion.VisibilityThreshold: Size
+    get() = Size(PxVisibilityThreshold, PxVisibilityThreshold)
+
+/**
+ * Visibility threshold for [IntSize]. This defines the amount of value change that is
+ * considered to be no longer visible. The animation system uses this to signal to some default
+ * [spring] animations to stop when the value is close enough to the target.
+ */
+val IntSize.Companion.VisibilityThreshold: IntSize
+    get() = IntSize(1, 1)
+
+/**
+ * Visibility threshold for [Rect]. This defines the amount of value change that is
+ * considered to be no longer visible. The animation system uses this to signal to some default
+ * [spring] animations to stop when the value is close enough to the target.
+ */
+val Rect.Companion.VisibilityThreshold: Rect
+    get() = rectVisibilityThreshold
+
+/**
+ * Visibility threshold for [Bounds]. This defines the amount of value change that is
+ * considered to be no longer visible. The animation system uses this to signal to some default
+ * [spring] animations to stop when the value is close enough to the target.
+ */
+val Bounds.Companion.VisibilityThreshold: Bounds
+    get() = boundsVisibilityThreshold
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
index 671941d..6524f1f 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
@@ -104,6 +104,25 @@
         assertEquals(100f, repeatAnim.getValue(901))
     }
 
+    @Test
+    fun testInfiniteRepeat() {
+        val repeat = infiniteRepeatable(
+            animation = TweenSpec<Float>(
+                durationMillis = 100, easing = LinearEasing
+            ),
+            repeatMode = RepeatMode.Reverse
+        )
+
+        assertEquals(
+            Int.MAX_VALUE.toLong() * 100,
+            repeat.vectorize(Float.VectorConverter).getDurationMillis(
+                AnimationVector(0f),
+                AnimationVector(100f),
+                AnimationVector(0f)
+            )
+        )
+    }
+
     private companion object {
         private val DelayDuration = 13
         private val Duration = 50
diff --git a/compose/animation/animation/api/current.txt b/compose/animation/animation/api/current.txt
index d9e7a92..3922f7a 100644
--- a/compose/animation/animation/api/current.txt
+++ b/compose/animation/animation/api/current.txt
@@ -94,10 +94,6 @@
   @kotlin.RequiresOptIn(message="This is an experimental animation API.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ExperimentalAnimationApi {
   }
 
-  public final class LegacyTransitionKt {
-    method @Deprecated @androidx.compose.runtime.Composable public static <T> void Transition(androidx.compose.animation.core.TransitionDefinition<T> definition, T? toState, optional androidx.compose.animation.core.AnimationClockObservable clock, optional T? initState, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished, kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.TransitionState,kotlin.Unit> children);
-  }
-
   public final class OffsetPropKey implements androidx.compose.animation.core.PropKey<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> {
     ctor public OffsetPropKey(String label);
     ctor public OffsetPropKey();
@@ -109,14 +105,14 @@
 
   public final class PropertyKeysKt {
     method public static kotlin.jvm.functions.Function1<androidx.compose.ui.graphics.colorspace.ColorSpace,androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.graphics.Color,androidx.compose.animation.core.AnimationVector4D>> getVectorConverter(androidx.compose.ui.graphics.Color.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Rect,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.geometry.Rect.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(androidx.compose.ui.unit.Dp.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Position,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.Position.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Size,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Size.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Bounds,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.unit.Bounds.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Offset.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntOffset.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntSize,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntSize.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Rect,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.geometry.Rect.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(androidx.compose.ui.unit.Dp.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Position,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.Position.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Size,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Size.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Bounds,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.unit.Bounds.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Offset.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntOffset.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntSize,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntSize.Companion);
   }
 
   public final class PxPropKey implements androidx.compose.animation.core.PropKey<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> {
@@ -154,6 +150,7 @@
   }
 
   public final class TransitionKt {
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> animateColor(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.graphics.Color>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.graphics.Color> targetValueByState);
     method @Deprecated @VisibleForTesting public static void setTransitionsEnabled(boolean p);
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.TransitionState transition(androidx.compose.animation.core.TransitionDefinition<T> definition, T? toState, optional androidx.compose.animation.core.AnimationClockObservable clock, optional T? initState, optional String? label, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished);
   }
diff --git a/compose/animation/animation/api/public_plus_experimental_current.txt b/compose/animation/animation/api/public_plus_experimental_current.txt
index d9e7a92..3922f7a 100644
--- a/compose/animation/animation/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation/api/public_plus_experimental_current.txt
@@ -94,10 +94,6 @@
   @kotlin.RequiresOptIn(message="This is an experimental animation API.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ExperimentalAnimationApi {
   }
 
-  public final class LegacyTransitionKt {
-    method @Deprecated @androidx.compose.runtime.Composable public static <T> void Transition(androidx.compose.animation.core.TransitionDefinition<T> definition, T? toState, optional androidx.compose.animation.core.AnimationClockObservable clock, optional T? initState, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished, kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.TransitionState,kotlin.Unit> children);
-  }
-
   public final class OffsetPropKey implements androidx.compose.animation.core.PropKey<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> {
     ctor public OffsetPropKey(String label);
     ctor public OffsetPropKey();
@@ -109,14 +105,14 @@
 
   public final class PropertyKeysKt {
     method public static kotlin.jvm.functions.Function1<androidx.compose.ui.graphics.colorspace.ColorSpace,androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.graphics.Color,androidx.compose.animation.core.AnimationVector4D>> getVectorConverter(androidx.compose.ui.graphics.Color.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Rect,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.geometry.Rect.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(androidx.compose.ui.unit.Dp.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Position,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.Position.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Size,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Size.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Bounds,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.unit.Bounds.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Offset.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntOffset.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntSize,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntSize.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Rect,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.geometry.Rect.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(androidx.compose.ui.unit.Dp.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Position,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.Position.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Size,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Size.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Bounds,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.unit.Bounds.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Offset.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntOffset.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntSize,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntSize.Companion);
   }
 
   public final class PxPropKey implements androidx.compose.animation.core.PropKey<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> {
@@ -154,6 +150,7 @@
   }
 
   public final class TransitionKt {
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> animateColor(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.graphics.Color>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.graphics.Color> targetValueByState);
     method @Deprecated @VisibleForTesting public static void setTransitionsEnabled(boolean p);
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.TransitionState transition(androidx.compose.animation.core.TransitionDefinition<T> definition, T? toState, optional androidx.compose.animation.core.AnimationClockObservable clock, optional T? initState, optional String? label, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished);
   }
diff --git a/compose/animation/animation/api/restricted_current.txt b/compose/animation/animation/api/restricted_current.txt
index d9e7a92..3922f7a 100644
--- a/compose/animation/animation/api/restricted_current.txt
+++ b/compose/animation/animation/api/restricted_current.txt
@@ -94,10 +94,6 @@
   @kotlin.RequiresOptIn(message="This is an experimental animation API.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ExperimentalAnimationApi {
   }
 
-  public final class LegacyTransitionKt {
-    method @Deprecated @androidx.compose.runtime.Composable public static <T> void Transition(androidx.compose.animation.core.TransitionDefinition<T> definition, T? toState, optional androidx.compose.animation.core.AnimationClockObservable clock, optional T? initState, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished, kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.TransitionState,kotlin.Unit> children);
-  }
-
   public final class OffsetPropKey implements androidx.compose.animation.core.PropKey<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> {
     ctor public OffsetPropKey(String label);
     ctor public OffsetPropKey();
@@ -109,14 +105,14 @@
 
   public final class PropertyKeysKt {
     method public static kotlin.jvm.functions.Function1<androidx.compose.ui.graphics.colorspace.ColorSpace,androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.graphics.Color,androidx.compose.animation.core.AnimationVector4D>> getVectorConverter(androidx.compose.ui.graphics.Color.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Rect,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.geometry.Rect.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(androidx.compose.ui.unit.Dp.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Position,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.Position.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Size,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Size.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Bounds,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.unit.Bounds.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Offset.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntOffset.Companion);
-    method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntSize,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntSize.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Rect,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.geometry.Rect.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(androidx.compose.ui.unit.Dp.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Position,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.Position.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Size,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Size.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Bounds,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.unit.Bounds.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Offset.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntOffset.Companion);
+    method @Deprecated public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntSize,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntSize.Companion);
   }
 
   public final class PxPropKey implements androidx.compose.animation.core.PropKey<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> {
@@ -154,6 +150,7 @@
   }
 
   public final class TransitionKt {
+    method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> animateColor(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.States<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.graphics.Color>> transitionSpec, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.graphics.Color> targetValueByState);
     method @Deprecated @VisibleForTesting public static void setTransitionsEnabled(boolean p);
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.TransitionState transition(androidx.compose.animation.core.TransitionDefinition<T> definition, T? toState, optional androidx.compose.animation.core.AnimationClockObservable clock, optional T? initState, optional String? label, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished);
   }
diff --git a/compose/animation/animation/integration-tests/animation-demos/build.gradle b/compose/animation/animation/integration-tests/animation-demos/build.gradle
index 043cb3a..feac8d4 100644
--- a/compose/animation/animation/integration-tests/animation-demos/build.gradle
+++ b/compose/animation/animation/integration-tests/animation-demos/build.gradle
@@ -21,6 +21,7 @@
     implementation project(":compose:ui:ui-text")
     implementation project(':compose:animation:animation')
     implementation project(':compose:animation:animation:animation-samples')
+    implementation project(':compose:animation:animation-core:animation-core-samples')
     implementation project(':compose:foundation:foundation')
     implementation project(':compose:material:material')
 }
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisiblilityLazyColumnDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisiblilityLazyColumnDemo.kt
index d28cb90..2d1b4b4 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisiblilityLazyColumnDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisiblilityLazyColumnDemo.kt
@@ -27,7 +27,7 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumnForIndexed
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.Button
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
@@ -62,13 +62,15 @@
                 Text("Remove")
             }
         }
-        LazyColumnForIndexed(turquoiseColors) { i, color ->
-            AnimatedVisibility(
-                (turquoiseColors.size - itemNum) <= i,
-                enter = expandVertically(),
-                exit = shrinkVertically()
-            ) {
-                Spacer(Modifier.fillMaxWidth().height(90.dp).background(color))
+        LazyColumn {
+            itemsIndexed(turquoiseColors) { i, color ->
+                AnimatedVisibility(
+                    (turquoiseColors.size - itemNum) <= i,
+                    enter = expandVertically(),
+                    exit = shrinkVertically()
+                ) {
+                    Spacer(Modifier.fillMaxWidth().height(90.dp).background(color))
+                }
             }
         }
 
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
index 1dbecc6..59b6638 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
@@ -26,9 +26,6 @@
             "State Transition Demos",
             listOf(
                 ComposableDemo("Multi-dimensional prop") { MultiDimensionalAnimationDemo() },
-                ComposableDemo("State animation with interruptions") {
-                    StateAnimationWithInterruptionsDemo()
-                },
                 ComposableDemo("State based ripple") { StateBasedRippleDemo() },
                 ComposableDemo("Repeating rotation") { RepeatedRotationDemo() },
                 ComposableDemo("Manual animation clock") { AnimatableSeekBarDemo() },
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt
index d5b30d9..4e85b17 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt
@@ -37,7 +37,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.wrapContentWidth
-import androidx.compose.foundation.lazy.LazyColumnFor
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.selection.selectable
 import androidx.compose.material.Button
 import androidx.compose.material.Checkbox
@@ -224,11 +224,10 @@
                     }
                 }
             }
-            LazyColumnFor(
-                menuText,
-                modifier = Modifier.fillMaxSize().background(Color(0xFFd8c7ff))
-            ) {
-                Text(it, Modifier.padding(5.dp))
+            LazyColumn(Modifier.fillMaxSize().background(Color(0xFFd8c7ff))) {
+                items(menuText) {
+                    Text(it, Modifier.padding(5.dp))
+                }
             }
         }
     }
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/GestureBasedAnimationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/GestureBasedAnimationDemo.kt
index 734e749..d0801a5 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/GestureBasedAnimationDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/GestureBasedAnimationDemo.kt
@@ -16,69 +16,10 @@
 
 package androidx.compose.animation.demos
 
-import androidx.compose.animation.ColorPropKey
-import androidx.compose.animation.core.FloatPropKey
-import androidx.compose.animation.core.spring
-import androidx.compose.animation.core.transitionDefinition
-import androidx.compose.animation.transition
-import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.animation.core.samples.GestureAnimationSample
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.gesture.pressIndicatorGestureFilter
-import androidx.compose.ui.graphics.Color
-
-private const val halfSize = 200f
-
-private enum class ComponentState { Pressed, Released }
-
-private val scale = FloatPropKey()
-private val color = ColorPropKey()
-
-private val definition = transitionDefinition<ComponentState> {
-    state(ComponentState.Released) {
-        this[scale] = 1f
-        this[color] = Color(red = 0, green = 200, blue = 0, alpha = 255)
-    }
-    state(ComponentState.Pressed) {
-        this[scale] = 3f
-        this[color] = Color(red = 0, green = 100, blue = 0, alpha = 255)
-    }
-    transition {
-        scale using spring(
-            stiffness = 50f
-        )
-        color using spring(
-            stiffness = 50f
-        )
-    }
-}
 
 @Composable
 fun GestureBasedAnimationDemo() {
-    val toState = remember { mutableStateOf(ComponentState.Released) }
-    val pressIndicator =
-        Modifier.pressIndicatorGestureFilter(
-            onStart = { toState.value = ComponentState.Pressed },
-            onStop = { toState.value = ComponentState.Released },
-            onCancel = { toState.value = ComponentState.Released }
-        )
-
-    val state = transition(definition = definition, toState = toState.value)
-    ScaledColorRect(pressIndicator, scale = state[scale], color = state[color])
-}
-
-@Composable
-private fun ScaledColorRect(modifier: Modifier = Modifier, scale: Float, color: Color) {
-    Canvas(modifier.fillMaxSize()) {
-        drawRect(
-            color,
-            topLeft = Offset(center.x - halfSize * scale, center.y - halfSize * scale),
-            size = Size(halfSize * 2 * scale, halfSize * 2 * scale)
-        )
-    }
+    GestureAnimationSample()
 }
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteAnimationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteAnimationDemo.kt
index 0070e96..e0aa04d 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteAnimationDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteAnimationDemo.kt
@@ -16,10 +16,9 @@
 
 package androidx.compose.animation.demos
 
-import androidx.compose.animation.core.AnimationConstants
 import androidx.compose.animation.core.RepeatMode
 import androidx.compose.animation.core.animate
-import androidx.compose.animation.core.repeatable
+import androidx.compose.animation.core.infiniteRepeatable
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
@@ -42,8 +41,7 @@
         animate(
             initialValue = 1f,
             targetValue = 0f,
-            animationSpec = repeatable(
-                iterations = AnimationConstants.Infinite,
+            animationSpec = infiniteRepeatable(
                 animation = tween(1000),
                 repeatMode = RepeatMode.Reverse
             )
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/MultiDimensionalAnimationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/MultiDimensionalAnimationDemo.kt
index a54d5b7..eb04f22 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/MultiDimensionalAnimationDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/MultiDimensionalAnimationDemo.kt
@@ -16,18 +16,19 @@
 
 package androidx.compose.animation.demos
 
-import androidx.compose.animation.ColorPropKey
-import androidx.compose.animation.RectPropKey
+import androidx.compose.animation.animateColor
+import androidx.compose.animation.core.animateRect
 import androidx.compose.animation.core.spring
-import androidx.compose.animation.core.transitionDefinition
 import androidx.compose.animation.core.tween
-import androidx.compose.animation.transition
+import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
@@ -36,33 +37,42 @@
 
 @Composable
 fun MultiDimensionalAnimationDemo() {
-    val currentState = remember { mutableStateOf(AnimState.Collapsed) }
+    var currentState by remember { mutableStateOf(AnimState.Collapsed) }
     val onClick = {
         // Cycle through states when clicked.
-        currentState.value = when (currentState.value) {
+        currentState = when (currentState) {
             AnimState.Collapsed -> AnimState.Expanded
             AnimState.Expanded -> AnimState.PutAway
             AnimState.PutAway -> AnimState.Collapsed
         }
     }
 
-    val width = remember { mutableStateOf(0f) }
-    val height = remember { mutableStateOf(0f) }
-    val state = transition(
-        definition = remember(width.value, height.value) {
-            createTransDef(width.value, height.value)
-        },
-        toState = currentState.value
-    )
-    Canvas(modifier = Modifier.fillMaxSize().clickable(onClick = onClick, indication = null)) {
-        width.value = size.width
-        height.value = size.height
+    var width by remember { mutableStateOf(0f) }
+    var height by remember { mutableStateOf(0f) }
+    val transition = updateTransition(currentState)
+    val rect by transition.animateRect({ spring(stiffness = 100f) }) {
+        when (it) {
+            AnimState.Collapsed -> Rect(600f, 600f, 900f, 900f)
+            AnimState.Expanded -> Rect(0f, 400f, width, height - 400f)
+            AnimState.PutAway -> Rect(width - 300f, height - 300f, width, height)
+        }
+    }
 
-        val bounds = state[bounds]
+    val color by transition.animateColor(transitionSpec = { tween(durationMillis = 500) }) {
+        when (it) {
+            AnimState.Collapsed -> Color.LightGray
+            AnimState.Expanded -> Color(0xFFd0fff8)
+            AnimState.PutAway -> Color(0xFFe3ffd9)
+        }
+    }
+    Canvas(modifier = Modifier.fillMaxSize().clickable(onClick = onClick, indication = null)) {
+        width = size.width
+        height = size.height
+
         drawRect(
-            state[background],
-            topLeft = Offset(bounds.left, bounds.top),
-            size = Size(bounds.width, bounds.height)
+            color,
+            topLeft = Offset(rect.left, rect.top),
+            size = Size(rect.width, rect.height)
         )
     }
 }
@@ -71,36 +81,4 @@
     Collapsed,
     Expanded,
     PutAway
-}
-
-// Both PropKeys below are multi-dimensional property keys. That means each dimension's
-// value and velocity will be tracked independently. In the case of a color, each color
-// channel is a separate dimension. For rectangles, the dimensions are: top, left,
-// right and bottom.
-private val background = ColorPropKey()
-private val bounds = RectPropKey()
-
-private fun createTransDef(width: Float, height: Float) =
-    transitionDefinition<AnimState> {
-        state(AnimState.Collapsed) {
-            this[background] = Color.LightGray
-            this[bounds] = Rect(600f, 600f, 900f, 900f)
-        }
-        state(AnimState.Expanded) {
-            this[background] = Color(0xFFd0fff8)
-            this[bounds] = Rect(0f, 400f, width, height - 400f)
-        }
-        state(AnimState.PutAway) {
-            this[background] = Color(0xFFe3ffd9)
-            this[bounds] = Rect(width - 300f, height - 300f, width, height)
-        }
-
-        transition {
-            bounds using spring(
-                stiffness = 100f
-            )
-            background using tween(
-                durationMillis = 500
-            )
-        }
-    }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/RepeatedRotationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/RepeatedRotationDemo.kt
index 5917cc2..c365c64 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/RepeatedRotationDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/RepeatedRotationDemo.kt
@@ -16,31 +16,32 @@
 
 package androidx.compose.animation.demos
 
-import androidx.compose.animation.core.FloatPropKey
 import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.keyframes
 import androidx.compose.animation.core.repeatable
-import androidx.compose.animation.core.transitionDefinition
+import androidx.compose.animation.core.updateTransition
 import androidx.compose.animation.core.tween
-import androidx.compose.animation.transition
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.Button
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.gesture.tapGestureFilter
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.rotate
-import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
 
 @Composable
 fun RepeatedRotationDemo() {
@@ -50,23 +51,39 @@
             .wrapContentSize(Alignment.Center),
         verticalArrangement = Arrangement.SpaceEvenly
     ) {
-        val textStyle = TextStyle(fontSize = 18.sp)
-        Text(
-            modifier = Modifier.tapGestureFilter(onTap = { state.value = RotationStates.Rotated }),
-            text = "Rotate 10 times",
-            style = textStyle
-        )
-        Text(
-            modifier = Modifier.tapGestureFilter(onTap = { state.value = RotationStates.Original }),
-            text = "Reset",
-            style = textStyle
-        )
-        val transitionState = transition(
-            definition = definition,
-            toState = state.value
-        )
+        Button(
+            { state.value = RotationStates.Rotated }
+        ) {
+            Text(text = "Rotate 10 times")
+        }
+        Spacer(Modifier.height(10.dp))
+        Button(
+            { state.value = RotationStates.Original }
+        ) {
+            Text(text = "Reset")
+        }
+        Spacer(Modifier.height(10.dp))
+        val transition = updateTransition(state.value)
+        val rotation by transition.animateFloat(
+            {
+                if (it.initialState == RotationStates.Original) {
+                    repeatable(
+                        iterations = 10,
+                        animation = keyframes {
+                            durationMillis = 1000
+                            0f at 0 with LinearEasing
+                            360f at 1000
+                        }
+                    )
+                } else {
+                    tween(durationMillis = 300)
+                }
+            }
+        ) {
+            0f
+        }
         Canvas(Modifier.preferredSize(100.dp)) {
-            rotate(transitionState[rotation], Offset.Zero) {
+            rotate(rotation, Offset.Zero) {
                 drawRect(Color(0xFF00FF00))
             }
         }
@@ -76,29 +93,4 @@
 private enum class RotationStates {
     Original,
     Rotated
-}
-
-private val rotation = FloatPropKey()
-
-private val definition = transitionDefinition<RotationStates> {
-    state(RotationStates.Original) {
-        this[rotation] = 0f
-    }
-    state(RotationStates.Rotated) {
-        this[rotation] = 360f
-    }
-    transition(RotationStates.Original to RotationStates.Rotated) {
-        rotation using repeatable(
-            iterations = 10,
-            animation = tween(
-                easing = LinearEasing,
-                durationMillis = 1000
-            )
-        )
-    }
-    transition(RotationStates.Rotated to RotationStates.Original) {
-        rotation using tween(
-            durationMillis = 300
-        )
-    }
-}
+}
\ No newline at end of file
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SpringBackScrollingDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SpringBackScrollingDemo.kt
index 9d70c16..933882e 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SpringBackScrollingDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SpringBackScrollingDemo.kt
@@ -42,7 +42,6 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.gesture.util.VelocityTracker
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.DrawScope
@@ -54,7 +53,6 @@
 import kotlinx.coroutines.launch
 import kotlin.math.roundToInt
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 fun SpringBackScrollingDemo() {
     Column(Modifier.fillMaxHeight()) {
@@ -72,12 +70,12 @@
         val gesture = Modifier.pointerInput {
             coroutineScope {
                 while (true) {
-                    val pointerId = handlePointerInput {
+                    val pointerId = awaitPointerEventScope {
                         awaitFirstDown().id
                     }
                     val velocityTracker = VelocityTracker()
                     mutatorMutex.mutate(MutatePriority.UserInput) {
-                        handlePointerInput {
+                        awaitPointerEventScope {
                             horizontalDrag(pointerId) {
                                 scrollPosition += it.positionChange().x
                                 velocityTracker.addPosition(
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateAnimationWithInterruptionsDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateAnimationWithInterruptionsDemo.kt
deleted file mode 100644
index cd5d8f6..0000000
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateAnimationWithInterruptionsDemo.kt
+++ /dev/null
@@ -1,109 +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.animation.demos
-
-import android.os.Handler
-import android.os.Looper
-import androidx.compose.animation.ColorPropKey
-import androidx.compose.animation.core.FloatPropKey
-import androidx.compose.animation.core.TransitionState
-import androidx.compose.animation.core.spring
-import androidx.compose.animation.core.transitionDefinition
-import androidx.compose.animation.core.tween
-import androidx.compose.animation.transition
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.Color
-
-@Composable
-fun StateAnimationWithInterruptionsDemo() {
-    Box(Modifier.fillMaxSize()) {
-        ColorRect()
-    }
-}
-
-private val background = ColorPropKey()
-private val y = FloatPropKey()
-
-private enum class OverlayState {
-    Open,
-    Closed
-}
-
-private val definition = transitionDefinition<OverlayState> {
-    state(OverlayState.Open) {
-        this[background] = Color(red = 128, green = 128, blue = 128, alpha = 255)
-        this[y] = 1f // percentage
-    }
-    state(OverlayState.Closed) {
-        this[background] = Color(red = 188, green = 222, blue = 145, alpha = 255)
-        this[y] = 0f // percentage
-    }
-    // Apply this transition to all state changes (i.e. Open -> Closed and Closed -> Open)
-    transition {
-        background using tween(
-            durationMillis = 800
-        )
-        y using spring(
-            // Extremely low stiffness
-            stiffness = 40f
-        )
-    }
-}
-
-private val handler = Handler(Looper.getMainLooper())
-
-@Composable
-private fun ColorRect() {
-    var toState by mutableStateOf(OverlayState.Closed)
-    handler.postDelayed(
-        object : Runnable {
-            override fun run() {
-                if ((0..1).random() == 0) {
-                    toState = OverlayState.Open
-                } else {
-                    toState = OverlayState.Closed
-                }
-            }
-        },
-        (200..800).random().toLong()
-    )
-    val state = transition(definition = definition, toState = toState)
-    ColorRectState(state = state)
-}
-
-@Composable
-private fun ColorRectState(state: TransitionState) {
-    val color = state[background]
-    val scaleY = state[y]
-    Canvas(Modifier.fillMaxSize().background(color = color)) {
-        drawRect(
-            Color(alpha = 255, red = 255, green = 255, blue = 255),
-            topLeft = Offset(100f, 0f),
-            size = Size(size.width - 200f, scaleY * size.height)
-        )
-    }
-}
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateBasedRippleDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateBasedRippleDemo.kt
index 03c4319..917bcd8 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateBasedRippleDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateBasedRippleDemo.kt
@@ -16,26 +16,25 @@
 
 package androidx.compose.animation.demos
 
-import android.graphics.PointF
-import androidx.compose.animation.core.FloatPropKey
-import androidx.compose.animation.core.InterruptionHandling
-import androidx.compose.animation.core.TransitionDefinition
-import androidx.compose.animation.core.TransitionState
+import androidx.compose.animation.core.Transition
+import androidx.compose.animation.core.animateDp
+import androidx.compose.animation.core.animateFloat
 import androidx.compose.animation.core.keyframes
-import androidx.compose.animation.core.transitionDefinition
+import androidx.compose.animation.core.snap
+import androidx.compose.animation.core.updateTransition
 import androidx.compose.animation.core.tween
-import androidx.compose.animation.transition
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.gesture.pressIndicatorGestureFilter
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.unit.dp
 
 @Composable
@@ -47,37 +46,76 @@
 
 @Composable
 private fun RippleRect() {
-    val radius = with(AmbientDensity.current) { TargetRadius.toPx() }
-    val toState = remember { mutableStateOf(ButtonStatus.Initial) }
-    val rippleTransDef = remember { createTransDef(radius) }
+    var down by remember { mutableStateOf(Offset(0f, 0f)) }
+    var toState by remember { mutableStateOf(ButtonStatus.Initial) }
     val onPress: (Offset) -> Unit = { position ->
-        down.x = position.x
-        down.y = position.y
-        toState.value = ButtonStatus.Pressed
+        down = position
+        toState = ButtonStatus.Pressed
     }
 
     val onRelease: () -> Unit = {
-        toState.value = ButtonStatus.Released
+        toState = ButtonStatus.Released
     }
-    val state = transition(definition = rippleTransDef, toState = toState.value)
+    val transition = updateTransition(toState)
     RippleRectFromState(
-        Modifier.pressIndicatorGestureFilter(onStart = onPress, onStop = onRelease), state = state
+        Modifier.pressIndicatorGestureFilter(onStart = onPress, onStop = onRelease),
+        center = down,
+        transition = transition
     )
 }
 
 @Composable
-private fun RippleRectFromState(modifier: Modifier = Modifier, state: TransitionState) {
+private fun RippleRectFromState(
+    modifier: Modifier = Modifier,
+    center: Offset,
+    transition: Transition<ButtonStatus>
+) {
+    // TODO: Initial -> Pressed: Uninterruptible
+    // TODO: Pressed -> Released: Uninterruptible
+    // TODO: Auto transition to Initial
+    val alpha by transition.animateFloat(
+        transitionSpec = {
+            if (it.initialState == ButtonStatus.Initial && it.targetState == ButtonStatus.Pressed) {
+                keyframes {
+                    durationMillis = 225
+                    0f at 0 // optional
+                    0.2f at 75
+                    0.2f at 225 // optional
+                }
+            } else if (it.initialState == ButtonStatus.Pressed &&
+                it.targetState == ButtonStatus.Released
+            ) {
+                tween(durationMillis = 220)
+            } else {
+                snap()
+            }
+        }
+    ) {
+        if (it == ButtonStatus.Pressed) 0.2f else 0f
+    }
+
+    val radius by transition.animateDp(
+        transitionSpec = {
+            if (it.initialState == ButtonStatus.Initial && it.targetState == ButtonStatus.Pressed) {
+                tween(225)
+            } else {
+                snap()
+            }
+        }
+    ) {
+        if (it == ButtonStatus.Initial) TargetRadius * 0.3f else TargetRadius + 15.dp
+    }
+
     Canvas(modifier.fillMaxSize()) {
-        // TODO: file bug for when "down" is not a file level val, it's not memoized correctly
         drawCircle(
             Color(
-                alpha = (state[alpha] * 255).toInt(),
+                alpha = (alpha * 255).toInt(),
                 red = 0,
                 green = 235,
                 blue = 224
             ),
-            center = Offset(down.x, down.y),
-            radius = state[radius]
+            center = center,
+            radius = radius.toPx()
         )
     }
 }
@@ -88,49 +126,4 @@
     Released
 }
 
-private val TargetRadius = 200.dp
-
-private val down = PointF(0f, 0f)
-
-private val alpha = FloatPropKey()
-private val radius = FloatPropKey()
-
-private fun createTransDef(targetRadius: Float): TransitionDefinition<ButtonStatus> {
-    return transitionDefinition {
-        state(ButtonStatus.Initial) {
-            this[alpha] = 0f
-            this[radius] = targetRadius * 0.3f
-        }
-        state(ButtonStatus.Pressed) {
-            this[alpha] = 0.2f
-            this[radius] = targetRadius + 15f
-        }
-        state(ButtonStatus.Released) {
-            this[alpha] = 0f
-            this[radius] = targetRadius + 15f
-        }
-
-        // Grow the ripple
-        transition(ButtonStatus.Initial to ButtonStatus.Pressed) {
-            alpha using keyframes {
-                durationMillis = 225
-                0f at 0 // optional
-                0.2f at 75
-                0.2f at 225 // optional
-            }
-            radius using tween(durationMillis = 225)
-            interruptionHandling = InterruptionHandling.UNINTERRUPTIBLE
-        }
-
-        // Fade out the ripple
-        transition(ButtonStatus.Pressed to ButtonStatus.Released) {
-            alpha using tween(durationMillis = 200)
-            interruptionHandling = InterruptionHandling.UNINTERRUPTIBLE
-            // switch back to Initial to prepare for the next ripple cycle
-            nextState = ButtonStatus.Initial
-        }
-
-        // State switch without animation
-        snapTransition(ButtonStatus.Released to ButtonStatus.Initial)
-    }
-}
+private val TargetRadius = 200.dp
\ No newline at end of file
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SupendAnimationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SupendAnimationDemo.kt
index 4045fb6..070ac30 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SupendAnimationDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SupendAnimationDemo.kt
@@ -37,14 +37,14 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
+import kotlin.math.roundToInt
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 fun SuspendAnimationDemo() {
     var animStateX by remember {
@@ -59,7 +59,7 @@
         Modifier.fillMaxSize().background(Color(0xffb99aff)).pointerInput {
             coroutineScope {
                 while (true) {
-                    val offset = handlePointerInput {
+                    val offset = awaitPointerEventScope {
                         awaitFirstDown().current.position
                     }
                     val x = offset.x
@@ -86,7 +86,9 @@
     ) {
         Text("Tap anywhere", Modifier.align(Alignment.Center))
         Box(
-            Modifier.offset({ animStateX.value }, { animStateY.value }).size(40.dp)
+            Modifier
+                .offset { IntOffset(animStateX.value.roundToInt(), animStateY.value.roundToInt()) }
+                .size(40.dp)
                 .background(Color(0xff3c1361), CircleShape)
         )
     }
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SwipeToDismissDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SwipeToDismissDemo.kt
index 1e5ef0d..65750ea 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SwipeToDismissDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SwipeToDismissDemo.kt
@@ -42,25 +42,27 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.gesture.util.VelocityTracker
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.input.pointer.positionChange
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 import kotlin.math.abs
+import kotlin.math.roundToInt
 
 @Composable
 fun SwipeToDismissDemo() {
     Column {
         var index by remember { mutableStateOf(0) }
-        Box(Modifier.height(500.dp).fillMaxWidth()) {
+        val dismissState = remember { DismissState() }
+        Box(Modifier.height(300.dp).fillMaxWidth()) {
             Box(
-                Modifier.swipeToDismiss(index).align(Alignment.BottomCenter).size(300.dp)
+                Modifier.swipeToDismiss(dismissState).align(Alignment.BottomCenter).size(150.dp)
                     .background(pastelColors[index])
             )
         }
@@ -72,6 +74,8 @@
         Button(
             onClick = {
                 index = (index + 1) % pastelColors.size
+                dismissState.alpha = 1f
+                dismissState.offset = 0f
             },
             modifier = Modifier.align(Alignment.CenterHorizontally)
         ) {
@@ -80,33 +84,25 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
-private fun Modifier.swipeToDismiss(index: Int): Modifier = composed {
+private fun Modifier.swipeToDismiss(dismissState: DismissState): Modifier = composed {
     val mutatorMutex = remember { MutatorMutex() }
-    var alpha by remember { mutableStateOf(1f) }
-    var offset by remember { mutableStateOf(0f) }
 
-    remember(index) {
-        // Reset internal states if index has been updated
-        alpha = 1f
-        offset = 0f
-    }
     this.pointerInput {
         fun updateOffset(value: Float) {
-            offset = value
-            alpha = 1f - abs(offset / size.height)
+            dismissState.offset = value
+            dismissState.alpha = 1f - abs(dismissState.offset / size.height)
         }
         coroutineScope {
             while (true) {
-                val pointerId = handlePointerInput {
+                val pointerId = awaitPointerEventScope {
                     awaitFirstDown().id
                 }
                 val velocityTracker = VelocityTracker()
                 // Set a high priority on the mutatorMutex for gestures
                 mutatorMutex.mutate(MutatePriority.UserInput) {
-                    handlePointerInput {
+                    awaitPointerEventScope {
                         verticalDrag(pointerId) {
-                            updateOffset(offset + it.positionChange().y)
+                            updateOffset(dismissState.offset + it.positionChange().y)
                             velocityTracker.addPosition(
                                 it.current.uptime,
                                 it.current.position
@@ -120,9 +116,9 @@
                     // animation job.
                     mutatorMutex.mutate {
                         // Either fling out of the sight, or snap back
-                        val animationState = AnimationState(offset, velocity)
+                        val animationState = AnimationState(dismissState.offset, velocity)
                         val decay = AndroidFlingDecaySpec(this@pointerInput)
-                        if (decay.getTarget(offset, velocity) >= -size.height) {
+                        if (decay.getTarget(dismissState.offset, velocity) >= -size.height) {
                             // Not enough velocity to be dismissed
                             animationState.animateTo(0f) {
                                 updateOffset(value)
@@ -142,7 +138,14 @@
                 }
             }
         }
-    }.offset(y = { offset }).graphicsLayer(alpha = alpha)
+    }
+        .offset { IntOffset(0, dismissState.offset.roundToInt()) }
+        .graphicsLayer(alpha = dismissState.alpha)
+}
+
+private class DismissState {
+    var alpha by mutableStateOf(1f)
+    var offset by mutableStateOf(0f)
 }
 
 internal val pastelColors = listOf(
diff --git a/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/SingleValueAnimationTest.kt b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/SingleValueAnimationTest.kt
index 8586b3c..e1e0cff 100644
--- a/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/SingleValueAnimationTest.kt
+++ b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/SingleValueAnimationTest.kt
@@ -22,6 +22,7 @@
 import androidx.compose.animation.core.FloatSpringSpec
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.LinearOutSlowInEasing
+import androidx.compose.animation.core.VectorConverter
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.layout.Box
@@ -338,14 +339,14 @@
         var boundsValue = Bounds(0.dp, 0.dp, 0.dp, 0.dp)
 
         val specForFloat = FloatSpringSpec(visibilityThreshold = 0.01f)
-        val specForVector = FloatSpringSpec(visibilityThreshold = PxVisibilityThreshold)
-        val specForOffset = FloatSpringSpec(visibilityThreshold = PxVisibilityThreshold)
-        val specForBounds = FloatSpringSpec(visibilityThreshold = DpVisibilityThreshold)
+        val specForVector = FloatSpringSpec(visibilityThreshold = 0.5f)
+        val specForOffset = FloatSpringSpec(visibilityThreshold = 0.5f)
+        val specForBounds = FloatSpringSpec(visibilityThreshold = 0.1f)
 
         val content: @Composable (Boolean) -> Unit = { enabled ->
             vectorValue = animate(
                 if (enabled) AnimationVector(100f) else AnimationVector(0f),
-                visibilityThreshold = AnimationVector(PxVisibilityThreshold)
+                visibilityThreshold = AnimationVector(0.5f)
             )
 
             offsetValue = animate(
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
index 9b7b320..7772778 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
@@ -25,8 +25,8 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
index 132dfda..e870d7c 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
@@ -21,6 +21,7 @@
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.AnimationVector2D
 import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.VectorConverter
 import androidx.compose.animation.core.spring
 import androidx.compose.runtime.remember
 import androidx.compose.ui.layout.LayoutModifier
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
index 795d9a5..3f4b536 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
@@ -20,6 +20,7 @@
 import androidx.compose.animation.core.AnimationEndReason
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.AnimationVector2D
+import androidx.compose.animation.core.VectorConverter
 import androidx.compose.animation.core.spring
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/LegacyTransition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/LegacyTransition.kt
deleted file mode 100644
index fcfad1b..0000000
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/LegacyTransition.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.animation
-
-import androidx.compose.animation.core.AnimationClockObservable
-import androidx.compose.animation.core.TransitionDefinition
-import androidx.compose.animation.core.TransitionState
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.AmbientAnimationClock
-
-/**
- * __Deprecated:__ [Transition] has been deprecated. Please use [transition] instead.
- *
- * [Transition] composable creates a state-based transition using the animation configuration
- * defined in [TransitionDefinition]. This can be especially useful when animating multiple
- * values from a predefined set of values to another. For animating a single value, consider using
- * [animatedValue], [animatedFloat], [animatedColor] or the more light-weight [animate] APIs.
- *
- * [Transition] starts a new animation or changes the on-going animation when the [toState]
- * parameter is changed to a different value. It dutifully ensures that the animation will head
- * towards new [toState] regardless of what state (or in-between state) it’s currently in: If the
- * transition is not currently animating, having a new [toState] value will start a new animation,
- * otherwise the in-flight animation will correct course and animate towards the new [toState]
- * based on the interruption handling logic.
- *
- * [Transition] takes a transition definition, a target state and child composables.
- * These child composables will be receiving a [TransitionState] object as an argument, which
- * captures all the current values of the animation. Child composables should read the animation
- * values from the [TransitionState] object, and apply the value wherever necessary.
- *
- * @sample androidx.compose.animation.samples.TransitionSample
- *
- * @param definition Transition definition that defines states and transitions
- * @param toState New state to transition to
- * @param clock Optional animation clock that pulses animations when time changes. By default,
- *              the system uses a choreographer based clock read from the [AnimationClockAmbient].
- *              A custom implementation of the [AnimationClockObservable] (such as a
- *              [androidx.compose.animation.core.ManualAnimationClock]) can be supplied here if there’s a need to
- *              manually control the clock (for example in tests).
- * @param initState Optional initial state for the transition. When undefined, the initial state
- *                  will be set to the first [toState] seen in the transition.
- * @param onStateChangeFinished An optional listener to get notified when state change animation
- *                              has completed
- * @param children The children composables that will be animated
- *
- * @see [TransitionDefinition]
- */
-@Deprecated(
-    "Transition has been renamed to transition, which returns a TransitionState instead " +
-        "of passing it to children",
-    replaceWith = ReplaceWith(
-        "transition(definition, toState, clock, initState, null, onStateChangeFinished)",
-        "androidx.compose.animation.transition"
-    )
-)
-@Composable
-fun <T> Transition(
-    definition: TransitionDefinition<T>,
-    toState: T,
-    clock: AnimationClockObservable = AmbientAnimationClock.current,
-    initState: T = toState,
-    onStateChangeFinished: ((T) -> Unit)? = null,
-    children: @Composable (state: TransitionState) -> Unit
-) {
-    val state = transition(definition, toState, clock, initState, null, onStateChangeFinished)
-    children(state)
-}
\ No newline at end of file
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/PropertyKeys.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/PropertyKeys.kt
index bfc1b0d..12cc147 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/PropertyKeys.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/PropertyKeys.kt
@@ -204,47 +204,55 @@
 /**
  * A type converter that converts a [Rect] to a [AnimationVector4D], and vice versa.
  */
+@Deprecated("Rect.VectorConverter has been moved to animation-core library")
 val Rect.Companion.VectorConverter: TwoWayConverter<Rect, AnimationVector4D>
     get() = RectToVector
 
 /**
  * A type converter that converts a [Dp] to a [AnimationVector1D], and vice versa.
  */
+@Deprecated("Dp.VectorConverter has been moved to animation-core library")
 val Dp.Companion.VectorConverter: TwoWayConverter<Dp, AnimationVector1D>
     get() = DpToVector
 
 /**
  * A type converter that converts a [Position] to a [AnimationVector2D], and vice versa.
  */
+@Deprecated("Position.VectorConverter has been moved to animation-core library")
 val Position.Companion.VectorConverter: TwoWayConverter<Position, AnimationVector2D>
     get() = PositionToVector
 
 /**
  * A type converter that converts a [Size] to a [AnimationVector2D], and vice versa.
  */
+@Deprecated("Size.VectorConverter has been moved to animation-core library")
 val Size.Companion.VectorConverter: TwoWayConverter<Size, AnimationVector2D>
     get() = SizeToVector
 
 /**
  * A type converter that converts a [Bounds] to a [AnimationVector4D], and vice versa.
  */
+@Deprecated("Bounds.VectorConverter has been moved to animation-core library")
 val Bounds.Companion.VectorConverter: TwoWayConverter<Bounds, AnimationVector4D>
     get() = BoundsToVector
 
 /**
  * A type converter that converts a [Offset] to a [AnimationVector2D], and vice versa.
  */
+@Deprecated("Offset.VectorConverter has been moved to animation-core library")
 val Offset.Companion.VectorConverter: TwoWayConverter<Offset, AnimationVector2D>
     get() = OffsetToVector
 
 /**
  * A type converter that converts a [IntOffset] to a [AnimationVector2D], and vice versa.
  */
+@Deprecated("IntOffset.VectorConverter has been moved to animation-core library")
 val IntOffset.Companion.VectorConverter: TwoWayConverter<IntOffset, AnimationVector2D>
     get() = IntOffsetToVector
 
 /**
  * A type converter that converts a [IntSize] to a [AnimationVector2D], and vice versa.
  */
+@Deprecated("IntSize.VectorConverter has been moved to animation-core library")
 val IntSize.Companion.VectorConverter: TwoWayConverter<IntSize, AnimationVector2D>
     get() = IntSizeToVector
\ No newline at end of file
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
index 5fd5dab..84ae2d8 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
@@ -21,10 +21,10 @@
 import androidx.compose.animation.core.AnimationState
 import androidx.compose.animation.core.AnimationVector
 import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.AnimationVector4D
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.animation.core.TwoWayConverter
 import androidx.compose.animation.core.VectorConverter
+import androidx.compose.animation.core.VisibilityThreshold
 import androidx.compose.animation.core.animateTo
 import androidx.compose.animation.core.isFinished
 import androidx.compose.runtime.Composable
@@ -45,26 +45,6 @@
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.Position
-import androidx.compose.ui.unit.dp
-
-internal const val DpVisibilityThreshold = 0.1f
-internal const val PxVisibilityThreshold = 0.5f
-
-// Dp-based visibility threshold
-private val DpVisibilityThreshold4D = AnimationVector4D(
-    DpVisibilityThreshold,
-    DpVisibilityThreshold,
-    DpVisibilityThreshold,
-    DpVisibilityThreshold
-)
-
-// Px-based visibility threshold
-private val PxVisibilityThreshold4D = AnimationVector4D(
-    PxVisibilityThreshold,
-    PxVisibilityThreshold,
-    PxVisibilityThreshold,
-    PxVisibilityThreshold
-)
 
 private val defaultAnimation = SpringSpec<Float>()
 
@@ -159,7 +139,7 @@
 fun animate(
     target: Dp,
     animSpec: AnimationSpec<Dp> = remember {
-        SpringSpec(visibilityThreshold = DpVisibilityThreshold.dp)
+        SpringSpec(visibilityThreshold = Dp.VisibilityThreshold)
     },
     endListener: ((Dp) -> Unit)? = null
 ): Dp {
@@ -187,7 +167,7 @@
     target: Position,
     animSpec: AnimationSpec<Position> = remember {
         SpringSpec(
-            visibilityThreshold = Position(DpVisibilityThreshold.dp, DpVisibilityThreshold.dp)
+            visibilityThreshold = Position.VisibilityThreshold
         )
     },
     endListener: ((Position) -> Unit)? = null
@@ -217,7 +197,7 @@
 fun animate(
     target: Size,
     animSpec: AnimationSpec<Size> = remember {
-        SpringSpec(visibilityThreshold = Size(PxVisibilityThreshold, PxVisibilityThreshold))
+        SpringSpec(visibilityThreshold = Size.VisibilityThreshold)
     },
     endListener: ((Size) -> Unit)? = null
 ): Size {
@@ -244,10 +224,7 @@
 fun animate(
     target: Bounds,
     animSpec: AnimationSpec<Bounds> = remember {
-        SpringSpec(
-            visibilityThreshold = Bounds.VectorConverter.convertFromVector
-            (DpVisibilityThreshold4D)
-        )
+        SpringSpec(visibilityThreshold = Bounds.VisibilityThreshold)
     },
     endListener: ((Bounds) -> Unit)? = null
 ): Bounds {
@@ -278,7 +255,7 @@
 fun animate(
     target: Offset,
     animSpec: AnimationSpec<Offset> = remember {
-        SpringSpec(visibilityThreshold = Offset(PxVisibilityThreshold, PxVisibilityThreshold))
+        SpringSpec(visibilityThreshold = Offset.VisibilityThreshold)
     },
     endListener: ((Offset) -> Unit)? = null
 ): Offset {
@@ -307,10 +284,7 @@
 fun animate(
     target: Rect,
     animSpec: AnimationSpec<Rect> = remember {
-        SpringSpec(
-            visibilityThreshold =
-                Rect.VectorConverter.convertFromVector(PxVisibilityThreshold4D)
-        )
+        SpringSpec(visibilityThreshold = Rect.VisibilityThreshold)
     },
     endListener: ((Rect) -> Unit)? = null
 ): Rect {
@@ -364,7 +338,7 @@
 fun animate(
     target: IntOffset,
     animSpec: AnimationSpec<IntOffset> = remember {
-        SpringSpec(visibilityThreshold = IntOffset(1, 1))
+        SpringSpec(visibilityThreshold = IntOffset.VisibilityThreshold)
     },
     endListener: ((IntOffset) -> Unit)? = null
 ): IntOffset {
@@ -390,7 +364,7 @@
 fun animate(
     target: IntSize,
     animSpec: AnimationSpec<IntSize> = remember {
-        SpringSpec(visibilityThreshold = IntSize(1, 1))
+        SpringSpec(visibilityThreshold = IntSize.VisibilityThreshold)
     },
     endListener: ((IntSize) -> Unit)? = null
 ): IntSize {
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt
index cb8442f..7ae0bb2 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt
@@ -18,18 +18,28 @@
 
 import androidx.compose.animation.core.AnimationClockObservable
 import androidx.compose.animation.core.AnimationVector
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.animation.core.InternalAnimationApi
 import androidx.compose.animation.core.PropKey
+import androidx.compose.animation.core.Transition
 import androidx.compose.animation.core.TransitionAnimation
 import androidx.compose.animation.core.TransitionDefinition
 import androidx.compose.animation.core.TransitionState
+import androidx.compose.animation.core.animateValue
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.keyframes
+import androidx.compose.animation.core.repeatable
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.onCommit
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.util.annotation.VisibleForTesting
 
@@ -130,6 +140,7 @@
 ) : TransitionState {
 
     private var animationPulse by mutableStateOf(0L)
+
     @InternalAnimationApi
     val anim: TransitionAnimation<T> =
         TransitionAnimation(transitionDef, clock, initState, label).apply {
@@ -144,4 +155,42 @@
         val pulse = animationPulse
         return anim[propKey]
     }
-}
\ No newline at end of file
+}
+
+/**
+ * Creates a [Color] animation as a part of the given [Transition]. This means the lifecycle
+ * of this animation will be managed by the [Transition].
+ *
+ * [targetValueByState] is used as a mapping from a target state to the target value of this
+ * animation. [Transition] will be using this mapping to determine what value to target this
+ * animation towards. __Note__ that [targetValueByState] is a composable function. This means the
+ * mapping function could access states, ambient, themes, etc. If the targetValue changes outside
+ * of a [Transition] run (i.e. when the [Transition] already reached its targetState), the
+ * [Transition] will start running again to ensure this animation reaches its new target smoothly.
+ *
+ * An optional [transitionSpec] can be provided to specify (potentially different) animation for
+ * each pair of initialState and targetState. [FiniteAnimationSpec] includes any non-infinite
+ * animation, such as [tween], [spring], [keyframes] and even [repeatable], but not
+ * [infiniteRepeatable]. By default, [transitionSpec] uses a [spring] animation for all transition
+ * destinations.
+ *
+ * @return A [State] object, the value of which is updated by animation
+ *
+ * @see animateValue
+ * @see androidx.compose.animation.core.animateFloat
+ * @see androidx.compose.animation.core.Transition
+ * @see androidx.compose.animation.core.updateTransition
+ */
+@Composable
+inline fun <S> Transition<S>.animateColor(
+    noinline transitionSpec:
+        @Composable (states: Transition.States<S>) -> FiniteAnimationSpec<Color> = { spring() },
+    targetValueByState: @Composable (state: S) -> Color
+): State<Color> {
+    val colorSpace = targetValueByState(targetState).colorSpace
+    val typeConverter = remember(colorSpace) {
+        Color.VectorConverter(colorSpace)
+    }
+
+    return animateValue(typeConverter, transitionSpec, targetValueByState)
+}
diff --git a/compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt b/compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
index cf2131d..49a60a3 100644
--- a/compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
+++ b/compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
@@ -19,6 +19,7 @@
 import androidx.compose.animation.core.AnimationVector1D
 import androidx.compose.animation.core.AnimationVector2D
 import androidx.compose.animation.core.AnimationVector4D
+import androidx.compose.animation.core.VectorConverter
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
diff --git a/compose/compiler/compiler-hosted/integration-tests/lint-baseline.xml b/compose/compiler/compiler-hosted/integration-tests/lint-baseline.xml
deleted file mode 100644
index db82975..0000000
--- a/compose/compiler/compiler-hosted/integration-tests/lint-baseline.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.2.0-alpha15" client="gradle" variant="debug" version="4.2.0-alpha15">
-
-    <issue
-        id="IgnoreWithoutReason"
-        message="Test is ignored without giving any explanation"
-        errorLine1="    @Ignore"
-        errorLine2="    ~~~~~~~">
-        <location
-            file="src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralCodegenTests.kt"
-            line="41"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="IgnoreWithoutReason"
-        message="Test is ignored without giving any explanation"
-        errorLine1="    @Ignore"
-        errorLine2="    ~~~~~~~">
-        <location
-            file="src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralCodegenTests.kt"
-            line="63"
-            column="5"/>
-    </issue>
-
-</issues>
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenSignatureTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenSignatureTest.kt
index 7e58ce0..c21c5e6 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenSignatureTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenSignatureTest.kt
@@ -126,7 +126,6 @@
                 fun makeComposer(): Composer<*> {
                     val container = LinearLayout(__context!!)
                     return Composer(
-                        SlotTable(),
                         UiApplier(container),
                         Recomposer.current()
                     )
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
index de2790c..6dcaf71 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
@@ -68,11 +68,10 @@
 abstract class ComposeIrTransformTest : AbstractIrTransformTest() {
     open val liveLiteralsEnabled get() = false
     open val sourceInformationEnabled get() = true
-    open val intrinsicRememberEnabled get() = false
     private val extension = ComposeIrGenerationExtension(
         liveLiteralsEnabled,
         sourceInformationEnabled,
-        intrinsicRememberEnabled
+        intrinsicRememberEnabled = true
     )
     // Some tests require the plugin context in order to perform assertions, for example, a
     // context is required to determine the stability of a type using the StabilityInferencer.
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractLoweringTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractLoweringTests.kt
index 0cd4d99..b0eed19 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractLoweringTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractLoweringTests.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
-import android.view.View
 import androidx.compose.runtime.Composer
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.snapshots.Snapshot
@@ -42,9 +41,6 @@
         )
     }
 
-    @Suppress("UNCHECKED_CAST")
-    fun View.getComposedSet(tagId: Int): Set<String>? = getTag(tagId) as? Set<String>
-
     @OptIn(ExperimentalComposeApi::class)
     protected fun execute(block: () -> Unit) {
         val scheduler = RuntimeEnvironment.getMasterScheduler()
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/CodegenMetadataTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/CodegenMetadataTests.kt
new file mode 100644
index 0000000..bacb9b6
--- /dev/null
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/CodegenMetadataTests.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.config.CompilerConfiguration
+import org.junit.Test
+
+class CodegenMetadataTests : AbstractLoweringTests() {
+
+    override fun updateConfiguration(configuration: CompilerConfiguration) {
+        super.updateConfiguration(configuration)
+        configuration.put(ComposeConfiguration.LIVE_LITERALS_ENABLED_KEY, true)
+    }
+
+    @Test
+    fun testBasicFunctionality(): Unit = ensureSetup {
+        val className = "Test_${uniqueNumber++}"
+        val fileName = "$className.kt"
+        val loader = classLoader(
+            """
+            import kotlin.reflect.full.primaryConstructor
+            import kotlin.reflect.jvm.isAccessible
+            data class MyClass(val someBoolean: Boolean? = false)
+            object Main { @JvmStatic fun main() { MyClass::class.java.kotlin.primaryConstructor!!.isAccessible = true } }
+            """,
+            fileName,
+            true
+        )
+        val main = loader.loadClass("Main").methods.single { it.name == "main" }
+        main.invoke(null)
+    }
+}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallLoweringTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallLoweringTests.kt
index d4c4674..9a43019 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallLoweringTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallLoweringTests.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
+import android.view.View
 import android.widget.Button
 import android.widget.LinearLayout
 import android.widget.TextView
@@ -2728,6 +2729,9 @@
     }
 }
 
+@Suppress("UNCHECKED_CAST")
+fun View.getComposedSet(tagId: Int): Set<String>? = getTag(tagId) as? Set<String>
+
 private val noParameters = { emptyMap<String, String>() }
 
 private inline fun <reified T : PsiElement> PsiElement.parentOfType(): T? = parentOfType(T::class)
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralCodegenTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralCodegenTests.kt
index 800bd34..9e9ab11 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralCodegenTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralCodegenTests.kt
@@ -38,7 +38,7 @@
         configuration.put(ComposeConfiguration.LIVE_LITERALS_ENABLED_KEY, true)
     }
 
-    @Ignore
+    @Ignore("Live literals are currently disabled by default")
     @Test
     fun testBasicFunctionality(): Unit = ensureSetup {
         compose(
@@ -60,7 +60,7 @@
         }
     }
 
-    @Ignore
+    @Ignore("Live literals are currently disabled by default")
     @Test
     fun testObjectFieldsLoweredToStaticFields(): Unit = ensureSetup {
         validateBytecode(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
index 524d05e..161a44a 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
@@ -19,7 +19,6 @@
 import org.junit.Test
 
 class RememberIntrinsicTransformTests : ComposeIrTransformTest() {
-    override val intrinsicRememberEnabled: Boolean get() = true
     private fun comparisonPropagation(
         unchecked: String,
         checked: String,
@@ -42,6 +41,214 @@
     )
 
     @Test
+    fun testElidedRememberInsideIfDeoptsRememberAfterIf(): Unit = comparisonPropagation(
+        "",
+        """
+            import androidx.compose.runtime.ComposableContract
+
+            @Composable
+            @ComposableContract(restartable = false)
+            fun app(x: Boolean) {
+                val a = if (x) { remember { 1 } } else { 2 }
+                val b = remember { 2 }
+            }
+        """,
+        """
+            @Composable
+            @ComposableContract(restartable = false)
+            fun app(x: Boolean, %composer: Composer<*>?, %changed: Int) {
+              %composer.startReplaceableGroup(<>, "C(app)<rememb...>:Test.kt")
+              val a = if (x) {
+                %composer.startReplaceableGroup(<>)
+                val tmp0_group = %composer.cache(false) {
+                  val tmp0_return = 1
+                  tmp0_return
+                }
+                %composer.endReplaceableGroup()
+                tmp0_group
+              } else {
+                %composer.startReplaceableGroup(<>)
+                %composer.endReplaceableGroup()
+                2
+              }
+              val b = remember({
+                val tmp0_return = 2
+                tmp0_return
+              }, %composer, 0)
+              %composer.endReplaceableGroup()
+            }
+        """
+    )
+
+    @Test
+    fun testMultipleParamInputs(): Unit = comparisonPropagation(
+        """
+        """,
+        """
+            @Composable
+            fun <T> loadResourceInternal(
+                key: String,
+                pendingResource: T? = null,
+                failedResource: T? = null
+            ): Boolean {
+                val deferred = remember(key, pendingResource, failedResource) {
+                    123
+                }
+                return deferred > 10
+            }
+        """,
+        """
+            @Composable
+            fun <T> loadResourceInternal(key: String, pendingResource: T?, failedResource: T?, %composer: Composer<*>?, %changed: Int, %default: Int): Boolean {
+              %composer.startReplaceableGroup(<>, "C(loadResourceInternal)P(1,2):Test.kt")
+              val pendingResource = if (%default and 0b0010 !== 0) null else pendingResource
+              val failedResource = if (%default and 0b0100 !== 0) null else failedResource
+              val deferred = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(key) || %changed and 0b0110 === 0b0100 or %changed and 0b01110000 xor 0b00110000 > 32 && %composer.changed(pendingResource) || %changed and 0b00110000 === 0b00100000 or %changed and 0b001110000000 xor 0b000110000000 > 256 && %composer.changed(failedResource) || %changed and 0b000110000000 === 0b000100000000) {
+                val tmp0_return = 123
+                tmp0_return
+              }
+              val tmp0 = deferred > 10
+              %composer.endReplaceableGroup()
+              return tmp0
+            }
+        """
+    )
+
+    @Test
+    fun testRestartableParameterInputsStableUnstableUncertain(): Unit = comparisonPropagation(
+        """
+            class KnownStable
+            class KnownUnstable(var x: Int)
+            interface Uncertain
+        """,
+        """
+            @Composable
+            fun test1(x: KnownStable) {
+                remember(x) { 1 }
+            }
+            @Composable
+            fun test2(x: KnownUnstable) {
+                remember(x) { 1 }
+            }
+            @Composable
+            fun test3(x: Uncertain) {
+                remember(x) { 1 }
+            }
+        """,
+        """
+            @Composable
+            fun test1(x: KnownStable, %composer: Composer<*>?, %changed: Int) {
+              %composer.startRestartGroup(<>, "C(test1):Test.kt")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                %composer.cache(%dirty and 0b1110 === 0b0100) {
+                  val tmp0_return = 1
+                  tmp0_return
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %force: Int ->
+                test1(x, %composer, %changed or 0b0001)
+              }
+            }
+            @Composable
+            fun test2(x: KnownUnstable, %composer: Composer<*>?, %changed: Int) {
+              %composer.startRestartGroup(<>, "C(test2):Test.kt")
+              %composer.cache(%composer.changed(x)) {
+                val tmp0_return = 1
+                tmp0_return
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %force: Int ->
+                test2(x, %composer, %changed or 0b0001)
+              }
+            }
+            @Composable
+            fun test3(x: Uncertain, %composer: Composer<*>?, %changed: Int) {
+              %composer.startRestartGroup(<>, "C(test3):Test.kt")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                %composer.cache(%dirty and 0b1110 === 0b0100 || %dirty and 0b1000 !== 0 && %composer.changed(x)) {
+                  val tmp0_return = 1
+                  tmp0_return
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %force: Int ->
+                test3(x, %composer, %changed or 0b0001)
+              }
+            }
+        """
+    )
+
+    @Test
+    fun testNonRestartableParameterInputsStableUnstableUncertain(): Unit = comparisonPropagation(
+        """
+            class KnownStable
+            class KnownUnstable(var x: Int)
+            interface Uncertain
+        """,
+        """
+            import androidx.compose.runtime.ComposableContract
+
+            @Composable
+            @ComposableContract(restartable=false)
+            fun test1(x: KnownStable) {
+                remember(x) { 1 }
+            }
+            @Composable
+            @ComposableContract(restartable=false)
+            fun test2(x: KnownUnstable) {
+                remember(x) { 1 }
+            }
+            @Composable
+            @ComposableContract(restartable=false)
+            fun test3(x: Uncertain) {
+                remember(x) { 1 }
+            }
+        """,
+        """
+            @Composable
+            @ComposableContract(restartable = false)
+            fun test1(x: KnownStable, %composer: Composer<*>?, %changed: Int) {
+              %composer.startReplaceableGroup(<>, "C(test1):Test.kt")
+              %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(x) || %changed and 0b0110 === 0b0100) {
+                val tmp0_return = 1
+                tmp0_return
+              }
+              %composer.endReplaceableGroup()
+            }
+            @Composable
+            @ComposableContract(restartable = false)
+            fun test2(x: KnownUnstable, %composer: Composer<*>?, %changed: Int) {
+              %composer.startReplaceableGroup(<>, "C(test2):Test.kt")
+              %composer.cache(%composer.changed(x)) {
+                val tmp0_return = 1
+                tmp0_return
+              }
+              %composer.endReplaceableGroup()
+            }
+            @Composable
+            @ComposableContract(restartable = false)
+            fun test3(x: Uncertain, %composer: Composer<*>?, %changed: Int) {
+              %composer.startReplaceableGroup(<>, "C(test3):Test.kt")
+              %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(x) || %changed and 0b0110 === 0b0100) {
+                val tmp0_return = 1
+                tmp0_return
+              }
+              %composer.endReplaceableGroup()
+            }
+        """
+    )
+
+    @Test
     fun testPassedArgs(): Unit = comparisonPropagation(
         """
             class Foo(val a: Int, val b: Int)
@@ -54,7 +261,7 @@
             @Composable
             fun rememberFoo(a: Int, b: Int, %composer: Composer<*>?, %changed: Int): Foo {
               %composer.startReplaceableGroup(<>, "C(rememberFoo):Test.kt")
-              val tmp0 = %composer.cache(%changed and 0b0110 === 0 && %composer.changed(a) || %changed and 0b1110 === 0b0100 or %changed and 0b00110000 === 0 && %composer.changed(b) || %changed and 0b01110000 === 0b00100000) {
+              val tmp0 = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(a) || %changed and 0b0110 === 0b0100 or %changed and 0b01110000 xor 0b00110000 > 32 && %composer.changed(b) || %changed and 0b00110000 === 0b00100000) {
                 val tmp0_return = Foo(a, b)
                 tmp0_return
               }
@@ -676,7 +883,7 @@
             fun Test(a: Int, %composer: Composer<*>?, %changed: Int): Foo {
               %composer.startReplaceableGroup(<>, "C(Test):Test.kt")
               val b = someInt()
-              val tmp0 = %composer.cache(%changed and 0b0110 === 0 && %composer.changed(a) || %changed and 0b1110 === 0b0100 or %composer.changed(b)) {
+              val tmp0 = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(a) || %changed and 0b0110 === 0b0100 or %composer.changed(b)) {
                 val tmp0_return = Foo(a, b)
                 tmp0_return
               }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
index bfb6031..1ad22c2 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
@@ -37,7 +37,7 @@
 class ComposeIrGenerationExtension(
     @Suppress("unused") private val liveLiteralsEnabled: Boolean = false,
     private val sourceInformationEnabled: Boolean = true,
-    private val intrinsicRememberEnabled: Boolean = false,
+    private val intrinsicRememberEnabled: Boolean = true,
 ) : IrGenerationExtension {
     @OptIn(ObsoleteDescriptorBasedAPI::class)
     override fun generate(
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 b55b131..c87c29d 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
@@ -27,7 +27,10 @@
 import org.jetbrains.kotlin.config.CompilerConfiguration
 import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
 import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
+import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
+import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
 import org.jetbrains.kotlin.config.CompilerConfigurationKey
+import org.jetbrains.kotlin.config.KotlinCompilerVersion
 import org.jetbrains.kotlin.extensions.internal.TypeResolutionInterceptor
 import org.jetbrains.kotlin.serialization.DescriptorSerializer
 
@@ -38,6 +41,8 @@
         CompilerConfigurationKey<Boolean>("Include source information in generated code")
     val INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY =
         CompilerConfigurationKey<Boolean>("Enable optimization to treat remember as an intrinsic")
+    val SUPPRESS_KOTLIN_VERSION_COMPATIBILITY_CHECK =
+        CompilerConfigurationKey<Boolean>("Suppress Kotlin version compatibility check")
 }
 
 class ComposeCommandLineProcessor : CommandLineProcessor {
@@ -64,13 +69,21 @@
             required = false,
             allowMultipleOccurrences = false
         )
+        val SUPPRESS_KOTLIN_VERSION_CHECK_ENABLED_OPTION = CliOption(
+            "suppressKotlinVersionCompatibilityCheck",
+            "<true|false>",
+            "Suppress Kotlin version compatibility check",
+            required = false,
+            allowMultipleOccurrences = false
+        )
     }
 
     override val pluginId = PLUGIN_ID
     override val pluginOptions = listOf(
         LIVE_LITERALS_ENABLED_OPTION,
         SOURCE_INFORMATION_ENABLED_OPTION,
-        INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_OPTION
+        INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_OPTION,
+        SUPPRESS_KOTLIN_VERSION_CHECK_ENABLED_OPTION
     )
 
     override fun processOption(
@@ -90,6 +103,10 @@
             ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY,
             value == "true"
         )
+        SUPPRESS_KOTLIN_VERSION_CHECK_ENABLED_OPTION -> configuration.put(
+            ComposeConfiguration.SUPPRESS_KOTLIN_VERSION_COMPATIBILITY_CHECK,
+            value == "true"
+        )
         else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
     }
 }
@@ -112,6 +129,26 @@
             project: Project,
             configuration: CompilerConfiguration
         ) {
+            val KOTLIN_VERSION_EXPECTATION = "1.4.21"
+            KotlinCompilerVersion.getVersion()?.let { version ->
+                val suppressKotlinVersionCheck = configuration.get(
+                    ComposeConfiguration.SUPPRESS_KOTLIN_VERSION_COMPATIBILITY_CHECK,
+                    false
+                )
+                if (!suppressKotlinVersionCheck && version != KOTLIN_VERSION_EXPECTATION) {
+                    val msgCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
+                    msgCollector?.report(
+                        CompilerMessageSeverity.ERROR,
+                        "This version (${VersionChecker.compilerVersion}) of the Compose" +
+                            " Compiler requires Kotlin version $KOTLIN_VERSION_EXPECTATION but" +
+                            " you appear to be using Kotlin version $version which is not known" +
+                            " to be compatible.  Please fix your configuration (or" +
+                            " `suppressKotlinVersionCompatibilityCheck` but don't say I didn't" +
+                            " warn you!)."
+                    )
+                }
+            }
+
             val liveLiteralsEnabled = configuration.get(
                 ComposeConfiguration.LIVE_LITERALS_ENABLED_KEY,
                 false
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
index b12654e..8f28e70 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
@@ -23,31 +23,34 @@
 
 class VersionChecker(val context: IrPluginContext) {
 
-    /**
-     * A table of version ints to version strings. This should be updated every time
-     * ComposeVersion.kt is updated.
-     */
-    private val versionTable = mapOf(
-        1600 to "0.1.0-dev16",
-        1700 to "1.0.0-alpha06",
-        1800 to "1.0.0-alpha07",
-        1900 to "1.0.0-alpha08",
-        2000 to "1.0.0-alpha09"
-    )
+    companion object {
+        /**
+         * A table of version ints to version strings. This should be updated every time
+         * ComposeVersion.kt is updated.
+         */
+        private val versionTable = mapOf(
+            1600 to "0.1.0-dev16",
+            1700 to "1.0.0-alpha06",
+            1800 to "1.0.0-alpha07",
+            1900 to "1.0.0-alpha08",
+            2000 to "1.0.0-alpha09",
+            2100 to "1.0.0-alpha10"
+        )
 
-    /**
-     * The minimum version int that this compiler is guaranteed to be compatible with. Typically
-     * this will match the version int that is in ComposeVersion.kt in the runtime.
-     */
-    private val minimumRuntimeVersionInt: Int = 2000
+        /**
+         * The minimum version int that this compiler is guaranteed to be compatible with. Typically
+         * this will match the version int that is in ComposeVersion.kt in the runtime.
+         */
+        private val minimumRuntimeVersionInt: Int = 2100
 
-    /**
-     * The maven version string of this compiler. This string should be updated before/after every
-     * release.
-     */
-    private val compilerVersion: String = "1.0.0-alpha09"
-    private val minimumRuntimeVersion: String
-        get() = versionTable[minimumRuntimeVersionInt] ?: "unknown"
+        /**
+         * The maven version string of this compiler. This string should be updated before/after every
+         * release.
+         */
+        val compilerVersion: String = "1.0.0-alpha10"
+        private val minimumRuntimeVersion: String
+            get() = versionTable[minimumRuntimeVersionInt] ?: "unknown"
+    }
 
     fun check() {
         val versionClass = context.referenceClass(ComposeFqNames.ComposeVersion)
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
index 1e6f5fe..d852691 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
@@ -117,6 +117,7 @@
 import org.jetbrains.kotlin.ir.types.IrSimpleType
 import org.jetbrains.kotlin.ir.types.IrType
 import org.jetbrains.kotlin.ir.types.classOrNull
+import org.jetbrains.kotlin.ir.types.classifierOrFail
 import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
 import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
 import org.jetbrains.kotlin.ir.types.isNullable
@@ -720,6 +721,19 @@
         )
     }
 
+    protected fun irGreater(lhs: IrExpression, rhs: IrExpression): IrCallImpl {
+        val int = context.irBuiltIns.intType
+        val gt = context.irBuiltIns.greaterFunByOperandType[int.classifierOrFail]
+        return irCall(
+            gt!!,
+            IrStatementOrigin.GT,
+            null,
+            null,
+            lhs,
+            rhs
+        )
+    }
+
     protected fun irReturn(
         target: IrReturnTargetSymbol,
         value: IrExpression,
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
index f38d76b..6925da4 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
@@ -252,6 +252,7 @@
 interface IrChangedBitMaskValue {
     fun irLowBit(): IrExpression
     fun irIsolateBitsAtSlot(slot: Int, includeStableBit: Boolean): IrExpression
+    fun irSlotAnd(slot: Int, bits: Int): IrExpression
     fun irHasDifferences(): IrExpression
     fun irCopyToTemporary(
         nameHint: String? = null,
@@ -2163,16 +2164,21 @@
 
     private fun encounteredComposableCall(withGroups: Boolean) {
         var scope: Scope? = currentScope
+        // it is important that we only report "withGroups: false" for the _nearest_ scope, and
+        // every scope above that it effectively means there was a group even if it is false
+        var groups = withGroups
         loop@ while (scope != null) {
             when (scope) {
                 is Scope.FunctionScope -> {
-                    scope.recordComposableCall(withGroups)
+                    scope.recordComposableCall(groups)
+                    groups = true
                     if (!scope.isInlinedLambda) {
                         break@loop
                     }
                 }
                 is Scope.BlockScope -> {
-                    scope.recordComposableCall(withGroups)
+                    scope.recordComposableCall(groups)
+                    groups = true
                 }
                 is Scope.ClassScope -> {
                     break@loop
@@ -2684,9 +2690,11 @@
 
         return when {
             meta.isStatic -> irConst(false)
-            meta.isCertain && param is IrChangedBitMaskVariable -> {
-                // if it's a dirty flag then we know that the value is now CERTAIN,
-                // thus we can avoid calling changed all together
+            meta.isCertain &&
+                meta.stability.knownStable() &&
+                param is IrChangedBitMaskVariable -> {
+                // if it's a dirty flag, and the parameter is _guaranteed_ to be stable, then we
+                // know that the value is now CERTAIN, thus we can avoid calling changed completely
                 //
                 // invalid = invalid or (mask == different)
                 irEqual(
@@ -2694,30 +2702,55 @@
                     irConst(ParamState.Different.bitsForSlot(meta.maskSlot))
                 )
             }
-            meta.isCertain && param != null -> {
-                // if it's a changed flag then uncertain is a possible value. If it is uncertain,
-                // then we need to call changed. If it is uncertain here it will _always_ be
-                // uncertain here, so this is safe. If it is not uncertain, we can just check to
-                // see if its different
-                // TODO(lmr): IMPORTANT QUESTION - is unstable + something other than uncertain
-                //  possible?
+            meta.isCertain &&
+                !meta.stability.knownUnstable() &&
+                param is IrChangedBitMaskVariable -> {
+                // if it's a dirty flag, and the parameter might be stable, then we only check
+                // changed if the value is unstable, otherwise we can just check to see if the mask
+                // is different
                 //
-                //
-                //     invalid = invalid or ((mask == uncertain && changed()) || mask == different)
+                // invalid = invalid or (stable && mask == different || unstable && changed)
+
+                val maskIsStableAndDifferent = irEqual(
+                    param.irIsolateBitsAtSlot(meta.maskSlot, includeStableBit = true),
+                    irConst(ParamState.Different.bitsForSlot(meta.maskSlot))
+                )
+                val stableBits = param.irSlotAnd(meta.maskSlot, StabilityBits.UNSTABLE.bits)
+                val maskIsUnstableAndChanged = irAndAnd(
+                    irNotEqual(stableBits, irConst(0)),
+                    irChanged(arg)
+                )
+                irOrOr(
+                    maskIsStableAndDifferent,
+                    maskIsUnstableAndChanged
+                )
+            }
+            meta.isCertain &&
+                !meta.stability.knownUnstable() &&
+                param != null -> {
+                // if it's a changed flag then uncertain is a possible value. If it is uncertain
+                // OR unstable, then we need to call changed. If it is uncertain or unstable here
+                // it will _always_ be uncertain or unstable here, so this is safe. If it is not
+                // uncertain or unstable, we can just check to see if its different
+
+                //     unstableOrUncertain = mask xor 011 > 010
+                //     invalid = invalid or ((unstableOrUncertain && changed()) || mask == different)
+
+                val maskIsUnstableOrUncertain =
+                    irGreater(
+                        irXor(
+                            param.irIsolateBitsAtSlot(meta.maskSlot, includeStableBit = true),
+                            irConst(bitsForSlot(0b011, meta.maskSlot))
+                        ),
+                        irConst(bitsForSlot(0b010, meta.maskSlot))
+                    )
                 irOrOr(
                     irAndAnd(
-                        irEqual(
-                            // we do NOT include the stable bit here because we want to capture
-                            // both of the cases where the type is stable and unstable.
-                            param.irIsolateBitsAtSlot(meta.maskSlot, includeStableBit = false),
-                            // NOTE: this is always "0", but i'm writing it out fully here to
-                            // just make the code more clear
-                            irConst(ParamState.Uncertain.bitsForSlot(meta.maskSlot))
-                        ),
+                        maskIsUnstableOrUncertain,
                         irChanged(arg)
                     ),
                     irEqual(
-                        param.irIsolateBitsAtSlot(meta.maskSlot, includeStableBit = true),
+                        param.irIsolateBitsAtSlot(meta.maskSlot, includeStableBit = false),
                         irConst(ParamState.Different.bitsForSlot(meta.maskSlot))
                     )
                 )
@@ -3702,6 +3735,14 @@
             )
         }
 
+        override fun irSlotAnd(slot: Int, bits: Int): IrExpression {
+            // %changed and 0b11
+            return irAnd(
+                irGet(params[paramIndexForSlot(slot)]),
+                irBitsForSlot(bits, slot)
+            )
+        }
+
         override fun irHasDifferences(): IrExpression {
             if (count == 0) {
                 // for 0 slots (no params), we can create a shortcut expression of just checking the
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
index d45ddd1..4bdb3e6 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
@@ -88,6 +88,10 @@
         return super.visitFunction(declaration).also { it.copyMetadataFrom(declaration) }
     }
 
+    override fun visitConstructor(declaration: IrConstructor): IrConstructor {
+        return super.visitConstructor(declaration).also { it.copyMetadataFrom(declaration) }
+    }
+
     override fun visitSimpleFunction(declaration: IrSimpleFunction): IrSimpleFunction {
         return super.visitSimpleFunction(declaration).also {
             it.correspondingPropertySymbol = declaration.correspondingPropertySymbol
@@ -284,6 +288,7 @@
             is IrPropertyImpl -> metadata = owner.metadata
             is IrFunction -> metadata = owner.metadata
             is IrClassImpl -> metadata = owner.metadata
+            else -> throw Error("Unknown type: $this")
         }
     }
 
diff --git a/compose/desktop/desktop/build.gradle b/compose/desktop/desktop/build.gradle
index 5a631af..328931fc 100644
--- a/compose/desktop/desktop/build.gradle
+++ b/compose/desktop/desktop/build.gradle
@@ -21,6 +21,7 @@
 import androidx.build.SupportConfigKt
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
+import static androidx.build.AndroidXPlugin.BUILD_ON_SERVER_TASK
 import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
@@ -124,6 +125,7 @@
     }
 }
 
-rootProject.tasks.getByName("buildOnServer").configure {
-    dependsOn(":compose:desktop:desktop:jar")
+def projectPath = project.path
+rootProject.tasks.named(BUILD_ON_SERVER_TASK).configure {
+    dependsOn("$projectPath:jvmJar")
 }
\ No newline at end of file
diff --git a/compose/desktop/desktop/samples/build.gradle b/compose/desktop/desktop/samples/build.gradle
index 12c04c9..f81a739 100644
--- a/compose/desktop/desktop/samples/build.gradle
+++ b/compose/desktop/desktop/samples/build.gradle
@@ -18,6 +18,7 @@
 
 import androidx.build.SupportConfigKt
 
+import static androidx.build.AndroidXPlugin.BUILD_ON_SERVER_TASK
 import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
@@ -91,3 +92,8 @@
 task run {
     dependsOn("run1")
 }
+
+def projectPath = project.path
+rootProject.tasks.named(BUILD_ON_SERVER_TASK).configure {
+    dependsOn("$projectPath:jvmJar")
+}
\ No newline at end of file
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 23699a4..fb54ac1 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
@@ -46,7 +46,7 @@
 import androidx.compose.foundation.text.appendInlineContent
 import androidx.compose.material.BottomAppBar
 import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants
+import androidx.compose.material.ButtonDefaults
 import androidx.compose.material.Checkbox
 import androidx.compose.material.CircularProgressIndicator
 import androidx.compose.material.ExtendedFloatingActionButton
@@ -72,7 +72,6 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shadow
 import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.input.key.plus
 import androidx.compose.ui.input.key.shortcuts
@@ -133,7 +132,7 @@
                     IconButton(
                         onClick = {}
                     ) {
-                        Icon(Icons.Filled.Menu, Modifier.size(ButtonConstants.DefaultIconSize))
+                        Icon(Icons.Filled.Menu, Modifier.size(ButtonDefaults.IconSize))
                     }
                 }
             },
@@ -158,7 +157,6 @@
     )
 }
 
-@OptIn(ExperimentalKeyInput::class)
 @Composable
 private fun ScrollableContent(scrollState: ScrollState) {
     val amount = remember { mutableStateOf(0) }
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.kt
index 163625a..db9c668 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.kt
@@ -21,25 +21,25 @@
 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.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredSize
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.AlertDialog
+import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
+import androidx.compose.material.Checkbox
 import androidx.compose.material.DropdownMenu
 import androidx.compose.material.DropdownMenuItem
-import androidx.compose.material.Text
-import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants
-import androidx.compose.material.Checkbox
 import androidx.compose.material.RadioButton
 import androidx.compose.material.Surface
+import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
@@ -47,10 +47,10 @@
 import androidx.compose.runtime.onDispose
 import androidx.compose.runtime.remember
 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.input.pointer.pointerMoveFilter
-import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
@@ -248,7 +248,11 @@
                 TextBox(text = "Alert Dialog")
             },
             text = {
+                println("Ambient value is ${AmbientTest.current}.")
                 TextBox(text = "Increment amount?")
+                onDispose {
+                    println("onDispose inside AlertDialog is called.")
+                }
             },
             shape = RoundedCornerShape(0.dp),
             backgroundColor = Color(70, 70, 70),
@@ -269,7 +273,11 @@
                 isFocusable = true,
                 onDismissRequest = onDismiss
             ) {
+                println("Ambient value is ${AmbientTest.current}.")
                 PopupContent(onDismiss)
+                onDispose {
+                    println("onDispose inside Popup is called.")
+                }
             }
         }
     }
@@ -317,7 +325,7 @@
     val buttonHover = remember { mutableStateOf(false) }
     Button(
         onClick = onClick,
-        colors = ButtonConstants.defaultButtonColors(
+        colors = ButtonDefaults.buttonColors(
             backgroundColor =
                 if (buttonHover.value)
                     Color(color.red / 1.3f, color.green / 1.3f, color.blue / 1.3f)
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.kt
index d5aa93c..4269d43 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.kt
@@ -18,6 +18,7 @@
 import androidx.compose.desktop.AppManager
 import androidx.compose.desktop.AppWindow
 import androidx.compose.desktop.WindowEvents
+import androidx.compose.runtime.Providers
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.window.Menu
@@ -75,7 +76,11 @@
             )
         )
     ).show {
-        content()
+        Providers(
+            AmbientTest provides 42
+        ) {
+            content()
+        }
     }
 }
 
diff --git a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/TestAmbient.kt
similarity index 80%
copy from compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt
copy to compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/TestAmbient.kt
index 3de9f0d..9f29f4b 100644
--- a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/TestAmbient.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.util
+package androidx.compose.desktop.examples.popupexample
 
-actual fun Float.toStringAsFixed(digits: Int): String = String.format("%.${digits}f", this)
+import androidx.compose.runtime.staticAmbientOf
+
+val AmbientTest = staticAmbientOf<Int>()
\ No newline at end of file
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt
index fb477e1..bfb6073 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt
@@ -22,31 +22,31 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.Text
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.Button
 import androidx.compose.material.Surface
+import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.Alignment
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
 import java.awt.BorderLayout
 import java.awt.Dimension
 import java.awt.event.ActionEvent
 import java.awt.event.ActionListener
-import javax.swing.JFrame
 import javax.swing.JButton
+import javax.swing.JFrame
 import javax.swing.WindowConstants
 
 val northClicks = mutableStateOf(0)
diff --git a/compose/foundation/foundation-layout/api/current.txt b/compose/foundation/foundation-layout/api/current.txt
index 9d96717..bf64d36 100644
--- a/compose/foundation/foundation-layout/api/current.txt
+++ b/compose/foundation/foundation-layout/api/current.txt
@@ -90,6 +90,10 @@
     property public default float spacing;
   }
 
+  public final class AspectRatioKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float ratio, optional boolean matchHeightConstraintsFirst);
+  }
+
   public final class BoxKt {
     method @androidx.compose.runtime.Composable public static inline void Box(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void Box(androidx.compose.ui.Modifier modifier);
@@ -316,15 +320,15 @@
   @kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") public @interface ExperimentalLayout {
   }
 
-  public enum FlowCrossAxisAlignment {
-    enum_constant public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Center;
-    enum_constant public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment End;
-    enum_constant public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Start;
+  @Deprecated public enum FlowCrossAxisAlignment {
+    enum_constant @Deprecated public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Center;
+    enum_constant @Deprecated public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment End;
+    enum_constant @Deprecated public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Start;
   }
 
   public final class FlowKt {
-    method @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowColumn-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowRow-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowColumn-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowRow-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
   @kotlin.RequiresOptIn(message="This is an internal layout API subject to change and should not be used directly.") public @interface InternalLayoutApi {
@@ -340,54 +344,9 @@
     enum_constant public static final androidx.compose.foundation.layout.IntrinsicSize Min;
   }
 
-  public final class LayoutAspectRatioKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float ratio, optional boolean matchHeightConstraintsFirst);
-  }
-
-  public final class LayoutOffsetKt {
-    method public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> x, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> y);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absoluteOffset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
-    method @Deprecated public static androidx.compose.ui.Modifier absoluteOffsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
-    method public static androidx.compose.ui.Modifier offset(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> x, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> y);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier offset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
-    method @Deprecated public static androidx.compose.ui.Modifier offsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
-  }
-
-  public final class LayoutPaddingKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absolutePadding-w2-DAAU(androidx.compose.ui.Modifier, optional float left, optional float top, optional float right, optional float bottom);
-    method public static androidx.compose.ui.Modifier padding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues padding);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-S2lCeAQ(androidx.compose.ui.Modifier, optional float horizontal, optional float vertical);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-w2-DAAU(androidx.compose.ui.Modifier, optional float start, optional float top, optional float end, optional float bottom);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-wxomhCo(androidx.compose.ui.Modifier, float all);
-  }
-
   @kotlin.DslMarker public @interface LayoutScopeMarker {
   }
 
-  public final class LayoutSizeKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier defaultMinSizeConstraints-S2lCeAQ(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height-wxomhCo(androidx.compose.ui.Modifier, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier heightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeight-wxomhCo(androidx.compose.ui.Modifier, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-wxomhCo(androidx.compose.ui.Modifier, float size);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidth-wxomhCo(androidx.compose.ui.Modifier, float width);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-wxomhCo(androidx.compose.ui.Modifier, float size);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier sizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier width-wxomhCo(androidx.compose.ui.Modifier, float width);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier widthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentHeight(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Vertical align, optional boolean unbounded);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentSize(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment align, optional boolean unbounded);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentWidth(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Horizontal align, optional boolean unbounded);
-  }
-
   public enum MainAxisAlignment {
     enum_constant public static final androidx.compose.foundation.layout.MainAxisAlignment Center;
     enum_constant public static final androidx.compose.foundation.layout.MainAxisAlignment End;
@@ -397,6 +356,23 @@
     enum_constant public static final androidx.compose.foundation.layout.MainAxisAlignment Start;
   }
 
+  public final class OffsetKt {
+    method public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.unit.IntOffset> offset);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absoluteOffset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
+    method @Deprecated public static androidx.compose.ui.Modifier absoluteOffsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
+    method public static androidx.compose.ui.Modifier offset(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.unit.IntOffset> offset);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier offset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
+    method @Deprecated public static androidx.compose.ui.Modifier offsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
+  }
+
+  public final class PaddingKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absolutePadding-w2-DAAU(androidx.compose.ui.Modifier, optional float left, optional float top, optional float right, optional float bottom);
+    method public static androidx.compose.ui.Modifier padding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues padding);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-S2lCeAQ(androidx.compose.ui.Modifier, optional float horizontal, optional float vertical);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-w2-DAAU(androidx.compose.ui.Modifier, optional float start, optional float top, optional float end, optional float bottom);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-wxomhCo(androidx.compose.ui.Modifier, float all);
+  }
+
   @androidx.compose.runtime.Immutable public final class PaddingValues {
     method public float component1-D9Ej5fM();
     method public float component2-D9Ej5fM();
@@ -434,6 +410,30 @@
   public static final class RowScope.Companion implements androidx.compose.foundation.layout.RowScope {
   }
 
+  public final class SizeKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier defaultMinSizeConstraints-S2lCeAQ(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height-wxomhCo(androidx.compose.ui.Modifier, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier heightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeight-wxomhCo(androidx.compose.ui.Modifier, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-wxomhCo(androidx.compose.ui.Modifier, float size);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidth-wxomhCo(androidx.compose.ui.Modifier, float width);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-wxomhCo(androidx.compose.ui.Modifier, float size);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier sizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier width-wxomhCo(androidx.compose.ui.Modifier, float width);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier widthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentHeight(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Vertical align, optional boolean unbounded);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentSize(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment align, optional boolean unbounded);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentWidth(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Horizontal align, optional boolean unbounded);
+  }
+
   public enum SizeMode {
     enum_constant public static final androidx.compose.foundation.layout.SizeMode Expand;
     enum_constant public static final androidx.compose.foundation.layout.SizeMode Wrap;
diff --git a/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt b/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
index 9d96717..bf64d36 100644
--- a/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation-layout/api/public_plus_experimental_current.txt
@@ -90,6 +90,10 @@
     property public default float spacing;
   }
 
+  public final class AspectRatioKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float ratio, optional boolean matchHeightConstraintsFirst);
+  }
+
   public final class BoxKt {
     method @androidx.compose.runtime.Composable public static inline void Box(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void Box(androidx.compose.ui.Modifier modifier);
@@ -316,15 +320,15 @@
   @kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") public @interface ExperimentalLayout {
   }
 
-  public enum FlowCrossAxisAlignment {
-    enum_constant public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Center;
-    enum_constant public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment End;
-    enum_constant public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Start;
+  @Deprecated public enum FlowCrossAxisAlignment {
+    enum_constant @Deprecated public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Center;
+    enum_constant @Deprecated public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment End;
+    enum_constant @Deprecated public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Start;
   }
 
   public final class FlowKt {
-    method @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowColumn-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowRow-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowColumn-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowRow-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
   @kotlin.RequiresOptIn(message="This is an internal layout API subject to change and should not be used directly.") public @interface InternalLayoutApi {
@@ -340,54 +344,9 @@
     enum_constant public static final androidx.compose.foundation.layout.IntrinsicSize Min;
   }
 
-  public final class LayoutAspectRatioKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float ratio, optional boolean matchHeightConstraintsFirst);
-  }
-
-  public final class LayoutOffsetKt {
-    method public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> x, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> y);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absoluteOffset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
-    method @Deprecated public static androidx.compose.ui.Modifier absoluteOffsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
-    method public static androidx.compose.ui.Modifier offset(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> x, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> y);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier offset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
-    method @Deprecated public static androidx.compose.ui.Modifier offsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
-  }
-
-  public final class LayoutPaddingKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absolutePadding-w2-DAAU(androidx.compose.ui.Modifier, optional float left, optional float top, optional float right, optional float bottom);
-    method public static androidx.compose.ui.Modifier padding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues padding);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-S2lCeAQ(androidx.compose.ui.Modifier, optional float horizontal, optional float vertical);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-w2-DAAU(androidx.compose.ui.Modifier, optional float start, optional float top, optional float end, optional float bottom);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-wxomhCo(androidx.compose.ui.Modifier, float all);
-  }
-
   @kotlin.DslMarker public @interface LayoutScopeMarker {
   }
 
-  public final class LayoutSizeKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier defaultMinSizeConstraints-S2lCeAQ(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height-wxomhCo(androidx.compose.ui.Modifier, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier heightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeight-wxomhCo(androidx.compose.ui.Modifier, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-wxomhCo(androidx.compose.ui.Modifier, float size);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidth-wxomhCo(androidx.compose.ui.Modifier, float width);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-wxomhCo(androidx.compose.ui.Modifier, float size);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier sizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier width-wxomhCo(androidx.compose.ui.Modifier, float width);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier widthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentHeight(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Vertical align, optional boolean unbounded);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentSize(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment align, optional boolean unbounded);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentWidth(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Horizontal align, optional boolean unbounded);
-  }
-
   public enum MainAxisAlignment {
     enum_constant public static final androidx.compose.foundation.layout.MainAxisAlignment Center;
     enum_constant public static final androidx.compose.foundation.layout.MainAxisAlignment End;
@@ -397,6 +356,23 @@
     enum_constant public static final androidx.compose.foundation.layout.MainAxisAlignment Start;
   }
 
+  public final class OffsetKt {
+    method public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.unit.IntOffset> offset);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absoluteOffset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
+    method @Deprecated public static androidx.compose.ui.Modifier absoluteOffsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
+    method public static androidx.compose.ui.Modifier offset(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.unit.IntOffset> offset);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier offset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
+    method @Deprecated public static androidx.compose.ui.Modifier offsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
+  }
+
+  public final class PaddingKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absolutePadding-w2-DAAU(androidx.compose.ui.Modifier, optional float left, optional float top, optional float right, optional float bottom);
+    method public static androidx.compose.ui.Modifier padding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues padding);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-S2lCeAQ(androidx.compose.ui.Modifier, optional float horizontal, optional float vertical);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-w2-DAAU(androidx.compose.ui.Modifier, optional float start, optional float top, optional float end, optional float bottom);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-wxomhCo(androidx.compose.ui.Modifier, float all);
+  }
+
   @androidx.compose.runtime.Immutable public final class PaddingValues {
     method public float component1-D9Ej5fM();
     method public float component2-D9Ej5fM();
@@ -434,6 +410,30 @@
   public static final class RowScope.Companion implements androidx.compose.foundation.layout.RowScope {
   }
 
+  public final class SizeKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier defaultMinSizeConstraints-S2lCeAQ(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height-wxomhCo(androidx.compose.ui.Modifier, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier heightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeight-wxomhCo(androidx.compose.ui.Modifier, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-wxomhCo(androidx.compose.ui.Modifier, float size);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidth-wxomhCo(androidx.compose.ui.Modifier, float width);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-wxomhCo(androidx.compose.ui.Modifier, float size);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier sizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier width-wxomhCo(androidx.compose.ui.Modifier, float width);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier widthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentHeight(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Vertical align, optional boolean unbounded);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentSize(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment align, optional boolean unbounded);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentWidth(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Horizontal align, optional boolean unbounded);
+  }
+
   public enum SizeMode {
     enum_constant public static final androidx.compose.foundation.layout.SizeMode Expand;
     enum_constant public static final androidx.compose.foundation.layout.SizeMode Wrap;
diff --git a/compose/foundation/foundation-layout/api/restricted_current.txt b/compose/foundation/foundation-layout/api/restricted_current.txt
index 7f48fdd..81fd350 100644
--- a/compose/foundation/foundation-layout/api/restricted_current.txt
+++ b/compose/foundation/foundation-layout/api/restricted_current.txt
@@ -90,6 +90,10 @@
     property public default float spacing;
   }
 
+  public final class AspectRatioKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float ratio, optional boolean matchHeightConstraintsFirst);
+  }
+
   public final class BoxKt {
     method @androidx.compose.runtime.Composable public static inline void Box(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void Box(androidx.compose.ui.Modifier modifier);
@@ -319,15 +323,15 @@
   @kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") public @interface ExperimentalLayout {
   }
 
-  public enum FlowCrossAxisAlignment {
-    enum_constant public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Center;
-    enum_constant public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment End;
-    enum_constant public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Start;
+  @Deprecated public enum FlowCrossAxisAlignment {
+    enum_constant @Deprecated public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Center;
+    enum_constant @Deprecated public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment End;
+    enum_constant @Deprecated public static final androidx.compose.foundation.layout.FlowCrossAxisAlignment Start;
   }
 
   public final class FlowKt {
-    method @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowColumn-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowRow-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowColumn-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayout @androidx.compose.runtime.Composable public static void FlowRow-4CIokMg(optional androidx.compose.foundation.layout.SizeMode mainAxisSize, optional androidx.compose.foundation.layout.MainAxisAlignment mainAxisAlignment, optional float mainAxisSpacing, optional androidx.compose.foundation.layout.FlowCrossAxisAlignment crossAxisAlignment, optional float crossAxisSpacing, optional androidx.compose.foundation.layout.MainAxisAlignment lastLineMainAxisAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
   @kotlin.RequiresOptIn(message="This is an internal layout API subject to change and should not be used directly.") public @interface InternalLayoutApi {
@@ -343,54 +347,9 @@
     enum_constant public static final androidx.compose.foundation.layout.IntrinsicSize Min;
   }
 
-  public final class LayoutAspectRatioKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, @FloatRange(from=0.0, to=3.4E38, fromInclusive=false) float ratio, optional boolean matchHeightConstraintsFirst);
-  }
-
-  public final class LayoutOffsetKt {
-    method public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> x, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> y);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absoluteOffset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
-    method @Deprecated public static androidx.compose.ui.Modifier absoluteOffsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
-    method public static androidx.compose.ui.Modifier offset(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> x, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,java.lang.Float> y);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier offset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
-    method @Deprecated public static androidx.compose.ui.Modifier offsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
-  }
-
-  public final class LayoutPaddingKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absolutePadding-w2-DAAU(androidx.compose.ui.Modifier, optional float left, optional float top, optional float right, optional float bottom);
-    method public static androidx.compose.ui.Modifier padding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues padding);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-S2lCeAQ(androidx.compose.ui.Modifier, optional float horizontal, optional float vertical);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-w2-DAAU(androidx.compose.ui.Modifier, optional float start, optional float top, optional float end, optional float bottom);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-wxomhCo(androidx.compose.ui.Modifier, float all);
-  }
-
   @kotlin.DslMarker public @interface LayoutScopeMarker {
   }
 
-  public final class LayoutSizeKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier defaultMinSizeConstraints-S2lCeAQ(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height-wxomhCo(androidx.compose.ui.Modifier, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier heightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeight-wxomhCo(androidx.compose.ui.Modifier, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-wxomhCo(androidx.compose.ui.Modifier, float size);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidth-wxomhCo(androidx.compose.ui.Modifier, float width);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-wxomhCo(androidx.compose.ui.Modifier, float size);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier sizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier width-wxomhCo(androidx.compose.ui.Modifier, float width);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier widthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentHeight(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Vertical align, optional boolean unbounded);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentSize(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment align, optional boolean unbounded);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentWidth(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Horizontal align, optional boolean unbounded);
-  }
-
   public enum MainAxisAlignment {
     enum_constant public static final androidx.compose.foundation.layout.MainAxisAlignment Center;
     enum_constant public static final androidx.compose.foundation.layout.MainAxisAlignment End;
@@ -400,6 +359,23 @@
     enum_constant public static final androidx.compose.foundation.layout.MainAxisAlignment Start;
   }
 
+  public final class OffsetKt {
+    method public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.unit.IntOffset> offset);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absoluteOffset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
+    method @Deprecated public static androidx.compose.ui.Modifier absoluteOffsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
+    method public static androidx.compose.ui.Modifier offset(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.unit.IntOffset> offset);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier offset-S2lCeAQ(androidx.compose.ui.Modifier, optional float x, optional float y);
+    method @Deprecated public static androidx.compose.ui.Modifier offsetPx(androidx.compose.ui.Modifier, optional androidx.compose.runtime.State<java.lang.Float> x, optional androidx.compose.runtime.State<java.lang.Float> y);
+  }
+
+  public final class PaddingKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absolutePadding-w2-DAAU(androidx.compose.ui.Modifier, optional float left, optional float top, optional float right, optional float bottom);
+    method public static androidx.compose.ui.Modifier padding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues padding);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-S2lCeAQ(androidx.compose.ui.Modifier, optional float horizontal, optional float vertical);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-w2-DAAU(androidx.compose.ui.Modifier, optional float start, optional float top, optional float end, optional float bottom);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding-wxomhCo(androidx.compose.ui.Modifier, float all);
+  }
+
   @androidx.compose.runtime.Immutable public final class PaddingValues {
     method public float component1-D9Ej5fM();
     method public float component2-D9Ej5fM();
@@ -440,6 +416,30 @@
   public static final class RowScope.Companion implements androidx.compose.foundation.layout.RowScope {
   }
 
+  public final class SizeKt {
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier defaultMinSizeConstraints-S2lCeAQ(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height-wxomhCo(androidx.compose.ui.Modifier, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier heightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeight-wxomhCo(androidx.compose.ui.Modifier, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredHeightIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSize-wxomhCo(androidx.compose.ui.Modifier, float size);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredSizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidth-wxomhCo(androidx.compose.ui.Modifier, float width);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier preferredWidthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-S2lCeAQ(androidx.compose.ui.Modifier, float width, float height);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size-wxomhCo(androidx.compose.ui.Modifier, float size);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier sizeIn-w2-DAAU(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier width-wxomhCo(androidx.compose.ui.Modifier, float width);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier widthIn-S2lCeAQ(androidx.compose.ui.Modifier, optional float min, optional float max);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentHeight(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Vertical align, optional boolean unbounded);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentSize(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment align, optional boolean unbounded);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentWidth(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Horizontal align, optional boolean unbounded);
+  }
+
   public enum SizeMode {
     enum_constant public static final androidx.compose.foundation.layout.SizeMode Expand;
     enum_constant public static final androidx.compose.foundation.layout.SizeMode Wrap;
diff --git a/compose/foundation/foundation-layout/build.gradle b/compose/foundation/foundation-layout/build.gradle
index 0d9b4e1..af73d79 100644
--- a/compose/foundation/foundation-layout/build.gradle
+++ b/compose/foundation/foundation-layout/build.gradle
@@ -118,7 +118,6 @@
 
 tasks.withType(KotlinCompile).configureEach {
     kotlinOptions {
-        freeCompilerArgs += ["-XXLanguage:-NewInference"]
         useIR = true
     }
 }
diff --git a/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/LayoutDemos.kt b/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/LayoutDemos.kt
index eef9219..e15daee 100644
--- a/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/LayoutDemos.kt
+++ b/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/LayoutDemos.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.foundation.layout.demos
 
-import androidx.compose.foundation.layout.samples.SimpleFlowRow
 import androidx.compose.integration.demos.common.ComposableDemo
 import androidx.compose.integration.demos.common.DemoCategory
 
@@ -30,7 +29,6 @@
                 ComposableDemo("With ConstraintSet DSL") { DemoConstraintSet() }
             )
         ),
-        ComposableDemo("Flow layout") { SimpleFlowRow() },
         ComposableDemo("Row and column") { SimpleLayoutDemo() },
         ComposableDemo("Rtl support") { RtlDemo() }
     )
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowSample.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowSample.kt
deleted file mode 100644
index d577191..0000000
--- a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowSample.kt
+++ /dev/null
@@ -1,62 +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.foundation.layout.samples
-
-import androidx.annotation.Sampled
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.ExperimentalLayout
-import androidx.compose.foundation.layout.FlowColumn
-import androidx.compose.foundation.layout.FlowRow
-import androidx.compose.foundation.layout.preferredSize
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.dp
-
-val sizes = listOf(
-    50.dp, 50.dp, 50.dp, 30.dp, 40.dp, 110.dp, 100.dp, 40.dp, 30.dp,
-    20.dp, 70.dp, 50.dp, 100.dp, 20.dp, 60.dp, 60.dp, 50.dp, 60.dp
-)
-
-@Sampled
-@Composable
-@OptIn(ExperimentalLayout::class)
-fun SimpleFlowRow() {
-    FlowRow(
-        mainAxisSpacing = 10.dp,
-        crossAxisSpacing = 10.dp
-    ) {
-        sizes.forEach { size ->
-            Box(Modifier.preferredSize(size, 20.dp).background(Color.Magenta))
-        }
-    }
-}
-
-@Sampled
-@Composable
-@OptIn(ExperimentalLayout::class)
-fun SimpleFlowColumn() {
-    FlowColumn(
-        mainAxisSpacing = 10.dp,
-        crossAxisSpacing = 10.dp
-    ) {
-        sizes.forEach { size ->
-            Box(Modifier.preferredSize(20.dp, size).background(Color.Magenta))
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/LayoutOffsetSample.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/LayoutOffsetSample.kt
index dd01667..03262c02 100644
--- a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/LayoutOffsetSample.kt
+++ b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/LayoutOffsetSample.kt
@@ -23,11 +23,14 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.gesture.tapGestureFilter
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 
 @Sampled
@@ -60,12 +63,12 @@
 fun OffsetPxModifier() {
     // This text will be offset in steps of 10.dp from the top left of the available space in
     // left-to-right context, and from top right in right-to-left context.
-    val offset = remember { mutableStateOf(0f) }
+    var offset by remember { mutableStateOf(0) }
     Text(
         "Layout offset modifier sample",
         Modifier
-            .tapGestureFilter { offset.value += 10f }
-            .offset({ offset.value }, { offset.value })
+            .tapGestureFilter { offset += 10 }
+            .offset { IntOffset(offset, offset) }
     )
 }
 
@@ -73,11 +76,11 @@
 @Composable
 fun AbsoluteOffsetPxModifier() {
     // This text will be offset in steps of 10.dp from the top left of the available space.
-    val offset = remember { mutableStateOf(0f) }
+    var offset by remember { mutableStateOf(0) }
     Text(
         "Layout offset modifier sample",
         Modifier
-            .tapGestureFilter { offset.value += 10f }
-            .absoluteOffset({ offset.value }, { offset.value })
+            .tapGestureFilter { offset += 10 }
+            .absoluteOffset { IntOffset(offset, offset) }
     )
 }
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutAspectRatioTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/AspectRatioTest.kt
similarity index 99%
rename from compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutAspectRatioTest.kt
rename to compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/AspectRatioTest.kt
index eafd7e9..0ecfd38 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutAspectRatioTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/AspectRatioTest.kt
@@ -41,7 +41,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class LayoutAspectRatioTest : LayoutTest() {
+class AspectRatioTest : LayoutTest() {
     @Before
     fun before() {
         isDebugInspectorInfoEnabled = true
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/BoxTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/BoxTest.kt
index d235e93..c64b175 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/BoxTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/BoxTest.kt
@@ -18,9 +18,11 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionInParent
@@ -453,6 +455,46 @@
     }
 
     @Test
+    fun testBox_childAffectsBoxSize() {
+        var layoutLatch = CountDownLatch(2)
+        val size = mutableStateOf(10.dp)
+        var measure = 0
+        var layout = 0
+        show {
+            Box {
+                Layout(
+                    content = {
+                        Box {
+                            Box(
+                                Modifier.size(size.value, 10.dp).onGloballyPositioned {
+                                    layoutLatch.countDown()
+                                }
+                            )
+                        }
+                    }
+                ) { measurables, constraints ->
+                    val placeable = measurables.first().measure(constraints)
+                    ++measure
+                    layout(placeable.width, placeable.height) {
+                        placeable.place(0, 0)
+                        ++layout
+                        layoutLatch.countDown()
+                    }
+                }
+            }
+        }
+        assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
+        assertEquals(1, measure)
+        assertEquals(1, layout)
+
+        layoutLatch = CountDownLatch(2)
+        activityTestRule.runOnUiThread { size.value = 20.dp }
+        assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
+        assertEquals(2, measure)
+        assertEquals(2, layout)
+    }
+
+    @Test
     fun testBox_hasCorrectIntrinsicMeasurements() = with(density) {
         val testWidth = 90.toDp()
         val testHeight = 80.toDp()
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/ContainerTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/ContainerTest.kt
deleted file mode 100644
index c2b1893..0000000
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/ContainerTest.kt
+++ /dev/null
@@ -1,513 +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.foundation.layout
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.positionInRoot
-import androidx.compose.ui.node.Ref
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.constrainHeight
-import androidx.compose.ui.unit.constrainWidth
-import androidx.compose.ui.unit.dp
-import androidx.test.filters.SmallTest
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertTrue
-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
-import kotlin.math.roundToInt
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class ContainerTest : LayoutTest() {
-    @Test
-    fun testContainer_wrapsChild() = with(density) {
-        val sizeDp = 50.dp
-        val size = sizeDp.toIntPx()
-
-        val positionedLatch = CountDownLatch(1)
-        val containerSize = Ref<IntSize>()
-        show {
-            Box {
-                Container(
-                    Modifier.onGloballyPositioned { coordinates ->
-                        containerSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    EmptyBox(width = sizeDp, height = sizeDp)
-                }
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(IntSize(size, size), containerSize.value)
-    }
-
-    @Test
-    fun testContainer_appliesPaddingToChild() = with(density) {
-        val paddingDp = 20.dp
-        val padding = paddingDp.toIntPx()
-        val sizeDp = 50.dp
-        val size = sizeDp.toIntPx()
-
-        val positionedLatch = CountDownLatch(2)
-        val containerSize = Ref<IntSize>()
-        val childPosition = Ref<Offset>()
-        show {
-            Box {
-                Container(
-                    padding = PaddingValues(paddingDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates ->
-                        containerSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    EmptyBox(
-                        width = sizeDp, height = sizeDp,
-                        modifier = Modifier.onGloballyPositioned { coordinates ->
-                            childPosition.value = coordinates.positionInRoot
-                            positionedLatch.countDown()
-                        }
-                    )
-                }
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        val totalPadding = paddingDp.toIntPx() * 2
-        assertEquals(
-            IntSize(size + totalPadding, size + totalPadding),
-            containerSize.value
-        )
-        assertEquals(Offset(padding.toFloat(), padding.toFloat()), childPosition.value)
-    }
-
-    @Test
-    fun testContainer_passesConstraintsToChild() = with(density) {
-        val sizeDp = 100.dp
-        val childWidthDp = 20.dp
-        val childWidth = childWidthDp.toIntPx()
-        val childHeightDp = 30.dp
-        val childHeight = childHeightDp.toIntPx()
-        val childConstraints = DpConstraints.fixed(childWidthDp, childHeightDp)
-
-        val positionedLatch = CountDownLatch(4)
-        val containerSize = Ref<IntSize>()
-        val childSize = Array(3) { IntSize(0, 0) }
-        show {
-            Box {
-                Row(
-                    Modifier.onGloballyPositioned { coordinates ->
-                        containerSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    Container(width = childWidthDp, height = childHeightDp) {
-                        EmptyBox(
-                            width = sizeDp, height = sizeDp,
-                            modifier = Modifier.onGloballyPositioned { coordinates ->
-                                childSize[0] = coordinates.size
-                                positionedLatch.countDown()
-                            }
-                        )
-                    }
-                    Container(constraints = childConstraints) {
-                        EmptyBox(
-                            width = sizeDp, height = sizeDp,
-                            modifier = Modifier.onGloballyPositioned { coordinates ->
-                                childSize[1] = coordinates.size
-                                positionedLatch.countDown()
-                            }
-                        )
-                    }
-                    Container(
-                        constraints = (childConstraints),
-                        // These should have priority.
-                        width = (childWidthDp * 2),
-                        height = (childHeightDp * 2)
-                    ) {
-                        EmptyBox(
-                            width = sizeDp, height = sizeDp,
-                            modifier = Modifier.onGloballyPositioned { coordinates ->
-                                childSize[2] = coordinates.size
-                                positionedLatch.countDown()
-                            }
-                        )
-                    }
-                }
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(IntSize(childWidth, childHeight), childSize[0])
-        assertEquals(IntSize(childWidth, childHeight), childSize[1])
-        assertEquals(
-            IntSize((childWidthDp * 2).toIntPx(), (childHeightDp * 2).toIntPx()),
-            childSize[2]
-        )
-    }
-
-    @Test
-    fun testContainer_fillsAvailableSpace_whenSizeIsMax() = with(density) {
-        val sizeDp = 50.dp
-        val size = sizeDp.toIntPx()
-
-        val positionedLatch = CountDownLatch(3)
-        val alignSize = Ref<IntSize>()
-        val containerSize = Ref<IntSize>()
-        val childSize = Ref<IntSize>()
-        val childPosition = Ref<Offset>()
-        show {
-            Container(
-                alignment = Alignment.TopStart,
-                modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                    alignSize.value = coordinates.size
-                    positionedLatch.countDown()
-                }
-            ) {
-                Container(
-                    expanded = true,
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        containerSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    EmptyBox(
-                        width = sizeDp,
-                        height = sizeDp,
-                        modifier = Modifier.onGloballyPositioned { coordinates ->
-                            childSize.value = coordinates.size
-                            childPosition.value = coordinates.positionInRoot
-                            positionedLatch.countDown()
-                        }
-                    )
-                }
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(alignSize.value, containerSize.value)
-        assertEquals(IntSize(size, size), childSize.value)
-        assertEquals(
-            Offset(
-                (containerSize.value!!.width.toFloat() / 2 - size.toFloat() / 2)
-                    .roundToInt().toFloat(),
-                (containerSize.value!!.height.toFloat() / 2 - size.toFloat() / 2)
-                    .roundToInt().toFloat()
-            ),
-            childPosition.value
-        )
-    }
-
-    @Test
-    fun testContainer_respectsIncomingMinConstraints() = with(density) {
-        // Start with an even number of Int to avoid rounding issues due to different DPI
-        // I.e, if we fix Dp instead, it's possible that when we convert to Px, sizeDp can round
-        // down but sizeDp * 2 can round up, causing a 1 pixel test error.
-        val size = 200
-        val sizeDp = size.toDp()
-
-        val positionedLatch = CountDownLatch(2)
-        val containerSize = Ref<IntSize>()
-        val childSize = Ref<IntSize>()
-        val childPosition = Ref<Offset>()
-        show {
-            Box {
-                val constraints = DpConstraints(minWidth = sizeDp * 2, minHeight = sizeDp * 2)
-                ConstrainedBox(
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        containerSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    },
-                    constraints = constraints
-                ) {
-                    Container(alignment = Alignment.BottomEnd) {
-                        EmptyBox(
-                            width = sizeDp, height = sizeDp,
-                            modifier = Modifier.onGloballyPositioned { coordinates ->
-                                childSize.value = coordinates.size
-                                childPosition.value =
-                                    coordinates.positionInRoot
-                                positionedLatch.countDown()
-                            }
-                        )
-                    }
-                }
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize((sizeDp * 2).toIntPx(), (sizeDp * 2).toIntPx()),
-            containerSize.value
-        )
-        assertEquals(IntSize(size, size), childSize.value)
-        assertEquals(Offset(size.toFloat(), size.toFloat()), childPosition.value)
-    }
-
-    @Test
-    fun testContainer_hasTheRightSize_withPaddingAndNoChildren() = with(density) {
-        val sizeDp = 50.dp
-        val size = sizeDp.toIntPx()
-
-        val containerSize = Ref<IntSize>()
-        val latch = CountDownLatch(1)
-        show {
-            Box {
-                Container(
-                    width = sizeDp, height = sizeDp, padding = PaddingValues(10.dp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        containerSize.value = coordinates.size
-                        latch.countDown()
-                    }
-                ) {
-                }
-            }
-        }
-        assertTrue(latch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(IntSize(size, size), containerSize.value)
-    }
-
-    @Test
-    fun testContainer_correctlyAppliesNonSymmetricPadding() = with(density) {
-        val childSizeDp = 50.toDp()
-        val paddingLeft = 8.toDp()
-        val paddingTop = 7.toDp()
-        val paddingRight = 5.toDp()
-        val paddingBottom = 10.toDp()
-        val innerPadding = PaddingValues(
-            start = paddingLeft,
-            top = paddingTop,
-            end = paddingRight,
-            bottom = paddingBottom
-        )
-        val expectedSize = IntSize(
-            childSizeDp.toIntPx() + paddingLeft.toIntPx() + paddingRight.toIntPx(),
-            childSizeDp.toIntPx() + paddingTop.toIntPx() + paddingBottom.toIntPx()
-        )
-
-        var containerSize: IntSize? = null
-        val latch = CountDownLatch(1)
-        show {
-            Box {
-                Container(
-                    Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        containerSize = coordinates.size
-                        latch.countDown()
-                    },
-                    padding = innerPadding
-                ) {
-                    Spacer(Modifier.preferredSize(width = childSizeDp, height = childSizeDp))
-                }
-            }
-        }
-        assertTrue(latch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(expectedSize, containerSize)
-    }
-
-    @Test
-    fun testContainer_contentSmallerThanPaddingIsCentered() = with(density) {
-        val containerSize = 50.toDp()
-        val padding = 10.toDp()
-        val childSize = 5.toDp()
-        val innerPadding = PaddingValues(padding)
-
-        var childCoordinates: LayoutCoordinates? = null
-        val latch = CountDownLatch(1)
-        show {
-            Box {
-                Container(width = containerSize, height = containerSize, padding = innerPadding) {
-                    Spacer(
-                        Modifier
-                            .preferredSize(width = childSize, height = childSize)
-                            .onGloballyPositioned { coordinates: LayoutCoordinates ->
-                                childCoordinates = coordinates
-                                latch.countDown()
-                            }
-                    )
-                }
-            }
-        }
-        assertTrue(latch.await(1, TimeUnit.SECONDS))
-
-        val centeringOffset = padding.toIntPx() +
-            (
-                (
-                    containerSize.toIntPx() - padding.toIntPx() * 2 -
-                        childSize.toIntPx()
-                    ) / 2f
-                ).roundToInt()
-        val childPosition = childCoordinates!!.parentCoordinates!!.childToLocal(
-            childCoordinates!!,
-            Offset.Zero
-        )
-        assertEquals(
-            Offset(centeringOffset.toFloat(), centeringOffset.toFloat()),
-            childPosition
-        )
-        assertEquals(IntSize(childSize.toIntPx(), childSize.toIntPx()), childCoordinates!!.size)
-    }
-
-    @Test
-    fun testContainer_childAffectsContainerSize() {
-        var layoutLatch = CountDownLatch(2)
-        val size = mutableStateOf(10.dp)
-        var measure = 0
-        var layout = 0
-        show {
-            Box {
-                Layout(
-                    content = {
-                        Container {
-                            EmptyBox(
-                                width = size.value,
-                                height = 10.dp,
-                                modifier = Modifier.onGloballyPositioned {
-                                    layoutLatch.countDown()
-                                }
-                            )
-                        }
-                    }
-                ) { measurables, constraints ->
-                    val placeable = measurables.first().measure(constraints)
-                    ++measure
-                    layout(placeable.width, placeable.height) {
-                        placeable.place(0, 0)
-                        ++layout
-                        layoutLatch.countDown()
-                    }
-                }
-            }
-        }
-        assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
-        assertEquals(1, measure)
-        assertEquals(1, layout)
-
-        layoutLatch = CountDownLatch(2)
-        activityTestRule.runOnUiThread { size.value = 20.dp }
-        assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
-        assertEquals(2, measure)
-        assertEquals(2, layout)
-    }
-
-    @Test
-    fun testContainer_childDoesNotAffectContainerSize_whenSizeIsMax() {
-        var layoutLatch = CountDownLatch(2)
-        val size = mutableStateOf(10.dp)
-        var measure = 0
-        var layout = 0
-        show {
-            Box {
-                Layout(
-                    content = {
-                        Container(expanded = true) {
-                            EmptyBox(
-                                width = size.value,
-                                height = 10.dp,
-                                modifier = Modifier.onGloballyPositioned {
-                                    layoutLatch.countDown()
-                                }
-                            )
-                        }
-                    }
-                ) { measurables, constraints ->
-                    val placeable = measurables.first().measure(constraints)
-                    ++measure
-                    layout(placeable.width, placeable.height) {
-                        placeable.place(0, 0)
-                        ++layout
-                        layoutLatch.countDown()
-                    }
-                }
-            }
-        }
-        assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
-        assertEquals(1, measure)
-        assertEquals(1, layout)
-
-        layoutLatch = CountDownLatch(1)
-        activityTestRule.runOnUiThread { size.value = 20.dp }
-        assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
-        assertEquals(1, measure)
-        assertEquals(1, layout)
-    }
-
-    @Test
-    fun testContainer_childDoesNotAffectContainerSize_whenFixedWidthAndHeight() {
-        var layoutLatch = CountDownLatch(2)
-        val size = mutableStateOf(10.dp)
-        var measure = 0
-        var layout = 0
-        show {
-            Box {
-                Layout(
-                    content = {
-                        Container(width = 20.dp, height = 20.dp) {
-                            EmptyBox(
-                                width = size.value,
-                                height = 10.dp,
-                                modifier = Modifier.onGloballyPositioned {
-                                    layoutLatch.countDown()
-                                }
-                            )
-                        }
-                    }
-                ) { measurables, constraints ->
-                    val placeable = measurables.first().measure(constraints)
-                    ++measure
-                    layout(placeable.width, placeable.height) {
-                        placeable.placeRelative(0, 0)
-                        ++layout
-                        layoutLatch.countDown()
-                    }
-                }
-            }
-        }
-        assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
-        assertEquals(1, measure)
-        assertEquals(1, layout)
-
-        layoutLatch = CountDownLatch(1)
-        activityTestRule.runOnUiThread { size.value = 20.dp }
-        assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
-        assertEquals(1, measure)
-        assertEquals(1, layout)
-    }
-
-    @Composable
-    fun EmptyBox(width: Dp, height: Dp, modifier: Modifier = Modifier) {
-        Layout(modifier = modifier, content = { }) { _, constraints ->
-            layout(
-                constraints.constrainWidth(width.toIntPx()),
-                constraints.constrainHeight(height.toIntPx())
-            ) {}
-        }
-    }
-}
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/FlowTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/FlowTest.kt
deleted file mode 100644
index 09d55d0..0000000
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/FlowTest.kt
+++ /dev/null
@@ -1,2195 +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.foundation.layout
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.node.Ref
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.unit.IntSize
-import androidx.test.filters.SmallTest
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertTrue
-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
-import kotlin.math.roundToInt
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalLayout::class)
-class FlowTest : LayoutTest() {
-    @Test
-    fun testFlowRow() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 5, height = size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(x = (size * (i % 5)).toFloat(), y = (size * (i / 5)).toFloat()),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withMainAxisSize_wrap() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(mainAxisSize = SizeMode.Wrap) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 5, height = size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i % 5)).toFloat(),
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withMainAxisSize_expand() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(mainAxisSize = SizeMode.Expand) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = flowWidth, height = size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i % 5)).toFloat(),
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withMainAxisAlignment_center() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.Center
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = flowWidth, height = size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = ((flowWidth - size * 5) / 2 + size * (i % 5)).toFloat(),
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withMainAxisAlignment_start() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.Start
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = flowWidth, height = size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i % 5)).toFloat(),
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withMainAxisAlignment_end() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.End
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = flowWidth, height = size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (flowWidth - size * 5 + size * (i % 5)).toFloat(),
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withMainAxisAlignment_spaceEvenly() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceEvenly
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = flowWidth, height = size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            val x = ((flowWidth - size * 5) * (i % 5 + 1) / 6f).roundToInt() + size * (i % 5)
-            assertEquals(
-                Offset(
-                    x = x.toFloat(),
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withMainAxisAlignment_spaceBetween() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = flowWidth, height = size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = ((flowWidth - size * 5) * (i % 5) / 4 + size * (i % 5)).toFloat(),
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withMainAxisAlignment_spaceAround() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceAround
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = flowWidth, height = size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = ((flowWidth - size * 5) * (i % 5 + 0.5f) / 5 + size * (i % 5)).roundToInt()
-                        .toFloat(),
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withLastLineMainAxisAlignment_justify_center() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween,
-                        lastLineMainAxisAlignment = FlowMainAxisAlignment.Center
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(flowWidth, size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = if (i < 10) {
-                        ((flowWidth - size * 5) * (i % 5) / 4 + size * (i % 5)).toFloat()
-                    } else {
-                        ((flowWidth - size * 5) / 2 + size * (i % 5)).toFloat()
-                    },
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withLastLineMainAxisAlignment_justify_start() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween,
-                        lastLineMainAxisAlignment = FlowMainAxisAlignment.Start
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(flowWidth, size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = if (i < 10) {
-                        ((flowWidth - size * 5) * (i % 5) / 4 + size * (i % 5)).toFloat()
-                    } else {
-                        (size * (i % 5)).toFloat()
-                    },
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withLastLineMainAxisAlignment_justify_end() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween,
-                        lastLineMainAxisAlignment = FlowMainAxisAlignment.End
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(flowWidth, size * 3),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = if (i < 10) {
-                        ((flowWidth - size * 5) * (i % 5) / 4 + size * (i % 5)).toFloat()
-                    } else {
-                        ((flowWidth - size * 5) + size * (i % 5)).toFloat()
-                    },
-                    y = (size * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withMainAxisSpacing() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val spacing = 32
-        val spacingDp = spacing.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(mainAxisSpacing = spacingDp) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3 + spacing * 2, height = size * 5),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = ((size + spacing) * (i % 3)).toFloat(),
-                    y = (size * (i / 3)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withCrossAxisAlignment_center() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(crossAxisAlignment = FlowCrossAxisAlignment.Center) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp,
-                                height = if (i % 2 == 0) sizeDp else sizeDp * 2,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 5, height = size * 6),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(
-                    width = size,
-                    height = if (i % 2 == 0) size else size * 2
-                ),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i % 5)).toFloat(),
-                    y = (size * 2 * (i / 5) + if (i % 2 == 0) size / 2 else 0)
-                        .toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withCrossAxisAlignment_start() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(crossAxisAlignment = FlowCrossAxisAlignment.Start) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp,
-                                height = if (i % 2 == 0) sizeDp else sizeDp * 2,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 5, height = size * 6),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(
-                    width = size,
-                    height = if (i % 2 == 0) size else size * 2
-                ),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i % 5)).toFloat(),
-                    y = (size * 2 * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withCrossAxisAlignment_end() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(crossAxisAlignment = FlowCrossAxisAlignment.End) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp,
-                                height = if (i % 2 == 0) sizeDp else sizeDp * 2,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 5, height = size * 6),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(
-                    width = size,
-                    height = if (i % 2 == 0) size else size * 2
-                ),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i % 5)).toFloat(),
-                    y = (size * 2 * (i / 5) + if (i % 2 == 0) size else 0).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowRow_withCrossAxisSpacing() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val spacing = 32
-        val spacingDp = spacing.toDp()
-        val flowWidth = 256
-        val flowWidthDp = flowWidth.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxWidth = flowWidthDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowRow(crossAxisSpacing = spacingDp) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 5, height = size * 3 + spacing * 2),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i % 5)).toFloat(),
-                    y = ((size + spacing) * (i / 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3, height = size * 5),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = (size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withMainAxisSize_wrap() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(mainAxisSize = SizeMode.Wrap) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3, height = size * 5),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = (size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withMainAxisSize_expand() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(mainAxisSize = SizeMode.Expand) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3, height = flowHeight),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = (size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withMainAxisAlignment_center() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.Center
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3, height = flowHeight),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = ((flowHeight - size * 5) / 2 + size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withMainAxisAlignment_start() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.Start
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3, height = flowHeight),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = (size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withMainAxisAlignment_end() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.End
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3, height = flowHeight),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = (flowHeight - size * 5 + size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withMainAxisAlignment_spaceEvenly() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceEvenly
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3, height = flowHeight),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            val y = ((flowHeight - size * 5) * (i % 5 + 1) / 6f).roundToInt() + size * (i % 5)
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = y.toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withMainAxisAlignment_spaceBetween() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3, height = flowHeight),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = ((flowHeight - size * 5) * (i % 5) / 4 + size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withMainAxisAlignment_spaceAround() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceAround
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3, height = flowHeight),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = ((flowHeight - size * 5) * (i % 5 + 0.5f) / 5 + size * (i % 5)).roundToInt()
-                        .toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withLastLineMainAxisAlignment_justify_center() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween,
-                        lastLineMainAxisAlignment = FlowMainAxisAlignment.Center
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(size * 3, flowHeight),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = if (i < 10) {
-                        ((flowHeight - size * 5) * (i % 5) / 4 + size * (i % 5)).toFloat()
-                    } else {
-                        ((flowHeight - size * 5) / 2 + size * (i % 5)).toFloat()
-                    }
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withLastLineMainAxisAlignment_justify_start() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween,
-                        lastLineMainAxisAlignment = FlowMainAxisAlignment.Start
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(size * 3, flowHeight),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = if (i < 10) {
-                        ((flowHeight - size * 5) * (i % 5) / 4 + size * (i % 5)).toFloat()
-                    } else {
-                        (size * (i % 5)).toFloat()
-                    }
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withLastLineMainAxisAlignment_justify_end() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(
-                        mainAxisSize = SizeMode.Expand,
-                        mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween,
-                        lastLineMainAxisAlignment = FlowMainAxisAlignment.End
-                    ) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(size * 3, flowHeight),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 5)).toFloat(),
-                    y = if (i < 10) {
-                        ((flowHeight - size * 5) * (i % 5) / 4 + size * (i % 5)).toFloat()
-                    } else {
-                        ((flowHeight - size * 5) + size * (i % 5)).toFloat()
-                    }
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withMainAxisSpacing() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val spacing = 32
-        val spacingDp = spacing.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(mainAxisSpacing = spacingDp) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 5, height = size * 3 + spacing * 2),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * (i / 3)).toFloat(),
-                    y = ((size + spacing) * (i % 3)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withCrossAxisAlignment_center() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(crossAxisAlignment = FlowCrossAxisAlignment.Center) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = if (i % 2 == 0) sizeDp else sizeDp * 2,
-                                height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 6, height = size * 5),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(
-                    width = if (i % 2 == 0) size else size * 2,
-                    height = size
-                ),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * 2 * (i / 5) + if (i % 2 == 0) size / 2 else 0)
-                        .toFloat(),
-                    y = (size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withCrossAxisAlignment_start() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(crossAxisAlignment = FlowCrossAxisAlignment.Start) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = if (i % 2 == 0) sizeDp else sizeDp * 2,
-                                height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 6, height = size * 5),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = if (i % 2 == 0) size else size * 2, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = (size * 2 * (i / 5)).toFloat(),
-                    y = (size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withCrossAxisAlignment_end() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(crossAxisAlignment = FlowCrossAxisAlignment.End) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = if (i % 2 == 0) sizeDp else sizeDp * 2,
-                                height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 6, height = size * 5),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(
-                    width = if (i % 2 == 0) size else size * 2,
-                    height = size
-                ),
-                childSize[i].value
-            )
-            val x = (size * 2 * (i / 5) + if (i % 2 == 0) size else 0)
-            assertEquals(
-                Offset(
-                    x = x.toFloat(),
-                    y = (size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-
-    @Test
-    fun testFlowColumn_withCrossAxisSpacing() = with(density) {
-        val numberOfSquares = 15
-        val size = 48
-        val sizeDp = size.toDp()
-        val spacing = 32
-        val spacingDp = spacing.toDp()
-        val flowHeight = 256
-        val flowHeightDp = flowHeight.toDp()
-
-        val flowSize = Ref<IntSize>()
-        val childSize = Array(numberOfSquares) { Ref<IntSize>() }
-        val childPosition = Array(numberOfSquares) { Ref<Offset>() }
-        val positionedLatch = CountDownLatch(numberOfSquares + 1)
-
-        show {
-            Box {
-                ConstrainedBox(
-                    constraints = DpConstraints(maxHeight = flowHeightDp),
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        flowSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }
-                ) {
-                    FlowColumn(crossAxisSpacing = spacingDp) {
-                        for (i in 0 until numberOfSquares) {
-                            Container(
-                                width = sizeDp, height = sizeDp,
-                                modifier = Modifier.saveLayoutInfo(
-                                    childSize[i],
-                                    childPosition[i],
-                                    positionedLatch
-                                )
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(
-            IntSize(width = size * 3 + spacing * 2, height = size * 5),
-            flowSize.value
-        )
-        for (i in 0 until numberOfSquares) {
-            assertEquals(
-                IntSize(width = size, height = size),
-                childSize[i].value
-            )
-            assertEquals(
-                Offset(
-                    x = ((size + spacing) * (i / 5)).toFloat(),
-                    y = (size * (i % 5)).toFloat()
-                ),
-                childPosition[i].value
-            )
-        }
-    }
-}
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutAlignTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutAlignTest.kt
deleted file mode 100644
index 7a9fe53..0000000
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutAlignTest.kt
+++ /dev/null
@@ -1,487 +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.foundation.layout
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Providers
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.positionInParent
-import androidx.compose.ui.node.Ref
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.platform.AmbientLayoutDirection
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.IntSize.Companion.Zero
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.enforce
-import androidx.test.filters.SmallTest
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertTrue
-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
-import kotlin.math.roundToInt
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class LayoutAlignTest : LayoutTest() {
-    @Test
-    fun test2DWrapContentSize() = with(density) {
-        val sizeDp = 50.dp
-        val size = sizeDp.toIntPx()
-
-        val positionedLatch = CountDownLatch(2)
-        val alignSize = Ref<IntSize>()
-        val alignPosition = Ref<Offset>()
-        val childSize = Ref<IntSize>()
-        val childPosition = Ref<Offset>()
-        show {
-            Container(Modifier.saveLayoutInfo(alignSize, alignPosition, positionedLatch)) {
-                Container(
-                    Modifier.fillMaxSize()
-                        .wrapContentSize(Alignment.BottomEnd)
-                        .preferredSize(sizeDp)
-                        .saveLayoutInfo(childSize, childPosition, positionedLatch)
-                ) {
-                }
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        val root = findOwnerView()
-        waitForDraw(root)
-
-        assertEquals(IntSize(root.width, root.height), alignSize.value)
-        assertEquals(Offset(0f, 0f), alignPosition.value)
-        assertEquals(IntSize(size, size), childSize.value)
-        assertEquals(
-            Offset(root.width - size.toFloat(), root.height - size.toFloat()),
-            childPosition.value
-        )
-    }
-
-    @Test
-    fun test1DWrapContentSize() = with(density) {
-        val sizeDp = 50.dp
-        val size = sizeDp.toIntPx()
-
-        val positionedLatch = CountDownLatch(2)
-        val alignSize = Ref<IntSize>()
-        val alignPosition = Ref<Offset>()
-        val childSize = Ref<IntSize>()
-        val childPosition = Ref<Offset>()
-        show {
-            Container(
-                Modifier.saveLayoutInfo(
-                    size = alignSize,
-                    position = alignPosition,
-                    positionedLatch = positionedLatch
-                )
-            ) {
-                Container(
-                    Modifier.fillMaxSize()
-                        .wrapContentWidth(Alignment.End)
-                        .preferredWidth(sizeDp)
-                        .saveLayoutInfo(childSize, childPosition, positionedLatch)
-                ) {
-                }
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        val root = findOwnerView()
-        waitForDraw(root)
-
-        assertEquals(IntSize(root.width, root.height), alignSize.value)
-        assertEquals(Offset(0f, 0f), alignPosition.value)
-        assertEquals(IntSize(size, root.height), childSize.value)
-        assertEquals(Offset(root.width - size.toFloat(), 0f), childPosition.value)
-    }
-
-    @Test
-    fun testWrapContentSize_rtl() = with(density) {
-        val sizeDp = 200.toDp()
-        val size = sizeDp.toIntPx()
-
-        val positionedLatch = CountDownLatch(3)
-        val childSize = Array(3) { Ref<IntSize>() }
-        val childPosition = Array(3) { Ref<Offset>() }
-        show {
-            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
-                Box(Modifier.fillMaxSize()) {
-                    Box(Modifier.fillMaxSize().wrapContentSize(Alignment.TopStart)) {
-                        Box(
-                            Modifier.preferredSize(sizeDp)
-                                .saveLayoutInfo(childSize[0], childPosition[0], positionedLatch)
-                        ) {
-                        }
-                    }
-                    Box(Modifier.fillMaxSize().wrapContentHeight(Alignment.CenterVertically)) {
-                        Box(
-                            Modifier.preferredSize(sizeDp)
-                                .saveLayoutInfo(childSize[1], childPosition[1], positionedLatch)
-                        ) {
-                        }
-                    }
-                    Box(Modifier.fillMaxSize().wrapContentSize(Alignment.BottomEnd)) {
-                        Box(
-                            Modifier.preferredSize(sizeDp)
-                                .saveLayoutInfo(childSize[2], childPosition[2], positionedLatch)
-                        ) {
-                        }
-                    }
-                }
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        val root = findOwnerView()
-        waitForDraw(root)
-
-        assertEquals(
-            Offset((root.width - size).toFloat(), 0f),
-            childPosition[0].value
-        )
-        assertEquals(
-            Offset(
-                (root.width - size).toFloat(),
-                ((root.height - size) / 2).toFloat()
-            ),
-            childPosition[1].value
-        )
-        assertEquals(
-            Offset(0f, (root.height - size).toFloat()),
-            childPosition[2].value
-        )
-    }
-
-    @Test
-    fun testModifier_wrapsContent() = with(density) {
-        val contentSize = 50.dp
-        val size = Ref<IntSize>()
-        val latch = CountDownLatch(1)
-        show {
-            Container {
-                Container(Modifier.saveLayoutInfo(size, Ref(), latch)) {
-                    Container(
-                        Modifier.wrapContentSize(Alignment.TopStart)
-                            .preferredSize(contentSize)
-                    ) {}
-                }
-            }
-        }
-
-        assertTrue(latch.await(1, TimeUnit.SECONDS))
-        assertEquals(IntSize(contentSize.toIntPx(), contentSize.toIntPx()), size.value)
-    }
-
-    @Test
-    fun testWrapContentSize_wrapsContent_whenMeasuredWithInfiniteConstraints() = with(density) {
-        val sizeDp = 50.dp
-        val size = sizeDp.toIntPx()
-
-        val positionedLatch = CountDownLatch(2)
-        val alignSize = Ref<IntSize>()
-        val alignPosition = Ref<Offset>()
-        val childSize = Ref<IntSize>()
-        val childPosition = Ref<Offset>()
-        show {
-            Layout(
-                content = {
-                    Container(
-                        Modifier.saveLayoutInfo(alignSize, alignPosition, positionedLatch)
-                    ) {
-                        Container(
-                            Modifier.wrapContentSize(Alignment.BottomEnd)
-                                .preferredSize(sizeDp)
-                                .saveLayoutInfo(childSize, childPosition, positionedLatch)
-                        ) {
-                        }
-                    }
-                },
-                measureBlock = { measurables, constraints ->
-                    val placeable = measurables.first().measure(Constraints())
-                    layout(constraints.maxWidth, constraints.maxHeight) {
-                        placeable.placeRelative(0, 0)
-                    }
-                }
-            )
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        val root = findOwnerView()
-        waitForDraw(root)
-
-        assertEquals(IntSize(size, size), alignSize.value)
-        assertEquals(Offset(0f, 0f), alignPosition.value)
-        assertEquals(IntSize(size, size), childSize.value)
-        assertEquals(Offset(0f, 0f), childPosition.value)
-    }
-
-    @Test
-    fun testWrapContentSize_respectsMinConstraints() = with(density) {
-        val sizeDp = 50.dp
-        val size = sizeDp.toIntPx()
-        val doubleSizeDp = sizeDp * 2
-        val doubleSize = doubleSizeDp.toIntPx()
-
-        val positionedLatch = CountDownLatch(2)
-        val wrapSize = Ref<IntSize>()
-        val childSize = Ref<IntSize>()
-        val childPosition = Ref<Offset>()
-        show {
-            Container(Modifier.wrapContentSize(Alignment.TopStart)) {
-                Layout(
-                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        wrapSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    },
-                    content = {
-                        Container(
-                            Modifier.wrapContentSize(Alignment.Center)
-                                .preferredSize(sizeDp)
-                                .saveLayoutInfo(childSize, childPosition, positionedLatch)
-                        ) {
-                        }
-                    },
-                    measureBlock = { measurables, incomingConstraints ->
-                        val measurable = measurables.first()
-                        val constraints = Constraints(
-                            minWidth = doubleSizeDp.toIntPx(),
-                            minHeight = doubleSizeDp.toIntPx()
-                        ).enforce(incomingConstraints)
-                        val placeable = measurable.measure(constraints)
-                        layout(placeable.width, placeable.height) {
-                            placeable.placeRelative(IntOffset.Zero)
-                        }
-                    }
-                )
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertEquals(IntSize(doubleSize, doubleSize), wrapSize.value)
-        assertEquals(IntSize(size, size), childSize.value)
-        assertEquals(
-            Offset(
-                ((doubleSize - size) / 2f).roundToInt().toFloat(),
-                ((doubleSize - size) / 2f).roundToInt().toFloat()
-            ),
-            childPosition.value
-        )
-    }
-
-    @Test
-    fun testWrapContentSize_unbounded() = with(density) {
-        val outerSize = 10f
-        val innerSize = 20f
-
-        val positionedLatch = CountDownLatch(4)
-        show {
-            Box(
-                Modifier.size(outerSize.toDp())
-                    .onGloballyPositioned {
-                        assertEquals(outerSize, it.size.width.toFloat())
-                        positionedLatch.countDown()
-                    }
-            ) {
-                Box(
-                    Modifier.wrapContentSize(Alignment.BottomEnd, unbounded = true)
-                        .size(innerSize.toDp())
-                        .onGloballyPositioned {
-                            assertEquals(
-                                Offset(outerSize - innerSize, outerSize - innerSize),
-                                it.positionInParent
-                            )
-                            positionedLatch.countDown()
-                        }
-                )
-                Box(
-                    Modifier.wrapContentWidth(Alignment.End, unbounded = true)
-                        .size(innerSize.toDp())
-                        .onGloballyPositioned {
-                            assertEquals(outerSize - innerSize, it.positionInParent.x)
-                            positionedLatch.countDown()
-                        }
-                )
-                Box(
-                    Modifier.wrapContentHeight(Alignment.Bottom, unbounded = true)
-                        .size(innerSize.toDp())
-                        .onGloballyPositioned {
-                            assertEquals(outerSize - innerSize, it.positionInParent.y)
-                            positionedLatch.countDown()
-                        }
-                )
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-    }
-
-    // TODO(popam): this should be unit test instead
-    @Test
-    fun testAlignmentCoordinates_evenSize() {
-        val size = IntSize(2, 2)
-        assertEquals(IntOffset(0, 0), Alignment.TopStart.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(1, 0), Alignment.TopCenter.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(2, 0), Alignment.TopEnd.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(0, 1), Alignment.CenterStart.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(1, 1), Alignment.Center.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(2, 1), Alignment.CenterEnd.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(0, 2), Alignment.BottomStart.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(1, 2), Alignment.BottomCenter.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(2, 2), Alignment.BottomEnd.align(Zero, size, LayoutDirection.Ltr))
-    }
-
-    // TODO(popam): this should be unit test instead
-    @Test
-    fun testAlignmentCoordinates_oddSize() {
-        val size = IntSize(3, 3)
-        assertEquals(IntOffset(0, 0), Alignment.TopStart.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(2, 0), Alignment.TopCenter.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(3, 0), Alignment.TopEnd.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(0, 2), Alignment.CenterStart.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(2, 2), Alignment.Center.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(3, 2), Alignment.CenterEnd.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(0, 3), Alignment.BottomStart.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(2, 3), Alignment.BottomCenter.align(Zero, size, LayoutDirection.Ltr))
-        assertEquals(IntOffset(3, 3), Alignment.BottomEnd.align(Zero, size, LayoutDirection.Ltr))
-    }
-
-    @Test
-    fun test2DAlignedModifier_hasCorrectIntrinsicMeasurements() = with(density) {
-        testIntrinsics(
-            @Composable {
-                Container(Modifier.wrapContentSize(Alignment.TopStart).aspectRatio(2f)) { }
-            }
-        ) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
-            // Min width.
-            assertEquals(0, minIntrinsicWidth(0))
-            assertEquals(25.dp.toIntPx() * 2, minIntrinsicWidth(25.dp.toIntPx()))
-            assertEquals(0.dp.toIntPx(), minIntrinsicWidth(Constraints.Infinity))
-
-            // Min height.
-            assertEquals(0, minIntrinsicWidth(0))
-            assertEquals((50.dp.toIntPx() / 2f).roundToInt(), minIntrinsicHeight(50.dp.toIntPx()))
-            assertEquals(0.dp.toIntPx(), minIntrinsicHeight(Constraints.Infinity))
-
-            // Max width.
-            assertEquals(0, minIntrinsicWidth(0))
-            assertEquals(25.dp.toIntPx() * 2, maxIntrinsicWidth(25.dp.toIntPx()))
-            assertEquals(0.dp.toIntPx(), maxIntrinsicWidth(Constraints.Infinity))
-
-            // Max height.
-            assertEquals(0, minIntrinsicWidth(0))
-            assertEquals((50.dp.toIntPx() / 2f).roundToInt(), maxIntrinsicHeight(50.dp.toIntPx()))
-            assertEquals(0.dp.toIntPx(), maxIntrinsicHeight(Constraints.Infinity))
-        }
-    }
-
-    @Test
-    fun test1DAlignedModifier_hasCorrectIntrinsicMeasurements() = with(density) {
-        testIntrinsics({
-            Container(
-                Modifier.wrapContentHeight(Alignment.CenterVertically)
-                    .aspectRatio(2f)
-            ) { }
-        }) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
-
-            // Min width.
-            assertEquals(0, minIntrinsicWidth(0))
-            assertEquals(25.dp.toIntPx() * 2, minIntrinsicWidth(25.dp.toIntPx()))
-            assertEquals(0.dp.toIntPx(), minIntrinsicWidth(Constraints.Infinity))
-
-            // Min height.
-            assertEquals(0, minIntrinsicWidth(0))
-            assertEquals((50.dp.toIntPx() / 2f).roundToInt(), minIntrinsicHeight(50.dp.toIntPx()))
-            assertEquals(0.dp.toIntPx(), minIntrinsicHeight(Constraints.Infinity))
-
-            // Max width.
-            assertEquals(0, minIntrinsicWidth(0))
-            assertEquals(25.dp.toIntPx() * 2, maxIntrinsicWidth(25.dp.toIntPx()))
-            assertEquals(0.dp.toIntPx(), maxIntrinsicWidth(Constraints.Infinity))
-
-            // Max height.
-            assertEquals(0, minIntrinsicWidth(0))
-            assertEquals((50.dp.toIntPx() / 2f).roundToInt(), maxIntrinsicHeight(50.dp.toIntPx()))
-            assertEquals(0.dp.toIntPx(), maxIntrinsicHeight(Constraints.Infinity))
-        }
-    }
-
-    @Test
-    fun testAlignedModifier_alignsCorrectly_whenOddDimensions_endAligned() = with(density) {
-        // Given a 100 x 100 pixel container, we want to make sure that when aligning a 1 x 1 pixel
-        // child to both ends (bottom, and right) we correctly position children at the last
-        // possible pixel, and avoid rounding issues. Previously we first centered the coordinates,
-        // and then aligned after, so the maths would actually be (99 / 2) * 2, which incorrectly
-        // ends up at 100 (Int rounds up) - so the last pixels in both directions just wouldn't
-        // be visible.
-        val parentSize = 100.toDp()
-        val childSizeDp = 1.toDp()
-        val childSizeIpx = childSizeDp.toIntPx()
-
-        val positionedLatch = CountDownLatch(2)
-        val alignSize = Ref<IntSize>()
-        val alignPosition = Ref<Offset>()
-        val childSize = Ref<IntSize>()
-        val childPosition = Ref<Offset>()
-        show {
-            Layout(
-                content = {
-                    Container(
-                        Modifier.preferredSize(parentSize)
-                            .saveLayoutInfo(alignSize, alignPosition, positionedLatch)
-                    ) {
-                        Container(
-                            Modifier.fillMaxSize()
-                                .wrapContentSize(Alignment.BottomEnd)
-                                .preferredSize(childSizeDp)
-                                .saveLayoutInfo(childSize, childPosition, positionedLatch)
-                        ) {
-                        }
-                    }
-                },
-                measureBlock = { measurables, constraints ->
-                    val placeable = measurables.first().measure(Constraints())
-                    layout(constraints.maxWidth, constraints.maxHeight) {
-                        placeable.placeRelative(0, 0)
-                    }
-                }
-            )
-        }
-        positionedLatch.await(1, TimeUnit.SECONDS)
-
-        val root = findOwnerView()
-        waitForDraw(root)
-
-        assertEquals(IntSize(childSizeIpx, childSizeIpx), childSize.value)
-        assertEquals(
-            Offset(
-                (alignSize.value!!.width - childSizeIpx).toFloat(),
-                (alignSize.value!!.height - childSizeIpx).toFloat()
-            ),
-            childPosition.value
-        )
-    }
-}
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutDirectionModifierTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutDirectionModifierTest.kt
deleted file mode 100644
index c0fa4a1..0000000
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutDirectionModifierTest.kt
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.layout
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Providers
-import androidx.compose.runtime.emptyContent
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.node.Ref
-import androidx.compose.ui.platform.AmbientLayoutDirection
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.test.filters.SmallTest
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNotNull
-import org.junit.Assert.assertTrue
-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
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class LayoutDirectionModifierTest : LayoutTest() {
-
-    @Test
-    fun testModifiedLayoutDirection_inMeasureScope() {
-        val latch = CountDownLatch(1)
-        val resultLayoutDirection = Ref<LayoutDirection>()
-
-        show {
-            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
-                Layout(content = @Composable {}) { _, _ ->
-                    resultLayoutDirection.value = layoutDirection
-                    latch.countDown()
-                    layout(0, 0) {}
-                }
-            }
-        }
-
-        assertTrue(latch.await(1, TimeUnit.SECONDS))
-        assertTrue(LayoutDirection.Rtl == resultLayoutDirection.value)
-    }
-
-    @Test
-    fun testModifiedLayoutDirection_inIntrinsicsMeasure() {
-        val latch = CountDownLatch(1)
-        var resultLayoutDirection: LayoutDirection? = null
-
-        show {
-            @OptIn(ExperimentalLayout::class)
-            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
-                Layout(
-                    content = @Composable {},
-                    modifier = Modifier.preferredWidth(IntrinsicSize.Max),
-                    minIntrinsicWidthMeasureBlock = { _, _ -> 0 },
-                    minIntrinsicHeightMeasureBlock = { _, _ -> 0 },
-                    maxIntrinsicWidthMeasureBlock = { _, _ ->
-                        resultLayoutDirection = this.layoutDirection
-                        latch.countDown()
-                        0
-                    },
-                    maxIntrinsicHeightMeasureBlock = { _, _ -> 0 }
-                ) { _, _ ->
-                    layout(0, 0) {}
-                }
-            }
-        }
-
-        assertTrue(latch.await(1, TimeUnit.SECONDS))
-        assertNotNull(resultLayoutDirection)
-        assertTrue(LayoutDirection.Rtl == resultLayoutDirection)
-    }
-
-    @Test
-    fun testRestoreLocaleLayoutDirection() {
-        val latch = CountDownLatch(1)
-        val resultLayoutDirection = Ref<LayoutDirection>()
-
-        show {
-            val initialLayoutDirection = AmbientLayoutDirection.current
-            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
-                Box {
-                    Providers(AmbientLayoutDirection provides initialLayoutDirection) {
-                        Layout(emptyContent()) { _, _ ->
-                            resultLayoutDirection.value = layoutDirection
-                            latch.countDown()
-                            layout(0, 0) {}
-                        }
-                    }
-                }
-            }
-        }
-
-        assertTrue(latch.await(1, TimeUnit.SECONDS))
-        assertEquals(LayoutDirection.Ltr, resultLayoutDirection.value)
-    }
-}
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutTest.kt
index 2fdccc7..bd875f3 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutTest.kt
@@ -36,7 +36,7 @@
 import androidx.compose.ui.node.Ref
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.AmbientDensity
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
@@ -95,22 +95,22 @@
         activityTestRule.runOnUiThread(runnable)
     }
 
-    internal fun findOwnerView(): View {
-        return findOwner(activity).view
+    internal fun findComposeView(): View {
+        return findViewRootForTest(activity).view
     }
 
-    internal fun findOwner(activity: Activity): AndroidOwner {
+    internal fun findViewRootForTest(activity: Activity): ViewRootForTest {
         val contentViewGroup = activity.findViewById<ViewGroup>(android.R.id.content)
-        return findOwner(contentViewGroup)!!
+        return findViewRootForTest(contentViewGroup)!!
     }
 
-    internal fun findOwner(parent: ViewGroup): AndroidOwner? {
+    internal fun findViewRootForTest(parent: ViewGroup): ViewRootForTest? {
         for (index in 0 until parent.childCount) {
             val child = parent.getChildAt(index)
-            if (child is AndroidOwner) {
+            if (child is ViewRootForTest) {
                 return child
             } else if (child is ViewGroup) {
-                val owner = findOwner(child)
+                val owner = findViewRootForTest(child)
                 if (owner != null) {
                     return owner
                 }
@@ -255,8 +255,8 @@
         val maxHeight: Dp = Dp.Infinity
     ) {
         init {
-            require(minWidth.isFinite()) { "Constraints#minWidth should be finite" }
-            require(minHeight.isFinite()) { "Constraints#minHeight should be finite" }
+            require(minWidth.isFinite) { "Constraints#minWidth should be finite" }
+            require(minHeight.isFinite) { "Constraints#minHeight should be finite" }
             require(!minWidth.value.isNaN()) { "Constraints#minWidth should not be NaN" }
             require(!maxWidth.value.isNaN()) { "Constraints#maxWidth should not be NaN" }
             require(!minHeight.value.isNaN()) { "Constraints#minHeight should not be NaN" }
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutOffsetTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/OffsetTest.kt
similarity index 90%
rename from compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutOffsetTest.kt
rename to compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/OffsetTest.kt
index fd818bd..4d70733 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutOffsetTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/OffsetTest.kt
@@ -34,6 +34,7 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -50,7 +51,7 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-class LayoutOffsetTest : LayoutTest() {
+class OffsetTest : LayoutTest() {
     @get:Rule
     val rule = createComposeRule()
 
@@ -198,7 +199,7 @@
             Box(
                 Modifier.testTag("box")
                     .wrapContentSize(Alignment.TopStart)
-                    .offset({ offsetX }, { offsetY })
+                    .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
                     .onGloballyPositioned { coordinates: LayoutCoordinates ->
                         positionX = coordinates.positionInRoot.x
                         positionY = coordinates.positionInRoot.y
@@ -217,11 +218,11 @@
     @Test
     fun offsetPx_positionIsModified_rtl() = with(density) {
         val containerWidth = 30.dp
-        val boxSize = 1f
-        val offsetX = 10f
-        val offsetY = 20f
-        var positionX = 0f
-        var positionY = 0f
+        val boxSize = 1
+        val offsetX = 10
+        val offsetY = 20
+        var positionX = 0
+        var positionY = 0
         rule.setContent {
             Providers((AmbientLayoutDirection provides LayoutDirection.Rtl)) {
                 Box(
@@ -229,10 +230,10 @@
                         .wrapContentSize(Alignment.TopEnd)
                         .preferredWidth(containerWidth)
                         .wrapContentSize(Alignment.TopStart)
-                        .offset({ offsetX }, { offsetY })
+                        .offset { IntOffset(offsetX, offsetY) }
                         .onGloballyPositioned { coordinates: LayoutCoordinates ->
-                            positionX = coordinates.positionInRoot.x
-                            positionY = coordinates.positionInRoot.y
+                            positionX = coordinates.positionInRoot.x.roundToInt()
+                            positionY = coordinates.positionInRoot.y.roundToInt()
                         }
                 ) {
                     // TODO(soboleva): this box should not be needed after b/154758475 is fixed.
@@ -244,7 +245,7 @@
         rule.onNodeWithTag("box").assertExists()
         rule.runOnIdle {
             Assert.assertEquals(
-                containerWidth.toIntPx() - offsetX.roundToInt() - boxSize,
+                containerWidth.toIntPx() - offsetX - boxSize,
                 positionX
             )
             Assert.assertEquals(offsetY, positionY)
@@ -253,18 +254,18 @@
 
     @Test
     fun absoluteOffsetPx_positionIsModified() = with(density) {
-        val offsetX = 10f
-        val offsetY = 20f
-        var positionX = 0f
-        var positionY = 0f
+        val offsetX = 10
+        val offsetY = 20
+        var positionX = 0
+        var positionY = 0
         rule.setContent {
             Box(
                 Modifier.testTag("box")
                     .wrapContentSize(Alignment.TopStart)
-                    .absoluteOffset({ offsetX }, { offsetY })
+                    .absoluteOffset { IntOffset(offsetX, offsetY) }
                     .onGloballyPositioned { coordinates: LayoutCoordinates ->
-                        positionX = coordinates.positionInRoot.x
-                        positionY = coordinates.positionInRoot.y
+                        positionX = coordinates.positionInRoot.x.roundToInt()
+                        positionY = coordinates.positionInRoot.y.roundToInt()
                     }
             ) {
             }
@@ -280,11 +281,11 @@
     @Test
     fun absoluteOffsetPx_positionIsModified_rtl() = with(density) {
         val containerWidth = 30.dp
-        val boxSize = 1f
-        val offsetX = 10f
-        val offsetY = 20f
-        var positionX = 0f
-        var positionY = 0f
+        val boxSize = 1
+        val offsetX = 10
+        val offsetY = 20
+        var positionX = 0
+        var positionY = 0
         rule.setContent {
             Providers((AmbientLayoutDirection provides LayoutDirection.Rtl)) {
                 Box(
@@ -292,10 +293,10 @@
                         .wrapContentSize(Alignment.TopEnd)
                         .preferredWidth(containerWidth)
                         .wrapContentSize(Alignment.TopStart)
-                        .absoluteOffset({ offsetX }, { offsetY })
+                        .absoluteOffset { IntOffset(offsetX, offsetY) }
                         .onGloballyPositioned { coordinates: LayoutCoordinates ->
-                            positionX = coordinates.positionInRoot.x
-                            positionY = coordinates.positionInRoot.y
+                            positionX = coordinates.positionInRoot.x.roundToInt()
+                            positionY = coordinates.positionInRoot.y.roundToInt()
                         }
                 ) {
                     // TODO(soboleva): this box should not be needed after b/154758475 is fixed.
@@ -307,7 +308,7 @@
         rule.onNodeWithTag("box").assertExists()
         rule.runOnIdle {
             Assert.assertEquals(
-                containerWidth.toIntPx() - boxSize + offsetX.roundToInt(),
+                containerWidth.toIntPx() - boxSize + offsetX,
                 positionX
             )
             Assert.assertEquals(offsetY, positionY)
@@ -338,20 +339,20 @@
 
     @Test
     fun testOffsetPxInspectableValue() {
-        val modifier = Modifier.offset({ 10.0f }, { 20.0f }) as InspectableValue
+        val modifier = Modifier.offset { IntOffset(10, 20) } as InspectableValue
         assertThat(modifier.nameFallback).isEqualTo("offset")
         assertThat(modifier.valueOverride).isNull()
         assertThat(modifier.inspectableElements.map { it.name }.asIterable())
-            .containsExactly("x", "y")
+            .containsExactly("offset")
     }
 
     @Test
     fun testAbsoluteOffsetPxInspectableValue() {
-        val modifier = Modifier.absoluteOffset({ 10.0f }, { 20.0f }) as InspectableValue
+        val modifier = Modifier.absoluteOffset { IntOffset(10, 20) } as InspectableValue
         assertThat(modifier.nameFallback).isEqualTo("absoluteOffset")
         assertThat(modifier.valueOverride).isNull()
         assertThat(modifier.inspectableElements.map { it.name }.asIterable())
-            .containsExactly("x", "y")
+            .containsExactly("offset")
     }
 
     @Test
@@ -362,7 +363,7 @@
             Box(
                 Modifier
                     .size(10.dp)
-                    .offset(x = { offset })
+                    .offset { IntOffset(offset.roundToInt(), 0) }
                     .drawBehind {
                         contentRedrawsCount ++
                     }
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/OnGloballyPositionedTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/OnGloballyPositionedTest.kt
deleted file mode 100644
index 22da4ae..0000000
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/OnGloballyPositionedTest.kt
+++ /dev/null
@@ -1,275 +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.foundation.layout
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.emptyContent
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.VerticalAlignmentLine
-import androidx.compose.ui.layout.positionInParent
-import androidx.compose.ui.layout.positionInRoot
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Assert
-import org.junit.Assert.assertTrue
-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
-import kotlin.math.min
-import kotlin.math.roundToInt
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class OnGloballyPositionedTest : LayoutTest() {
-
-    @Test
-    fun simplePadding() = with(density) {
-        val paddingLeftPx = 100.0f
-        val paddingTopPx = 120.0f
-        var realLeft: Float? = null
-        var realTop: Float? = null
-
-        val positionedLatch = CountDownLatch(1)
-        show {
-            Container(
-                Modifier.fillMaxSize()
-                    .padding(start = paddingLeftPx.toDp(), top = paddingTopPx.toDp())
-                    .onGloballyPositioned {
-                        realLeft = it.positionInParent.x
-                        realTop = it.positionInParent.y
-                        positionedLatch.countDown()
-                    }
-            ) {
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertThat(paddingLeftPx).isEqualTo(realLeft)
-        assertThat(paddingTopPx).isEqualTo(realTop)
-    }
-
-    @Test
-    fun nestedLayoutCoordinates() = with(density) {
-        val firstPaddingPx = 10f
-        val secondPaddingPx = 20f
-        val thirdPaddingPx = 30f
-        var gpCoordinates: LayoutCoordinates? = null
-        var childCoordinates: LayoutCoordinates? = null
-
-        val positionedLatch = CountDownLatch(2)
-        show {
-            Container(
-                Modifier.padding(start = firstPaddingPx.toDp()).then(
-                    Modifier.onGloballyPositioned {
-                        gpCoordinates = it
-                        positionedLatch.countDown()
-                    }
-                )
-            ) {
-                Container(Modifier.padding(start = secondPaddingPx.toDp())) {
-                    Container(
-                        Modifier.fillMaxSize()
-                            .padding(start = thirdPaddingPx.toDp())
-                            .onGloballyPositioned {
-                                childCoordinates = it
-                                positionedLatch.countDown()
-                            }
-                    ) {
-                    }
-                }
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        // global position
-        val gPos = childCoordinates!!.localToGlobal(Offset.Zero).x
-        assertThat(gPos).isEqualTo((firstPaddingPx + secondPaddingPx + thirdPaddingPx))
-        // Position in grandparent Px(value=50.0)
-        val gpPos = gpCoordinates!!.childToLocal(childCoordinates!!, Offset.Zero).x
-        assertThat(gpPos).isEqualTo((secondPaddingPx + thirdPaddingPx))
-        // local position
-        assertThat(childCoordinates!!.positionInParent.x).isEqualTo(thirdPaddingPx)
-    }
-
-    @Test
-    fun globalCoordinatesAreInActivityCoordinates() = with(density) {
-        val padding = 30
-        val localPosition = Offset.Zero
-        val framePadding = Offset(padding.toFloat(), padding.toFloat())
-        var realGlobalPosition: Offset? = null
-        var realLocalPosition: Offset? = null
-        var frameGlobalPosition: Offset? = null
-
-        val positionedLatch = CountDownLatch(1)
-        activityTestRule.runOnUiThread(object : Runnable {
-            override fun run() {
-                val frameLayout = ComposeView(activity)
-                frameLayout.setPadding(padding, padding, padding, padding)
-                activity.setContentView(frameLayout)
-
-                val position = IntArray(2)
-                frameLayout.getLocationOnScreen(position)
-                frameGlobalPosition = Offset(position[0].toFloat(), position[1].toFloat())
-
-                frameLayout.setContent {
-                    Container(
-                        Modifier.onGloballyPositioned {
-                            realGlobalPosition = it.localToGlobal(localPosition)
-                            realLocalPosition = it.globalToLocal(
-                                framePadding +
-                                    frameGlobalPosition!!
-                            )
-                            positionedLatch.countDown()
-                        },
-                        expanded = true,
-                        content = emptyContent()
-                    )
-                }
-            }
-        })
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        assertThat(realGlobalPosition).isEqualTo(frameGlobalPosition!! + framePadding)
-        assertThat(realLocalPosition).isEqualTo(localPosition)
-    }
-
-    @Test
-    fun justAddedOnPositionedCallbackFiredWithoutLayoutChanges() = with(density) {
-        val needCallback = mutableStateOf(false)
-
-        val positionedLatch = CountDownLatch(1)
-        show {
-            val modifier = if (needCallback.value) {
-                Modifier.onGloballyPositioned { positionedLatch.countDown() }
-            } else {
-                Modifier
-            }
-            Container(modifier, expanded = true) { }
-        }
-
-        activityTestRule.runOnUiThread { needCallback.value = true }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-    }
-
-    @Test
-    fun testRepositionTriggersCallback() {
-        val left = mutableStateOf(30.dp)
-        var realLeft: Float? = null
-
-        var positionedLatch = CountDownLatch(1)
-        show {
-            Box {
-                Container(
-                    Modifier.onGloballyPositioned {
-                        realLeft = it.positionInParent.x
-                        positionedLatch.countDown()
-                    }
-                        .fillMaxSize()
-                        .padding(start = left.value),
-                    content = emptyContent()
-                )
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        positionedLatch = CountDownLatch(1)
-        activityTestRule.runOnUiThread { left.value = 40.dp }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-        with(density) {
-            assertThat(realLeft).isEqualTo(40.dp.toPx())
-        }
-    }
-
-    @Test
-    fun testGrandParentRepositionTriggersChildrenCallback() {
-        // when we reposition any parent layout is causes the change in global
-        // position of all the children down the tree(for example during the scrolling).
-        // children should be able to react on this change.
-        val left = mutableStateOf(20.dp)
-        var realLeft: Float? = null
-        var positionedLatch = CountDownLatch(1)
-        show {
-            Box {
-                Offset(left) {
-                    Container(width = 10.dp, height = 10.dp) {
-                        Container(width = 10.dp, height = 10.dp) {
-                            Container(
-                                Modifier.onGloballyPositioned {
-                                    realLeft = it.positionInRoot.x
-                                    positionedLatch.countDown()
-                                },
-                                width = 10.dp,
-                                height = 10.dp
-                            ) {
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-
-        positionedLatch = CountDownLatch(1)
-        activityTestRule.runOnUiThread { left.value = 40.dp }
-
-        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
-        with(density) {
-            assertThat(realLeft).isEqualTo(40.dp.toPx())
-        }
-    }
-
-    @Test
-    fun testAlignmentLinesArePresent() {
-        val latch = CountDownLatch(1)
-        val line = VerticalAlignmentLine(::min)
-        val lineValue = 10
-        show {
-            val onPositioned = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
-                Assert.assertEquals(1, coordinates.providedAlignmentLines.size)
-                Assert.assertEquals(lineValue, coordinates[line])
-                latch.countDown()
-            }
-            Layout(modifier = onPositioned, content = { }) { _, _ ->
-                layout(0, 0, mapOf(line to lineValue)) { }
-            }
-        }
-        assertTrue(latch.await(1, TimeUnit.SECONDS))
-    }
-
-    @Composable
-    private fun Offset(sizeModel: State<Dp>, content: @Composable () -> Unit) {
-        // simple copy of Padding which doesn't recompose when the size changes
-        Layout(content) { measurables, constraints ->
-            layout(constraints.maxWidth, constraints.maxHeight) {
-                measurables.first().measure(constraints)
-                    .placeRelative(sizeModel.value.toPx().roundToInt(), 0)
-            }
-        }
-    }
-}
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutPaddingTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/PaddingTest.kt
similarity index 98%
rename from compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutPaddingTest.kt
rename to compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/PaddingTest.kt
index 06c69bc..4f66539 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutPaddingTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/PaddingTest.kt
@@ -50,7 +50,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class LayoutPaddingTest : LayoutTest() {
+class PaddingTest : LayoutTest() {
 
     @Before
     fun before() {
@@ -298,7 +298,7 @@
             }
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val rootWidth = root.width
@@ -361,7 +361,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
         val rootWidth = root.width
 
@@ -451,7 +451,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val innerSize = (size - paddingPx * 2)
@@ -499,7 +499,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val paddingLeft = left.toIntPx()
@@ -553,7 +553,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(IntSize(0, 0), childSize)
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
index bd823de..b9a80bf 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
@@ -109,7 +109,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(IntSize(size, size), childSize[0])
@@ -161,7 +161,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
         val rootWidth = root.width
 
@@ -218,7 +218,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(IntSize(childrenWidth, childrenHeight), childSize[0])
@@ -263,7 +263,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(IntSize(size, size), childSize[0])
@@ -315,7 +315,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
         val rootHeight = root.height
 
@@ -369,7 +369,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(IntSize(childrenWidth, childrenHeight), childSize[0])
@@ -624,7 +624,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(IntSize(size, root.height), childSize[0])
@@ -682,7 +682,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
         val rootHeight = root.height
 
@@ -755,7 +755,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
         val rootHeight = root.height
 
@@ -976,7 +976,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(IntSize(root.width, size), childSize[0])
@@ -1035,7 +1035,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
         val rootWidth = root.width
 
@@ -1104,7 +1104,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
         val rootWidth = root.width
 
@@ -1286,7 +1286,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1316,7 +1316,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1355,7 +1355,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1385,7 +1385,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1415,7 +1415,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1448,7 +1448,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1490,7 +1490,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1523,7 +1523,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1556,7 +1556,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1589,7 +1589,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1638,7 +1638,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1756,7 +1756,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1786,7 +1786,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1825,7 +1825,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1855,7 +1855,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1885,7 +1885,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1918,7 +1918,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1960,7 +1960,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -1993,7 +1993,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -2026,7 +2026,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -2059,7 +2059,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -2109,7 +2109,7 @@
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -2244,7 +2244,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(Offset(0f, 0f), childPosition[0])
@@ -2290,7 +2290,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(Offset((root.width - size.toFloat() * 3), 0f), childPosition[0])
@@ -2336,7 +2336,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val extraSpace = root.width - size * 3
@@ -2392,7 +2392,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.width - size.toFloat() * 3f) / 4f
@@ -2447,7 +2447,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.width - size.toFloat() * 3) / 2
@@ -2500,7 +2500,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.width.toFloat() - size * 3) / 3
@@ -2705,7 +2705,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(Offset(0f, 0f), childPosition[0])
@@ -2751,7 +2751,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(Offset(0f, (root.height - size.toFloat() * 3)), childPosition[0])
@@ -2797,7 +2797,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val extraSpace = root.height - size * 3f
@@ -2856,7 +2856,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.height - size.toFloat() * 3) / 4
@@ -2920,7 +2920,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.height - size.toFloat() * 3f) / 2f
@@ -2973,7 +2973,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.height - size.toFloat() * 3f) / 3f
@@ -4047,7 +4047,7 @@
         }
 
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
         val rootWidth = root.width
 
@@ -4096,7 +4096,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val extraSpace = root.width - size * 3
@@ -4152,7 +4152,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.width - size.toFloat() * 3f) / 4f
@@ -4207,7 +4207,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.width - size.toFloat() * 3) / 2
@@ -4260,7 +4260,7 @@
 
         calculateChildPositions(childPosition, parentLayoutCoordinates, childLayoutCoordinates)
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.width.toFloat() - size * 3) / 3
@@ -4388,7 +4388,7 @@
         }
 
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
         val rootWidth = root.width
 
@@ -4475,7 +4475,7 @@
         }
 
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
         val rootWidth = root.width
 
@@ -4535,7 +4535,7 @@
             childLayoutCoordinates
         )
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(Offset(0f, 0f), childPosition[0])
@@ -4590,7 +4590,7 @@
             childLayoutCoordinates
         )
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(Offset(0f, 0f), childPosition[0])
@@ -4642,7 +4642,7 @@
             childLayoutCoordinates
         )
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -4703,7 +4703,7 @@
             childLayoutCoordinates
         )
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         assertEquals(
@@ -4761,7 +4761,7 @@
             childLayoutCoordinates
         )
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val extraSpace = root.width - size * 3
@@ -4832,7 +4832,7 @@
             childLayoutCoordinates
         )
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val extraSpace = root.width - size * 3
@@ -4900,7 +4900,7 @@
             childLayoutCoordinates
         )
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.width - size.toFloat() * 3f) / 4f
@@ -4969,7 +4969,7 @@
                 childLayoutCoordinates
             )
 
-            val root = findOwnerView()
+            val root = findComposeView()
             waitForDraw(root)
 
             val gap = (root.width - size.toFloat() * 3f) / 4f
@@ -5034,7 +5034,7 @@
             childLayoutCoordinates
         )
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.width - size.toFloat() * 3) / 2
@@ -5101,7 +5101,7 @@
                 childLayoutCoordinates
             )
 
-            val root = findOwnerView()
+            val root = findComposeView()
             waitForDraw(root)
 
             val gap = (root.width - size.toFloat() * 3) / 2
@@ -5163,7 +5163,7 @@
             childLayoutCoordinates
         )
 
-        val root = findOwnerView()
+        val root = findComposeView()
         waitForDraw(root)
 
         val gap = (root.width.toFloat() - size * 3) / 3
@@ -5233,7 +5233,7 @@
                 childLayoutCoordinates
             )
 
-            val root = findOwnerView()
+            val root = findComposeView()
             waitForDraw(root)
 
             val gap = (root.width.toFloat() - size * 3) / 3
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutSizeTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
similarity index 75%
rename from compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutSizeTest.kt
rename to compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
index 6e71283..7041c6d 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutSizeTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
@@ -17,6 +17,8 @@
 package androidx.compose.foundation.layout
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.emptyContent
+import androidx.compose.runtime.Providers
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.Modifier
@@ -25,13 +27,18 @@
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.node.Ref
 import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.positionInParent
+import androidx.compose.ui.platform.AmbientLayoutDirection
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.ValueElement
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
+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.ui.unit.enforce
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
@@ -43,10 +50,11 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
+import kotlin.math.roundToInt
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class LayoutSizeTest : LayoutTest() {
+class SizeTest : LayoutTest() {
 
     @Before
     fun before() {
@@ -1283,10 +1291,16 @@
         expectedConstraints: Constraints
     ) {
         val latch = CountDownLatch(1)
+        // Capture constraints and assert on test thread
+        var actualConstraints: Constraints? = null
+        // Clear contents before each test so that we don't recompose the WithConstraints call;
+        // doing so would recompose the old subcomposition with old constraints in the presence of
+        // new content before the measurement performs explicit composition the new constraints.
+        show(emptyContent())
         show {
             Layout({
                 WithConstraints(modifier) {
-                    assertEquals(expectedConstraints, constraints)
+                    actualConstraints = constraints
                     latch.countDown()
                 }
             }) { measurables, _ ->
@@ -1295,6 +1309,7 @@
             }
         }
         assertTrue(latch.await(1, TimeUnit.SECONDS))
+        assertEquals(expectedConstraints, actualConstraints)
     }
 
     private fun verifyIntrinsicMeasurements(expandedModifier: Modifier) = with(density) {
@@ -1322,4 +1337,411 @@
             assertEquals(40, maxIntrinsicHeight(Constraints.Infinity))
         }
     }
+    @Test
+    fun test2DWrapContentSize() = with(density) {
+        val sizeDp = 50.dp
+        val size = sizeDp.toIntPx()
+
+        val positionedLatch = CountDownLatch(2)
+        val alignSize = Ref<IntSize>()
+        val alignPosition = Ref<Offset>()
+        val childSize = Ref<IntSize>()
+        val childPosition = Ref<Offset>()
+        show {
+            Container(Modifier.saveLayoutInfo(alignSize, alignPosition, positionedLatch)) {
+                Container(
+                    Modifier.fillMaxSize()
+                        .wrapContentSize(Alignment.BottomEnd)
+                        .preferredSize(sizeDp)
+                        .saveLayoutInfo(childSize, childPosition, positionedLatch)
+                ) {
+                }
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        val root = findComposeView()
+        waitForDraw(root)
+
+        assertEquals(IntSize(root.width, root.height), alignSize.value)
+        assertEquals(Offset(0f, 0f), alignPosition.value)
+        assertEquals(IntSize(size, size), childSize.value)
+        assertEquals(
+            Offset(root.width - size.toFloat(), root.height - size.toFloat()),
+            childPosition.value
+        )
+    }
+
+    @Test
+    fun test1DWrapContentSize() = with(density) {
+        val sizeDp = 50.dp
+        val size = sizeDp.toIntPx()
+
+        val positionedLatch = CountDownLatch(2)
+        val alignSize = Ref<IntSize>()
+        val alignPosition = Ref<Offset>()
+        val childSize = Ref<IntSize>()
+        val childPosition = Ref<Offset>()
+        show {
+            Container(
+                Modifier.saveLayoutInfo(
+                    size = alignSize,
+                    position = alignPosition,
+                    positionedLatch = positionedLatch
+                )
+            ) {
+                Container(
+                    Modifier.fillMaxSize()
+                        .wrapContentWidth(Alignment.End)
+                        .preferredWidth(sizeDp)
+                        .saveLayoutInfo(childSize, childPosition, positionedLatch)
+                ) {
+                }
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        val root = findComposeView()
+        waitForDraw(root)
+
+        assertEquals(IntSize(root.width, root.height), alignSize.value)
+        assertEquals(Offset(0f, 0f), alignPosition.value)
+        assertEquals(IntSize(size, root.height), childSize.value)
+        assertEquals(Offset(root.width - size.toFloat(), 0f), childPosition.value)
+    }
+
+    @Test
+    fun testWrapContentSize_rtl() = with(density) {
+        val sizeDp = 200.toDp()
+        val size = sizeDp.toIntPx()
+
+        val positionedLatch = CountDownLatch(3)
+        val childSize = Array(3) { Ref<IntSize>() }
+        val childPosition = Array(3) { Ref<Offset>() }
+        show {
+            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
+                Box(Modifier.fillMaxSize()) {
+                    Box(Modifier.fillMaxSize().wrapContentSize(Alignment.TopStart)) {
+                        Box(
+                            Modifier.preferredSize(sizeDp)
+                                .saveLayoutInfo(childSize[0], childPosition[0], positionedLatch)
+                        ) {
+                        }
+                    }
+                    Box(Modifier.fillMaxSize().wrapContentHeight(Alignment.CenterVertically)) {
+                        Box(
+                            Modifier.preferredSize(sizeDp)
+                                .saveLayoutInfo(childSize[1], childPosition[1], positionedLatch)
+                        ) {
+                        }
+                    }
+                    Box(Modifier.fillMaxSize().wrapContentSize(Alignment.BottomEnd)) {
+                        Box(
+                            Modifier.preferredSize(sizeDp)
+                                .saveLayoutInfo(childSize[2], childPosition[2], positionedLatch)
+                        ) {
+                        }
+                    }
+                }
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        val root = findComposeView()
+        waitForDraw(root)
+
+        assertEquals(
+            Offset((root.width - size).toFloat(), 0f),
+            childPosition[0].value
+        )
+        assertEquals(
+            Offset(
+                (root.width - size).toFloat(),
+                ((root.height - size) / 2).toFloat()
+            ),
+            childPosition[1].value
+        )
+        assertEquals(
+            Offset(0f, (root.height - size).toFloat()),
+            childPosition[2].value
+        )
+    }
+
+    @Test
+    fun testModifier_wrapsContent() = with(density) {
+        val contentSize = 50.dp
+        val size = Ref<IntSize>()
+        val latch = CountDownLatch(1)
+        show {
+            Container {
+                Container(Modifier.saveLayoutInfo(size, Ref(), latch)) {
+                    Container(
+                        Modifier.wrapContentSize(Alignment.TopStart)
+                            .preferredSize(contentSize)
+                    ) {}
+                }
+            }
+        }
+
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        assertEquals(IntSize(contentSize.toIntPx(), contentSize.toIntPx()), size.value)
+    }
+
+    @Test
+    fun testWrapContentSize_wrapsContent_whenMeasuredWithInfiniteConstraints() = with(density) {
+        val sizeDp = 50.dp
+        val size = sizeDp.toIntPx()
+
+        val positionedLatch = CountDownLatch(2)
+        val alignSize = Ref<IntSize>()
+        val alignPosition = Ref<Offset>()
+        val childSize = Ref<IntSize>()
+        val childPosition = Ref<Offset>()
+        show {
+            Layout(
+                content = {
+                    Container(
+                        Modifier.saveLayoutInfo(alignSize, alignPosition, positionedLatch)
+                    ) {
+                        Container(
+                            Modifier.wrapContentSize(Alignment.BottomEnd)
+                                .preferredSize(sizeDp)
+                                .saveLayoutInfo(childSize, childPosition, positionedLatch)
+                        ) {
+                        }
+                    }
+                },
+                measureBlock = { measurables, constraints ->
+                    val placeable = measurables.first().measure(Constraints())
+                    layout(constraints.maxWidth, constraints.maxHeight) {
+                        placeable.placeRelative(0, 0)
+                    }
+                }
+            )
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        val root = findComposeView()
+        waitForDraw(root)
+
+        assertEquals(IntSize(size, size), alignSize.value)
+        assertEquals(Offset(0f, 0f), alignPosition.value)
+        assertEquals(IntSize(size, size), childSize.value)
+        assertEquals(Offset(0f, 0f), childPosition.value)
+    }
+
+    @Test
+    fun testWrapContentSize_respectsMinConstraints() = with(density) {
+        val sizeDp = 50.dp
+        val size = sizeDp.toIntPx()
+        val doubleSizeDp = sizeDp * 2
+        val doubleSize = doubleSizeDp.toIntPx()
+
+        val positionedLatch = CountDownLatch(2)
+        val wrapSize = Ref<IntSize>()
+        val childSize = Ref<IntSize>()
+        val childPosition = Ref<Offset>()
+        show {
+            Container(Modifier.wrapContentSize(Alignment.TopStart)) {
+                Layout(
+                    modifier = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
+                        wrapSize.value = coordinates.size
+                        positionedLatch.countDown()
+                    },
+                    content = {
+                        Container(
+                            Modifier.wrapContentSize(Alignment.Center)
+                                .preferredSize(sizeDp)
+                                .saveLayoutInfo(childSize, childPosition, positionedLatch)
+                        ) {
+                        }
+                    },
+                    measureBlock = { measurables, incomingConstraints ->
+                        val measurable = measurables.first()
+                        val constraints = Constraints(
+                            minWidth = doubleSizeDp.toIntPx(),
+                            minHeight = doubleSizeDp.toIntPx()
+                        ).enforce(incomingConstraints)
+                        val placeable = measurable.measure(constraints)
+                        layout(placeable.width, placeable.height) {
+                            placeable.placeRelative(IntOffset.Zero)
+                        }
+                    }
+                )
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        assertEquals(IntSize(doubleSize, doubleSize), wrapSize.value)
+        assertEquals(IntSize(size, size), childSize.value)
+        assertEquals(
+            Offset(
+                ((doubleSize - size) / 2f).roundToInt().toFloat(),
+                ((doubleSize - size) / 2f).roundToInt().toFloat()
+            ),
+            childPosition.value
+        )
+    }
+
+    @Test
+    fun testWrapContentSize_unbounded() = with(density) {
+        val outerSize = 10f
+        val innerSize = 20f
+
+        val positionedLatch = CountDownLatch(4)
+        show {
+            Box(
+                Modifier.size(outerSize.toDp())
+                    .onGloballyPositioned {
+                        assertEquals(outerSize, it.size.width.toFloat())
+                        positionedLatch.countDown()
+                    }
+            ) {
+                Box(
+                    Modifier.wrapContentSize(Alignment.BottomEnd, unbounded = true)
+                        .size(innerSize.toDp())
+                        .onGloballyPositioned {
+                            assertEquals(
+                                Offset(outerSize - innerSize, outerSize - innerSize),
+                                it.positionInParent
+                            )
+                            positionedLatch.countDown()
+                        }
+                )
+                Box(
+                    Modifier.wrapContentWidth(Alignment.End, unbounded = true)
+                        .size(innerSize.toDp())
+                        .onGloballyPositioned {
+                            assertEquals(outerSize - innerSize, it.positionInParent.x)
+                            positionedLatch.countDown()
+                        }
+                )
+                Box(
+                    Modifier.wrapContentHeight(Alignment.Bottom, unbounded = true)
+                        .size(innerSize.toDp())
+                        .onGloballyPositioned {
+                            assertEquals(outerSize - innerSize, it.positionInParent.y)
+                            positionedLatch.countDown()
+                        }
+                )
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+    }
+
+    @Test
+    fun test2DAlignedModifier_hasCorrectIntrinsicMeasurements() = with(density) {
+        testIntrinsics(
+            @Composable {
+                Container(Modifier.wrapContentSize(Alignment.TopStart).aspectRatio(2f)) { }
+            }
+        ) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
+            // Min width.
+            assertEquals(0, minIntrinsicWidth(0))
+            assertEquals(25.dp.toIntPx() * 2, minIntrinsicWidth(25.dp.toIntPx()))
+            assertEquals(0.dp.toIntPx(), minIntrinsicWidth(Constraints.Infinity))
+
+            // Min height.
+            assertEquals(0, minIntrinsicWidth(0))
+            assertEquals((50.dp.toIntPx() / 2f).roundToInt(), minIntrinsicHeight(50.dp.toIntPx()))
+            assertEquals(0.dp.toIntPx(), minIntrinsicHeight(Constraints.Infinity))
+
+            // Max width.
+            assertEquals(0, minIntrinsicWidth(0))
+            assertEquals(25.dp.toIntPx() * 2, maxIntrinsicWidth(25.dp.toIntPx()))
+            assertEquals(0.dp.toIntPx(), maxIntrinsicWidth(Constraints.Infinity))
+
+            // Max height.
+            assertEquals(0, minIntrinsicWidth(0))
+            assertEquals((50.dp.toIntPx() / 2f).roundToInt(), maxIntrinsicHeight(50.dp.toIntPx()))
+            assertEquals(0.dp.toIntPx(), maxIntrinsicHeight(Constraints.Infinity))
+        }
+    }
+
+    @Test
+    fun test1DAlignedModifier_hasCorrectIntrinsicMeasurements() = with(density) {
+        testIntrinsics({
+            Container(
+                Modifier.wrapContentHeight(Alignment.CenterVertically)
+                    .aspectRatio(2f)
+            ) { }
+        }) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
+
+            // Min width.
+            assertEquals(0, minIntrinsicWidth(0))
+            assertEquals(25.dp.toIntPx() * 2, minIntrinsicWidth(25.dp.toIntPx()))
+            assertEquals(0.dp.toIntPx(), minIntrinsicWidth(Constraints.Infinity))
+
+            // Min height.
+            assertEquals(0, minIntrinsicWidth(0))
+            assertEquals((50.dp.toIntPx() / 2f).roundToInt(), minIntrinsicHeight(50.dp.toIntPx()))
+            assertEquals(0.dp.toIntPx(), minIntrinsicHeight(Constraints.Infinity))
+
+            // Max width.
+            assertEquals(0, minIntrinsicWidth(0))
+            assertEquals(25.dp.toIntPx() * 2, maxIntrinsicWidth(25.dp.toIntPx()))
+            assertEquals(0.dp.toIntPx(), maxIntrinsicWidth(Constraints.Infinity))
+
+            // Max height.
+            assertEquals(0, minIntrinsicWidth(0))
+            assertEquals((50.dp.toIntPx() / 2f).roundToInt(), maxIntrinsicHeight(50.dp.toIntPx()))
+            assertEquals(0.dp.toIntPx(), maxIntrinsicHeight(Constraints.Infinity))
+        }
+    }
+
+    @Test
+    fun testAlignedModifier_alignsCorrectly_whenOddDimensions_endAligned() = with(density) {
+        // Given a 100 x 100 pixel container, we want to make sure that when aligning a 1 x 1 pixel
+        // child to both ends (bottom, and right) we correctly position children at the last
+        // possible pixel, and avoid rounding issues. Previously we first centered the coordinates,
+        // and then aligned after, so the maths would actually be (99 / 2) * 2, which incorrectly
+        // ends up at 100 (Int rounds up) - so the last pixels in both directions just wouldn't
+        // be visible.
+        val parentSize = 100.toDp()
+        val childSizeDp = 1.toDp()
+        val childSizeIpx = childSizeDp.toIntPx()
+
+        val positionedLatch = CountDownLatch(2)
+        val alignSize = Ref<IntSize>()
+        val alignPosition = Ref<Offset>()
+        val childSize = Ref<IntSize>()
+        val childPosition = Ref<Offset>()
+        show {
+            Layout(
+                content = {
+                    Container(
+                        Modifier.preferredSize(parentSize)
+                            .saveLayoutInfo(alignSize, alignPosition, positionedLatch)
+                    ) {
+                        Container(
+                            Modifier.fillMaxSize()
+                                .wrapContentSize(Alignment.BottomEnd)
+                                .preferredSize(childSizeDp)
+                                .saveLayoutInfo(childSize, childPosition, positionedLatch)
+                        ) {
+                        }
+                    }
+                },
+                measureBlock = { measurables, constraints ->
+                    val placeable = measurables.first().measure(Constraints())
+                    layout(constraints.maxWidth, constraints.maxHeight) {
+                        placeable.placeRelative(0, 0)
+                    }
+                }
+            )
+        }
+        positionedLatch.await(1, TimeUnit.SECONDS)
+
+        val root = findComposeView()
+        waitForDraw(root)
+
+        assertEquals(IntSize(childSizeIpx, childSizeIpx), childSize.value)
+        assertEquals(
+            Offset(
+                (alignSize.value!!.width - childSizeIpx).toFloat(),
+                (alignSize.value!!.height - childSizeIpx).toFloat()
+            ),
+            childPosition.value
+        )
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/TextLayoutDirectionModifierTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/TextLayoutDirectionModifierTest.kt
deleted file mode 100644
index bad57a1..0000000
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/TextLayoutDirectionModifierTest.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.layout
-
-import androidx.compose.foundation.text.BasicText
-import androidx.compose.foundation.text.BasicTextField
-import androidx.compose.runtime.Providers
-import androidx.compose.ui.platform.AmbientLayoutDirection
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.TextFieldValue
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class TextLayoutDirectionModifierTest : LayoutTest() {
-
-    @Test
-    fun test_CoreTextField_RtlLayoutDirection_changesDirectionTo_Rtl() {
-        val latch = CountDownLatch(1)
-        var layoutDirection: LayoutDirection? = null
-
-        show {
-            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
-                BasicTextField(
-                    value = TextFieldValue("..."),
-                    onValueChange = {},
-                    onTextLayout = { result ->
-                        layoutDirection = result.layoutInput.layoutDirection
-                        latch.countDown()
-                    }
-                )
-            }
-        }
-
-        assertThat(latch.await(1, TimeUnit.SECONDS)).isTrue()
-        assertThat(layoutDirection).isNotNull()
-        assertThat(layoutDirection!!).isEqualTo(LayoutDirection.Rtl)
-    }
-
-    @Test
-    fun test_CoreText_RtlLayoutDirection_changesDirectionTo_Rtl() {
-        val latch = CountDownLatch(1)
-        var layoutDirection: LayoutDirection? = null
-        show {
-            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
-                BasicText(
-                    text = AnnotatedString("..."),
-                    style = TextStyle.Default,
-                    onTextLayout = { result ->
-                        layoutDirection = result.layoutInput.layoutDirection
-                        latch.countDown()
-                    },
-                    softWrap = true,
-                    overflow = TextOverflow.Clip,
-                    maxLines = 1,
-                    inlineContent = mapOf()
-                )
-            }
-        }
-
-        assertThat(latch.await(1, TimeUnit.SECONDS)).isTrue()
-        assertThat(layoutDirection).isNotNull()
-        assertThat(layoutDirection!!).isEqualTo(LayoutDirection.Rtl)
-    }
-}
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 79958f7..6a7ef5d 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
@@ -32,6 +32,7 @@
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.isUnspecified
 import kotlin.math.max
 
 /**
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt
index 9589035..36f9646 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt
@@ -27,14 +27,14 @@
 import kotlin.math.roundToInt
 
 /**
- * Used to specify the arrangement of the layout's children in [Row] or [Column] in the main axis
- * direction (horizontal and vertical, respectively).
+ * Used to specify the arrangement of the layout's children in layouts like [Row] or [Column] in
+ * the main axis direction (horizontal and vertical, respectively).
  */
 @Immutable
 @OptIn(InternalLayoutApi::class)
 object Arrangement {
     /**
-     * Used to specify the horizontal arrangement of the layout's children in a [Row].
+     * Used to specify the horizontal arrangement of the layout's children in layouts like [Row].
      */
     @InternalLayoutApi
     @Immutable
@@ -45,7 +45,7 @@
         val spacing get() = 0.dp
 
         /**
-         * Horizontally places the layout children inside the [Row].
+         * Horizontally places the layout children.
          *
          * @param totalSize Available space that can be occupied by the children.
          * @param size An array of sizes of all children.
@@ -64,7 +64,7 @@
     }
 
     /**
-     * Used to specify the vertical arrangement of the layout's children in a [Column].
+     * Used to specify the vertical arrangement of the layout's children in layouts like [Column].
      */
     @InternalLayoutApi
     @Immutable
@@ -75,7 +75,7 @@
         val spacing get() = 0.dp
 
         /**
-         * Vertically places the layout children inside the [Column].
+         * Vertically places the layout children.
          *
          * @param totalSize Available space that can be occupied by the children.
          * @param size An array of sizes of all children.
@@ -91,8 +91,9 @@
     }
 
     /**
-     * Used to specify the horizontal arrangement of the layout's children in a [Row], or
-     * the vertical arrangement of the layout's children in a [Column].
+     * Used to specify the horizontal arrangement of the layout's children in horizontal layouts
+     * like [Row], or the vertical arrangement of the layout's children in vertical layouts like
+     * [Column].
      */
     @InternalLayoutApi
     @Immutable
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutAspectRatio.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
similarity index 96%
rename from compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutAspectRatio.kt
rename to compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
index 39c7330..55d6b1b 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutAspectRatio.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
@@ -29,7 +29,7 @@
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.satisfiedBy
+import androidx.compose.ui.unit.isSatisfiedBy
 import androidx.compose.ui.util.annotation.FloatRange
 import kotlin.math.roundToInt
 
@@ -158,7 +158,7 @@
             val height = (maxWidth / aspectRatio).roundToInt()
             if (height > 0) {
                 val size = IntSize(maxWidth, height)
-                if (!enforceConstraints || satisfiedBy(size)) {
+                if (!enforceConstraints || isSatisfiedBy(size)) {
                     return size
                 }
             }
@@ -172,7 +172,7 @@
             val width = (maxHeight * aspectRatio).roundToInt()
             if (width > 0) {
                 val size = IntSize(width, maxHeight)
-                if (!enforceConstraints || satisfiedBy(size)) {
+                if (!enforceConstraints || isSatisfiedBy(size)) {
                     return size
                 }
             }
@@ -185,7 +185,7 @@
         val height = (minWidth / aspectRatio).roundToInt()
         if (height > 0) {
             val size = IntSize(minWidth, height)
-            if (!enforceConstraints || satisfiedBy(size)) {
+            if (!enforceConstraints || isSatisfiedBy(size)) {
                 return size
             }
         }
@@ -197,7 +197,7 @@
         val width = (minHeight * aspectRatio).roundToInt()
         if (width > 0) {
             val size = IntSize(width, minHeight)
-            if (!enforceConstraints || satisfiedBy(size)) {
+            if (!enforceConstraints || isSatisfiedBy(size)) {
                 return size
             }
         }
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Flow.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Flow.kt
index bb2d576..c9a850f 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Flow.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Flow.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATION")
+
 package androidx.compose.foundation.layout
 
 import androidx.compose.runtime.Composable
@@ -34,10 +36,6 @@
  *
  * Note that just like [Row], flex values cannot be used with [FlowRow].
  *
- * Example usage:
- *
- * @sample androidx.compose.foundation.layout.samples.SimpleFlowRow
- *
  * @param mainAxisSize The size of the layout in the main axis direction.
  * @param mainAxisAlignment The alignment of each row's children in the main axis direction.
  * @param mainAxisSpacing The main axis spacing between the children of each row.
@@ -47,6 +45,7 @@
  */
 @ExperimentalLayout
 @Composable
+@Deprecated("FlowRow is deprecated and will be removed. Replace it with a custom layout.")
 fun FlowRow(
     mainAxisSize: SizeMode = SizeMode.Wrap,
     mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
@@ -74,10 +73,6 @@
  *
  * Note that just like [Column], flex values cannot be used with [FlowColumn].
  *
- * Example usage:
- *
- * @sample androidx.compose.foundation.layout.samples.SimpleFlowColumn
- *
  * @param mainAxisSize The size of the layout in the main axis direction.
  * @param mainAxisAlignment The alignment of each column's children in the main axis direction.
  * @param mainAxisSpacing The main axis spacing between the children of each column.
@@ -87,6 +82,7 @@
  */
 @ExperimentalLayout
 @Composable
+@Deprecated("FlowColumn is deprecated and will be removed. Replace it with a custom layout.")
 fun FlowColumn(
     mainAxisSize: SizeMode = SizeMode.Wrap,
     mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
@@ -111,6 +107,7 @@
 /**
  * Used to specify the alignment of a layout's children, in cross axis direction.
  */
+@Deprecated("Flow layouts are deprecated and will be removed. Replace them with a custom layout.")
 enum class FlowCrossAxisAlignment {
     /**
      * Place children such that their center is in the middle of the cross axis.
@@ -126,6 +123,7 @@
     End,
 }
 
+@Deprecated("Flow layouts are deprecated and will be removed. Replace them with a custom layout.")
 typealias FlowMainAxisAlignment = MainAxisAlignment
 
 /**
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutOffset.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Offset.kt
similarity index 81%
rename from compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutOffset.kt
rename to compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Offset.kt
index eaf2378..537efd3 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutOffset.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Offset.kt
@@ -30,6 +30,7 @@
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import kotlin.math.roundToInt
 
@@ -86,10 +87,11 @@
 )
 
 /**
- * Offset the content by ([x] px, [y] px). The offsets can be positive as well as non-positive.
+ * Offset the content by [offset] px. The offsets can be positive as well as non-positive.
  * Applying an offset only changes the position of the content, without interfering with
  * its size measurement.
- * This modifier is designed to be used for offsets that change, possibly due to user interactions.
+ * This modifier is designed to be used for offsets that change, possibly due to user interactions,
+ * the advantage being that it avoids recomposition when the offset is changing.
  * Note that, even if for convenience the API is accepting [Float] values, these will be rounded
  * to the closest integer value before applying the offset.
  * This modifier will automatically adjust the horizontal offset according to the layout direction.
@@ -100,37 +102,37 @@
  * Example usage:
  * @sample androidx.compose.foundation.layout.samples.OffsetPxModifier
  */
-fun Modifier.offset(
-    x: Density.() -> Float = { 0f },
-    y: Density.() -> Float = { 0f }
-) = this.then(
+fun Modifier.offset(offset: Density.() -> IntOffset) = this.then(
     OffsetPxModifier(
-        x = x,
-        y = y,
+        offset = offset,
         rtlAware = true,
         inspectorInfo = debugInspectorInfo {
             name = "offset"
-            properties["x"] = x
-            properties["y"] = y
+            properties["offset"] = offset
         }
     )
 )
 
 @Deprecated(
     "offsetPx was deprecated. Please use offset with lambda parameters instead.",
-    ReplaceWith("offset({x.value}, {y.value})", "androidx.compose.foundation.layout.offset")
+    ReplaceWith(
+        "offset({x.value}, {y.value})",
+        "androidx.compose.foundation.layout.offset",
+        "kotlin.math.roundToInt()"
+    )
 )
 @Suppress("ModifierInspectorInfo")
 fun Modifier.offsetPx(
     x: State<Float> = mutableStateOf(0f),
     y: State<Float> = mutableStateOf(0f)
-) = this.offset({ x.value }, { y.value })
+) = this.offset { IntOffset(x.value.roundToInt(), y.value.roundToInt()) }
 
 /**
- * Offset the content by ([x] px, [y] px). The offsets can be positive as well as non-positive.
+ * Offset the content by [offset] px. The offsets can be positive as well as non-positive.
  * Applying an offset only changes the position of the content, without interfering with
  * its size measurement.
- * This modifier is designed to be used for offsets that change, possibly due to user interactions.
+ * This modifier is designed to be used for offsets that change, possibly due to user interactions,
+ * the advantage being that it avoids recomposition when the offset is changing.
  * Note that, even if for convenience the API is accepting [Float] values, these will be rounded
  * to the closest integer value before applying the offset.
  * This modifier will not consider layout direction when calculating the position of the content.
@@ -142,17 +144,14 @@
  * @sample androidx.compose.foundation.layout.samples.AbsoluteOffsetPxModifier
  */
 fun Modifier.absoluteOffset(
-    x: Density.() -> Float = { 0f },
-    y: Density.() -> Float = { 0f }
+    offset: Density.() -> IntOffset
 ) = this.then(
     OffsetPxModifier(
-        x = x,
-        y = y,
+        offset = offset,
         rtlAware = false,
         inspectorInfo = debugInspectorInfo {
             name = "absoluteOffset"
-            properties["x"] = x
-            properties["y"] = y
+            properties["offset"] = offset
         }
     )
 )
@@ -160,15 +159,16 @@
 @Deprecated(
     "absoluteOffsetPx was deprecated. Please use absoluteOffset with lambda parameters instead.",
     ReplaceWith(
-        "absoluteOffset({x.value}, {y.value})",
-        "androidx.compose.foundation.layout.absoluteOffset"
+        "absoluteOffset({x.value.roundToInt()}, {y.value.roundToInt()})",
+        "androidx.compose.foundation.layout.absoluteOffset",
+        "kotlin.math.roundToInt"
     )
 )
 @Suppress("ModifierInspectorInfo")
 fun Modifier.absoluteOffsetPx(
     x: State<Float> = mutableStateOf(0f),
     y: State<Float> = mutableStateOf(0f)
-) = this.absoluteOffset({ x.value }, { y.value })
+) = this.absoluteOffset { IntOffset(x.value.roundToInt(), y.value.roundToInt()) }
 
 private class OffsetModifier(
     val x: Dp,
@@ -210,8 +210,7 @@
 }
 
 private class OffsetPxModifier(
-    val x: Density.() -> Float,
-    val y: Density.() -> Float,
+    val offset: Density.() -> IntOffset,
     val rtlAware: Boolean,
     inspectorInfo: InspectorInfo.() -> Unit
 ) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
@@ -221,10 +220,11 @@
     ): MeasureResult {
         val placeable = measurable.measure(constraints)
         return layout(placeable.width, placeable.height) {
+            val offsetValue = offset()
             if (rtlAware) {
-                placeable.placeRelativeWithLayer(x().roundToInt(), y().roundToInt())
+                placeable.placeRelativeWithLayer(offsetValue.x, offsetValue.y)
             } else {
-                placeable.placeWithLayer(x().roundToInt(), y().roundToInt())
+                placeable.placeWithLayer(offsetValue.x, offsetValue.y)
             }
         }
     }
@@ -233,17 +233,15 @@
         if (this === other) return true
         val otherModifier = other as? OffsetPxModifier ?: return false
 
-        return x == otherModifier.x &&
-            y == otherModifier.y &&
+        return offset == otherModifier.offset &&
             rtlAware == otherModifier.rtlAware
     }
 
     override fun hashCode(): Int {
-        var result = x.hashCode()
-        result = 31 * result + y.hashCode()
+        var result = offset.hashCode()
         result = 31 * result + rtlAware.hashCode()
         return result
     }
 
-    override fun toString(): String = "OffsetPxModifier(x=$x, y=$y, rtlAware=$rtlAware)"
+    override fun toString(): String = "OffsetPxModifier(offset=$offset, rtlAware=$rtlAware)"
 }
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutPadding.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
similarity index 100%
rename from compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutPadding.kt
rename to compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutSize.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
similarity index 100%
rename from compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutSize.kt
rename to compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt
index 4ee0c5f..6db4f41 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt
@@ -24,8 +24,8 @@
 import androidx.compose.ui.unit.hasFixedWidth
 
 /**
- * Component that represents an empty space layout, whose size can be defined using the [LayoutWidth],
- * [LayoutHeight] and [LayoutSize] modifiers.
+ * Component that represents an empty space layout, whose size can be defined using [Modifier.width],
+ * [Modifier.height] and [Modifier.size] modifiers.
  *
  * @sample androidx.compose.foundation.layout.samples.SpacerExample
  *
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index ddb6503..6cdc91d 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -137,7 +137,7 @@
     method public static androidx.compose.ui.Modifier verticalScroll(androidx.compose.ui.Modifier, androidx.compose.foundation.ScrollState state, optional boolean enabled, optional boolean reverseScrolling);
   }
 
-  @androidx.compose.runtime.Stable public final class ScrollState {
+  @androidx.compose.runtime.Stable public final class ScrollState implements androidx.compose.foundation.gestures.Scrollable {
     ctor public ScrollState(float initial, androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock, androidx.compose.foundation.InteractionState? interactionState);
     method public float getMaxValue();
     method public float getValue();
@@ -165,6 +165,7 @@
     method public String getNotSelected();
     method public String getSelected();
     method public String getTemplatePercent();
+    method public String getToggle();
     method public String getUnchecked();
     property public final String Checked;
     property public final String InProgress;
@@ -172,6 +173,7 @@
     property public final String NotSelected;
     property public final String Selected;
     property public final String TemplatePercent;
+    property public final String Toggle;
     property public final String Unchecked;
     field public static final androidx.compose.foundation.Strings INSTANCE;
   }
@@ -224,6 +226,10 @@
     method public static void fling(androidx.compose.animation.core.AnimatedFloat, float startVelocity, androidx.compose.foundation.animation.FlingConfig config, optional kotlin.jvm.functions.Function3<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,? super java.lang.Float,kotlin.Unit>? onAnimationEnd);
   }
 
+  public final class SmoothScrollKt {
+    method public static suspend Object? smoothScrollBy(androidx.compose.foundation.gestures.Scrollable, float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
+  }
+
 }
 
 package androidx.compose.foundation.gestures {
@@ -232,18 +238,18 @@
   }
 
   public final class DragGestureDetectorKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitDragOrCancellation-3UZYup8(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitHorizontalDragOrCancellation-3UZYup8(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitHorizontalTouchSlopOrCancellation-s7qLkbw(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitTouchSlopOrCancellation-s7qLkbw(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitVerticalDragOrCancellation-3UZYup8(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitVerticalTouchSlopOrCancellation-s7qLkbw(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectHorizontalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onHorizontalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectVerticalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onVerticalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? drag-xpXNQDM(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? horizontalDrag-xpXNQDM(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? verticalDrag-xpXNQDM(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+    method public static suspend Object? awaitDragOrCancellation-ijcpFGM(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitHorizontalDragOrCancellation-ijcpFGM(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitHorizontalTouchSlopOrCancellation-qFc19kk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitTouchSlopOrCancellation-qFc19kk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitVerticalDragOrCancellation-ijcpFGM(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitVerticalTouchSlopOrCancellation-qFc19kk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? detectDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? detectHorizontalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onHorizontalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? detectVerticalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onVerticalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? drag-Pd94rOk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+    method public static suspend Object? horizontalDrag-Pd94rOk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+    method public static suspend Object? verticalDrag-Pd94rOk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
   }
 
   public final class DraggableKt {
@@ -251,7 +257,7 @@
   }
 
   public final class ForEachGestureKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
   }
 
   public final class GestureCancellationException extends java.util.concurrent.CancellationException {
@@ -265,7 +271,7 @@
     method public static long calculatePan(androidx.compose.ui.input.pointer.PointerEvent);
     method public static float calculateRotation(androidx.compose.ui.input.pointer.PointerEvent);
     method public static float calculateZoom(androidx.compose.ui.input.pointer.PointerEvent);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectMultitouchGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional boolean panZoomLock, kotlin.jvm.functions.Function4<? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.geometry.Offset,? super java.lang.Float,? super java.lang.Float,kotlin.Unit> onGesture, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? detectMultitouchGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional boolean panZoomLock, kotlin.jvm.functions.Function4<? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.geometry.Offset,? super java.lang.Float,? super java.lang.Float,kotlin.Unit> onGesture, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
   }
 
   public interface PressGestureScope extends androidx.compose.ui.unit.Density {
@@ -277,12 +283,15 @@
     method public float scrollBy(float pixels);
   }
 
-  public final class ScrollableController {
+  public interface Scrollable {
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+  }
+
+  public final class ScrollableController implements androidx.compose.foundation.gestures.Scrollable {
     ctor public ScrollableController(kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> consumeScrollDelta, androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock, androidx.compose.foundation.InteractionState? interactionState);
     method public boolean isAnimationRunning();
     method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.jvm.functions.Function2<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,kotlin.Unit> onEnd);
-    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
     method public void stopAnimation();
     property public final boolean isAnimationRunning;
   }
@@ -293,9 +302,9 @@
   }
 
   public final class TapGestureDetectorKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.HandlePointerInputScope, optional boolean requireUnconsumed, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectTapGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleTap, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongPress, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.gestures.PressGestureScope,? super androidx.compose.ui.geometry.Offset,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onPress, kotlin.jvm.functions.Function0<kotlin.Unit> onTap, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.HandlePointerInputScope, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional boolean requireUnconsumed, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? detectTapGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleTap, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongPress, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.gestures.PressGestureScope,? super androidx.compose.ui.geometry.Offset,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onPress, kotlin.jvm.functions.Function0<kotlin.Unit> onTap, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
   }
 
   public final class ZoomableController {
@@ -319,19 +328,40 @@
   public final class CachingItemContentFactoryKt {
   }
 
+  public abstract sealed class GridCells {
+  }
+
+  public static final class GridCells.Adaptive extends androidx.compose.foundation.lazy.GridCells {
+    method public float getMinSize-D9Ej5fM();
+    property public final float minSize;
+  }
+
+  public static final class GridCells.Fixed extends androidx.compose.foundation.lazy.GridCells {
+    ctor public GridCells.Fixed(int count);
+    method public int getCount();
+    property public final int count;
+  }
+
   public final class LazyDslKt {
-    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
   }
 
   public final class LazyForKt {
-    method @androidx.compose.runtime.Composable public static <T> void LazyColumnFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static <T> void LazyColumnForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static <T> void LazyRowFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static <T> void LazyRowForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyColumnFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyColumnForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyRowFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyRowForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
   }
 
   public final class LazyGridKt {
+    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.compose.foundation.lazy.GridCells cells, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyGridScope,kotlin.Unit> content);
+  }
+
+  public interface LazyGridScope {
+    method public void item(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
+    method public <T> void items(java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public <T> void itemsIndexed(java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
   }
 
   @androidx.compose.runtime.Stable public interface LazyItemScope {
@@ -340,9 +370,29 @@
     method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
+  public interface LazyListItemInfo {
+    method public int getIndex();
+    method public int getOffset();
+    method public int getSize();
+    property public abstract int index;
+    property public abstract int offset;
+    property public abstract int size;
+  }
+
   public final class LazyListKt {
   }
 
+  public interface LazyListLayoutInfo {
+    method public int getTotalItemsCount();
+    method public int getViewportEndOffset();
+    method public int getViewportStartOffset();
+    method public java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> getVisibleItemsInfo();
+    property public abstract int totalItemsCount;
+    property public abstract int viewportEndOffset;
+    property public abstract int viewportStartOffset;
+    property public abstract java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> visibleItemsInfo;
+  }
+
   public final class LazyListMeasureKt {
   }
 
@@ -350,19 +400,21 @@
     method public void item(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
     method public <T> void items(java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
     method public <T> void itemsIndexed(java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public void stickyHeader(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
   }
 
-  @androidx.compose.runtime.Stable public final class LazyListState {
+  @androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.Scrollable {
     ctor public LazyListState(int firstVisibleItemIndex, int firstVisibleItemScrollOffset, androidx.compose.foundation.InteractionState? interactionState, androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock);
     method public int getFirstVisibleItemIndex();
     method public int getFirstVisibleItemScrollOffset();
+    method public androidx.compose.foundation.lazy.LazyListLayoutInfo getLayoutInfo();
     method public boolean isAnimationRunning();
     method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
     method public suspend Object? snapToItemIndex(@IntRange(from=0) int index, optional @IntRange(from=0) int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     property public final int firstVisibleItemIndex;
     property public final int firstVisibleItemScrollOffset;
     property public final boolean isAnimationRunning;
+    property public final androidx.compose.foundation.lazy.LazyListLayoutInfo layoutInfo;
     field public static final androidx.compose.foundation.lazy.LazyListState.Companion Companion;
   }
 
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index ddb6503..6cdc91d 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -137,7 +137,7 @@
     method public static androidx.compose.ui.Modifier verticalScroll(androidx.compose.ui.Modifier, androidx.compose.foundation.ScrollState state, optional boolean enabled, optional boolean reverseScrolling);
   }
 
-  @androidx.compose.runtime.Stable public final class ScrollState {
+  @androidx.compose.runtime.Stable public final class ScrollState implements androidx.compose.foundation.gestures.Scrollable {
     ctor public ScrollState(float initial, androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock, androidx.compose.foundation.InteractionState? interactionState);
     method public float getMaxValue();
     method public float getValue();
@@ -165,6 +165,7 @@
     method public String getNotSelected();
     method public String getSelected();
     method public String getTemplatePercent();
+    method public String getToggle();
     method public String getUnchecked();
     property public final String Checked;
     property public final String InProgress;
@@ -172,6 +173,7 @@
     property public final String NotSelected;
     property public final String Selected;
     property public final String TemplatePercent;
+    property public final String Toggle;
     property public final String Unchecked;
     field public static final androidx.compose.foundation.Strings INSTANCE;
   }
@@ -224,6 +226,10 @@
     method public static void fling(androidx.compose.animation.core.AnimatedFloat, float startVelocity, androidx.compose.foundation.animation.FlingConfig config, optional kotlin.jvm.functions.Function3<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,? super java.lang.Float,kotlin.Unit>? onAnimationEnd);
   }
 
+  public final class SmoothScrollKt {
+    method public static suspend Object? smoothScrollBy(androidx.compose.foundation.gestures.Scrollable, float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
+  }
+
 }
 
 package androidx.compose.foundation.gestures {
@@ -232,18 +238,18 @@
   }
 
   public final class DragGestureDetectorKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitDragOrCancellation-3UZYup8(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitHorizontalDragOrCancellation-3UZYup8(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitHorizontalTouchSlopOrCancellation-s7qLkbw(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitTouchSlopOrCancellation-s7qLkbw(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitVerticalDragOrCancellation-3UZYup8(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitVerticalTouchSlopOrCancellation-s7qLkbw(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectHorizontalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onHorizontalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectVerticalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onVerticalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? drag-xpXNQDM(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? horizontalDrag-xpXNQDM(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? verticalDrag-xpXNQDM(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+    method public static suspend Object? awaitDragOrCancellation-ijcpFGM(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitHorizontalDragOrCancellation-ijcpFGM(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitHorizontalTouchSlopOrCancellation-qFc19kk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitTouchSlopOrCancellation-qFc19kk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitVerticalDragOrCancellation-ijcpFGM(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitVerticalTouchSlopOrCancellation-qFc19kk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? detectDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? detectHorizontalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onHorizontalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? detectVerticalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onVerticalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? drag-Pd94rOk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+    method public static suspend Object? horizontalDrag-Pd94rOk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+    method public static suspend Object? verticalDrag-Pd94rOk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
   }
 
   public final class DraggableKt {
@@ -251,7 +257,7 @@
   }
 
   public final class ForEachGestureKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
   }
 
   public final class GestureCancellationException extends java.util.concurrent.CancellationException {
@@ -265,7 +271,7 @@
     method public static long calculatePan(androidx.compose.ui.input.pointer.PointerEvent);
     method public static float calculateRotation(androidx.compose.ui.input.pointer.PointerEvent);
     method public static float calculateZoom(androidx.compose.ui.input.pointer.PointerEvent);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectMultitouchGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional boolean panZoomLock, kotlin.jvm.functions.Function4<? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.geometry.Offset,? super java.lang.Float,? super java.lang.Float,kotlin.Unit> onGesture, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? detectMultitouchGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional boolean panZoomLock, kotlin.jvm.functions.Function4<? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.geometry.Offset,? super java.lang.Float,? super java.lang.Float,kotlin.Unit> onGesture, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
   }
 
   public interface PressGestureScope extends androidx.compose.ui.unit.Density {
@@ -277,12 +283,15 @@
     method public float scrollBy(float pixels);
   }
 
-  public final class ScrollableController {
+  public interface Scrollable {
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+  }
+
+  public final class ScrollableController implements androidx.compose.foundation.gestures.Scrollable {
     ctor public ScrollableController(kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> consumeScrollDelta, androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock, androidx.compose.foundation.InteractionState? interactionState);
     method public boolean isAnimationRunning();
     method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.jvm.functions.Function2<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,kotlin.Unit> onEnd);
-    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
     method public void stopAnimation();
     property public final boolean isAnimationRunning;
   }
@@ -293,9 +302,9 @@
   }
 
   public final class TapGestureDetectorKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.HandlePointerInputScope, optional boolean requireUnconsumed, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectTapGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleTap, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongPress, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.gestures.PressGestureScope,? super androidx.compose.ui.geometry.Offset,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onPress, kotlin.jvm.functions.Function0<kotlin.Unit> onTap, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.HandlePointerInputScope, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional boolean requireUnconsumed, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? detectTapGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleTap, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongPress, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.gestures.PressGestureScope,? super androidx.compose.ui.geometry.Offset,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onPress, kotlin.jvm.functions.Function0<kotlin.Unit> onTap, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
   }
 
   public final class ZoomableController {
@@ -319,19 +328,40 @@
   public final class CachingItemContentFactoryKt {
   }
 
+  public abstract sealed class GridCells {
+  }
+
+  public static final class GridCells.Adaptive extends androidx.compose.foundation.lazy.GridCells {
+    method public float getMinSize-D9Ej5fM();
+    property public final float minSize;
+  }
+
+  public static final class GridCells.Fixed extends androidx.compose.foundation.lazy.GridCells {
+    ctor public GridCells.Fixed(int count);
+    method public int getCount();
+    property public final int count;
+  }
+
   public final class LazyDslKt {
-    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
   }
 
   public final class LazyForKt {
-    method @androidx.compose.runtime.Composable public static <T> void LazyColumnFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static <T> void LazyColumnForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static <T> void LazyRowFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static <T> void LazyRowForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyColumnFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyColumnForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyRowFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyRowForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
   }
 
   public final class LazyGridKt {
+    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.compose.foundation.lazy.GridCells cells, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyGridScope,kotlin.Unit> content);
+  }
+
+  public interface LazyGridScope {
+    method public void item(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
+    method public <T> void items(java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public <T> void itemsIndexed(java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
   }
 
   @androidx.compose.runtime.Stable public interface LazyItemScope {
@@ -340,9 +370,29 @@
     method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
+  public interface LazyListItemInfo {
+    method public int getIndex();
+    method public int getOffset();
+    method public int getSize();
+    property public abstract int index;
+    property public abstract int offset;
+    property public abstract int size;
+  }
+
   public final class LazyListKt {
   }
 
+  public interface LazyListLayoutInfo {
+    method public int getTotalItemsCount();
+    method public int getViewportEndOffset();
+    method public int getViewportStartOffset();
+    method public java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> getVisibleItemsInfo();
+    property public abstract int totalItemsCount;
+    property public abstract int viewportEndOffset;
+    property public abstract int viewportStartOffset;
+    property public abstract java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> visibleItemsInfo;
+  }
+
   public final class LazyListMeasureKt {
   }
 
@@ -350,19 +400,21 @@
     method public void item(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
     method public <T> void items(java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
     method public <T> void itemsIndexed(java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public void stickyHeader(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
   }
 
-  @androidx.compose.runtime.Stable public final class LazyListState {
+  @androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.Scrollable {
     ctor public LazyListState(int firstVisibleItemIndex, int firstVisibleItemScrollOffset, androidx.compose.foundation.InteractionState? interactionState, androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock);
     method public int getFirstVisibleItemIndex();
     method public int getFirstVisibleItemScrollOffset();
+    method public androidx.compose.foundation.lazy.LazyListLayoutInfo getLayoutInfo();
     method public boolean isAnimationRunning();
     method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
     method public suspend Object? snapToItemIndex(@IntRange(from=0) int index, optional @IntRange(from=0) int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     property public final int firstVisibleItemIndex;
     property public final int firstVisibleItemScrollOffset;
     property public final boolean isAnimationRunning;
+    property public final androidx.compose.foundation.lazy.LazyListLayoutInfo layoutInfo;
     field public static final androidx.compose.foundation.lazy.LazyListState.Companion Companion;
   }
 
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index ddb6503..6cdc91d 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -137,7 +137,7 @@
     method public static androidx.compose.ui.Modifier verticalScroll(androidx.compose.ui.Modifier, androidx.compose.foundation.ScrollState state, optional boolean enabled, optional boolean reverseScrolling);
   }
 
-  @androidx.compose.runtime.Stable public final class ScrollState {
+  @androidx.compose.runtime.Stable public final class ScrollState implements androidx.compose.foundation.gestures.Scrollable {
     ctor public ScrollState(float initial, androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock, androidx.compose.foundation.InteractionState? interactionState);
     method public float getMaxValue();
     method public float getValue();
@@ -165,6 +165,7 @@
     method public String getNotSelected();
     method public String getSelected();
     method public String getTemplatePercent();
+    method public String getToggle();
     method public String getUnchecked();
     property public final String Checked;
     property public final String InProgress;
@@ -172,6 +173,7 @@
     property public final String NotSelected;
     property public final String Selected;
     property public final String TemplatePercent;
+    property public final String Toggle;
     property public final String Unchecked;
     field public static final androidx.compose.foundation.Strings INSTANCE;
   }
@@ -224,6 +226,10 @@
     method public static void fling(androidx.compose.animation.core.AnimatedFloat, float startVelocity, androidx.compose.foundation.animation.FlingConfig config, optional kotlin.jvm.functions.Function3<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,? super java.lang.Float,kotlin.Unit>? onAnimationEnd);
   }
 
+  public final class SmoothScrollKt {
+    method public static suspend Object? smoothScrollBy(androidx.compose.foundation.gestures.Scrollable, float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
+  }
+
 }
 
 package androidx.compose.foundation.gestures {
@@ -232,18 +238,18 @@
   }
 
   public final class DragGestureDetectorKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitDragOrCancellation-3UZYup8(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitHorizontalDragOrCancellation-3UZYup8(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitHorizontalTouchSlopOrCancellation-s7qLkbw(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitTouchSlopOrCancellation-s7qLkbw(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitVerticalDragOrCancellation-3UZYup8(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitVerticalTouchSlopOrCancellation-s7qLkbw(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectHorizontalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onHorizontalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectVerticalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onVerticalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? drag-xpXNQDM(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? horizontalDrag-xpXNQDM(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? verticalDrag-xpXNQDM(androidx.compose.ui.input.pointer.HandlePointerInputScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+    method public static suspend Object? awaitDragOrCancellation-ijcpFGM(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitHorizontalDragOrCancellation-ijcpFGM(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitHorizontalTouchSlopOrCancellation-qFc19kk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitTouchSlopOrCancellation-qFc19kk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitVerticalDragOrCancellation-ijcpFGM(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitVerticalTouchSlopOrCancellation-qFc19kk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? detectDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? detectHorizontalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onHorizontalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? detectVerticalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onVerticalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? drag-Pd94rOk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+    method public static suspend Object? horizontalDrag-Pd94rOk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+    method public static suspend Object? verticalDrag-Pd94rOk(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
   }
 
   public final class DraggableKt {
@@ -251,7 +257,7 @@
   }
 
   public final class ForEachGestureKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
   }
 
   public final class GestureCancellationException extends java.util.concurrent.CancellationException {
@@ -265,7 +271,7 @@
     method public static long calculatePan(androidx.compose.ui.input.pointer.PointerEvent);
     method public static float calculateRotation(androidx.compose.ui.input.pointer.PointerEvent);
     method public static float calculateZoom(androidx.compose.ui.input.pointer.PointerEvent);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectMultitouchGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional boolean panZoomLock, kotlin.jvm.functions.Function4<? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.geometry.Offset,? super java.lang.Float,? super java.lang.Float,kotlin.Unit> onGesture, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? detectMultitouchGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional boolean panZoomLock, kotlin.jvm.functions.Function4<? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.geometry.Offset,? super java.lang.Float,? super java.lang.Float,kotlin.Unit> onGesture, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
   }
 
   public interface PressGestureScope extends androidx.compose.ui.unit.Density {
@@ -277,12 +283,15 @@
     method public float scrollBy(float pixels);
   }
 
-  public final class ScrollableController {
+  public interface Scrollable {
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+  }
+
+  public final class ScrollableController implements androidx.compose.foundation.gestures.Scrollable {
     ctor public ScrollableController(kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> consumeScrollDelta, androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock, androidx.compose.foundation.InteractionState? interactionState);
     method public boolean isAnimationRunning();
     method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.jvm.functions.Function2<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,kotlin.Unit> onEnd);
-    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
     method public void stopAnimation();
     property public final boolean isAnimationRunning;
   }
@@ -293,9 +302,9 @@
   }
 
   public final class TapGestureDetectorKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.HandlePointerInputScope, optional boolean requireUnconsumed, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? detectTapGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleTap, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongPress, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.gestures.PressGestureScope,? super androidx.compose.ui.geometry.Offset,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onPress, kotlin.jvm.functions.Function0<kotlin.Unit> onTap, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.HandlePointerInputScope, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional boolean requireUnconsumed, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
+    method public static suspend Object? detectTapGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleTap, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongPress, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.gestures.PressGestureScope,? super androidx.compose.ui.geometry.Offset,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onPress, kotlin.jvm.functions.Function0<kotlin.Unit> onTap, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange> p);
   }
 
   public final class ZoomableController {
@@ -319,19 +328,40 @@
   public final class CachingItemContentFactoryKt {
   }
 
+  public abstract sealed class GridCells {
+  }
+
+  public static final class GridCells.Adaptive extends androidx.compose.foundation.lazy.GridCells {
+    method public float getMinSize-D9Ej5fM();
+    property public final float minSize;
+  }
+
+  public static final class GridCells.Fixed extends androidx.compose.foundation.lazy.GridCells {
+    ctor public GridCells.Fixed(int count);
+    method public int getCount();
+    property public final int count;
+  }
+
   public final class LazyDslKt {
-    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
   }
 
   public final class LazyForKt {
-    method @androidx.compose.runtime.Composable public static <T> void LazyColumnFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static <T> void LazyColumnForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static <T> void LazyRowFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static <T> void LazyRowForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyColumnFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyColumnForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyRowFor(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> void LazyRowForIndexed(java.util.List<? extends T> items, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
   }
 
   public final class LazyGridKt {
+    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.compose.foundation.lazy.GridCells cells, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyGridScope,kotlin.Unit> content);
+  }
+
+  public interface LazyGridScope {
+    method public void item(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
+    method public <T> void items(java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method public <T> void itemsIndexed(java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
   }
 
   @androidx.compose.runtime.Stable public interface LazyItemScope {
@@ -340,9 +370,29 @@
     method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
+  public interface LazyListItemInfo {
+    method public int getIndex();
+    method public int getOffset();
+    method public int getSize();
+    property public abstract int index;
+    property public abstract int offset;
+    property public abstract int size;
+  }
+
   public final class LazyListKt {
   }
 
+  public interface LazyListLayoutInfo {
+    method public int getTotalItemsCount();
+    method public int getViewportEndOffset();
+    method public int getViewportStartOffset();
+    method public java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> getVisibleItemsInfo();
+    property public abstract int totalItemsCount;
+    property public abstract int viewportEndOffset;
+    property public abstract int viewportStartOffset;
+    property public abstract java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> visibleItemsInfo;
+  }
+
   public final class LazyListMeasureKt {
   }
 
@@ -350,19 +400,21 @@
     method public void item(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
     method public <T> void items(java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
     method public <T> void itemsIndexed(java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public void stickyHeader(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
   }
 
-  @androidx.compose.runtime.Stable public final class LazyListState {
+  @androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.Scrollable {
     ctor public LazyListState(int firstVisibleItemIndex, int firstVisibleItemScrollOffset, androidx.compose.foundation.InteractionState? interactionState, androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock);
     method public int getFirstVisibleItemIndex();
     method public int getFirstVisibleItemScrollOffset();
+    method public androidx.compose.foundation.lazy.LazyListLayoutInfo getLayoutInfo();
     method public boolean isAnimationRunning();
     method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
     method public suspend Object? snapToItemIndex(@IntRange(from=0) int index, optional @IntRange(from=0) int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     property public final int firstVisibleItemIndex;
     property public final int firstVisibleItemScrollOffset;
     property public final boolean isAnimationRunning;
+    property public final androidx.compose.foundation.lazy.LazyListLayoutInfo layoutInfo;
     field public static final androidx.compose.foundation.lazy.LazyListState.Companion Companion;
   }
 
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
index 33ca738..b44a23a 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
@@ -35,6 +35,7 @@
         ComposableDemo("Multiple-interaction InteractionState") {
             MultipleInteractionStateSample()
         },
-        DemoCategory("Suspending Gesture Detectors", CoroutineGestureDemos)
+        DemoCategory("Suspending Gesture Detectors", CoroutineGestureDemos),
+        ComposableDemo("NestedScroll") { NestedScrollDemo() },
     )
 )
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
index f804404..d46aa5e 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
@@ -16,10 +16,15 @@
 
 package androidx.compose.foundation.demos
 
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Interaction
 import androidx.compose.foundation.InteractionState
+import androidx.compose.foundation.ScrollableColumn
+import androidx.compose.foundation.animation.smoothScrollBy
 import androidx.compose.foundation.background
+import androidx.compose.foundation.border
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ExperimentalLayout
@@ -28,16 +33,18 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.LazyColumnFor
-import androidx.compose.foundation.lazy.LazyColumnForIndexed
+import androidx.compose.foundation.lazy.LazyVerticalGrid
+import androidx.compose.foundation.lazy.GridCells
 import androidx.compose.foundation.lazy.LazyRow
-import androidx.compose.foundation.lazy.LazyRowFor
-import androidx.compose.foundation.lazy.LazyRowForIndexed
 import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.samples.StickyHeaderSample
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.integration.demos.common.ComposableDemo
 import androidx.compose.material.AmbientContentColor
@@ -49,6 +56,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.savedinstancestate.savedInstanceState
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -73,22 +81,29 @@
     ComposableDemo("Rtl list") { RtlListDemo() },
     ComposableDemo("LazyColumn DSL") { LazyColumnScope() },
     ComposableDemo("LazyRow DSL") { LazyRowScope() },
+    ComposableDemo("LazyColumn with sticky headers") { StickyHeaderSample() },
+    ComposableDemo("Arrangements") { LazyListArrangements() },
+    ComposableDemo("Reverse scroll direction") { ReverseLayout() },
+    ComposableDemo("Nested lazy lists") { NestedLazyDemo() },
+    ComposableDemo("LazyGrid") { LazyGridDemo() },
     PagingDemos
 )
 
 @Composable
 private fun LazyColumnDemo() {
-    LazyColumnFor(
-        items = listOf(
-            "Hello,", "World:", "It works!", "",
-            "this one is really long and spans a few lines for scrolling purposes",
-            "these", "are", "offscreen"
-        ) + (1..100).map { "$it" }
-    ) {
-        Text(text = it, fontSize = 80.sp)
+    LazyColumn {
+        items(
+            items = listOf(
+                "Hello,", "World:", "It works!", "",
+                "this one is really long and spans a few lines for scrolling purposes",
+                "these", "are", "offscreen"
+            ) + (1..100).map { "$it" }
+        ) {
+            Text(text = it, fontSize = 80.sp)
 
-        if (it.contains("works")) {
-            Text("You can even emit multiple components per item.")
+            if (it.contains("works")) {
+                Text("You can even emit multiple components per item.")
+            }
         }
     }
 }
@@ -104,11 +119,10 @@
             Button(modifier = buttonModifier, onClick = { numItems-- }) { Text("Remove") }
             Button(modifier = buttonModifier, onClick = { offset++ }) { Text("Offset") }
         }
-        LazyColumnFor(
-            (1..numItems).map { it + offset }.toList(),
-            Modifier.fillMaxWidth()
-        ) {
-            Text("$it", style = AmbientTextStyle.current.copy(fontSize = 40.sp))
+        LazyColumn(Modifier.fillMaxWidth()) {
+            items((1..numItems).map { it + offset }.toList()) {
+                Text("$it", style = AmbientTextStyle.current.copy(fontSize = 40.sp))
+            }
         }
     }
 }
@@ -120,6 +134,7 @@
     val state = rememberLazyListState(interactionState = interactionState)
     var lastScrollDescription: String by remember { mutableStateOf("") }
     Column {
+        @Suppress("DEPRECATION")
         FlowRow {
             val buttonModifier = Modifier.padding(8.dp)
             val density = AmbientDensity.current
@@ -174,18 +189,19 @@
                 fontSize = 20.sp
             )
         }
-        LazyColumnFor(
-            (0..1000).toList(),
+        LazyColumn(
             Modifier.fillMaxWidth(),
             state = state
         ) {
-            Text("$it", style = AmbientTextStyle.current.copy(fontSize = 40.sp))
+            items((0..1000).toList()) {
+                Text("$it", style = AmbientTextStyle.current.copy(fontSize = 40.sp))
+            }
         }
     }
 }
 
 @Composable
-fun Button(modifier: Modifier, onClick: () -> Unit, content: @Composable () -> Unit) {
+fun Button(modifier: Modifier = Modifier, onClick: () -> Unit, content: @Composable () -> Unit) {
     Box(
         modifier
             .clickable(onClick = onClick)
@@ -198,8 +214,10 @@
 
 @Composable
 private fun LazyRowItemsDemo() {
-    LazyRowFor(items = (1..1000).toList()) {
-        Square(it)
+    LazyRow {
+        items((1..1000).toList()) {
+            Square(it)
+        }
     }
 }
 
@@ -218,11 +236,15 @@
 private fun ListWithIndexSample() {
     val friends = listOf("Alex", "John", "Danny", "Sam")
     Column {
-        LazyRowForIndexed(friends, Modifier.fillMaxWidth()) { index, friend ->
-            Text("$friend at index $index", Modifier.padding(16.dp))
+        LazyRow(Modifier.fillMaxWidth()) {
+            itemsIndexed(friends) { index, friend ->
+                Text("$friend at index $index", Modifier.padding(16.dp))
+            }
         }
-        LazyColumnForIndexed(friends, Modifier.fillMaxWidth()) { index, friend ->
-            Text("$friend at index $index", Modifier.padding(16.dp))
+        LazyColumn(Modifier.fillMaxWidth()) {
+            itemsIndexed(friends) { index, friend ->
+                Text("$friend at index $index", Modifier.padding(16.dp))
+            }
         }
     }
 }
@@ -230,14 +252,16 @@
 @Composable
 private fun RtlListDemo() {
     Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
-        LazyRowForIndexed((0..100).toList(), Modifier.fillMaxWidth()) { index, item ->
-            Text(
-                "$item",
-                Modifier
-                    .size(100.dp)
-                    .background(if (index % 2 == 0) Color.LightGray else Color.Transparent)
-                    .padding(16.dp)
-            )
+        LazyRow(Modifier.fillMaxWidth()) {
+            itemsIndexed((0..100).toList()) { index, item ->
+                Text(
+                    "$item",
+                    Modifier
+                        .size(100.dp)
+                        .background(if (index % 2 == 0) Color.LightGray else Color.Transparent)
+                        .padding(16.dp)
+                )
+            }
         }
     }
 }
@@ -245,8 +269,10 @@
 @Composable
 private fun PagerLikeDemo() {
     val pages = listOf(Color.LightGray, Color.White, Color.DarkGray)
-    LazyRowFor(pages) {
-        Spacer(Modifier.fillParentMaxSize().background(it))
+    LazyRow {
+        items(pages) {
+            Spacer(Modifier.fillParentMaxSize().background(it))
+        }
     }
 }
 
@@ -297,4 +323,205 @@
             }
         }
     }
-}
\ No newline at end of file
+}
+
+@Composable
+private fun LazyListArrangements() {
+    var count by remember { mutableStateOf(3) }
+    var arrangement by remember { mutableStateOf(6) }
+    Column {
+        Row {
+            Button(onClick = { count-- }) {
+                Text("--")
+            }
+            Button(onClick = { count++ }) {
+                Text("++")
+            }
+            Button(
+                onClick = {
+                    arrangement++
+                    if (arrangement == Arrangements.size) {
+                        arrangement = 0
+                    }
+                }
+            ) {
+                Text("Next")
+            }
+            Text("$arrangement ${Arrangements[arrangement]}")
+        }
+        Row {
+            val item = @Composable {
+                Box(
+                    Modifier
+                        .height(200.dp)
+                        .fillMaxWidth()
+                        .background(Color.Red)
+                        .border(1.dp, Color.Cyan)
+                )
+            }
+            ScrollableColumn(
+                verticalArrangement = Arrangements[arrangement],
+                modifier = Modifier.weight(1f).fillMaxHeight()
+            ) {
+                (1..count).forEach {
+                    item()
+                }
+            }
+            LazyColumn(
+                verticalArrangement = Arrangements[arrangement],
+                modifier = Modifier.weight(1f).fillMaxHeight()
+            ) {
+                items((1..count).toList()) {
+                    item()
+                }
+            }
+        }
+    }
+}
+
+private val Arrangements = listOf(
+    Arrangement.Center,
+    Arrangement.Top,
+    Arrangement.Bottom,
+    Arrangement.SpaceAround,
+    Arrangement.SpaceBetween,
+    Arrangement.SpaceEvenly,
+    Arrangement.spacedBy(40.dp),
+    Arrangement.spacedBy(40.dp, Alignment.Bottom),
+)
+
+@Composable
+fun ReverseLayout() {
+    Column {
+        val scrollState = rememberScrollState()
+        val lazyState = rememberLazyListState()
+        var count by remember { mutableStateOf(3) }
+        var reverse by remember { mutableStateOf(true) }
+        Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
+            Button(onClick = { count -= 5 }) {
+                Text("--")
+            }
+            Button(onClick = { count += 5 }) {
+                Text("++")
+            }
+            Button(onClick = { reverse = !reverse }) {
+                Text("=!")
+            }
+            Text("Scroll=${scrollState.value.toInt()}")
+            Text(
+                "Lazy=${lazyState.firstVisibleItemIndex}; " +
+                    "${lazyState.firstVisibleItemScrollOffset}"
+            )
+        }
+        Row {
+            val item1 = @Composable { index: Int ->
+                Text(
+                    "$index",
+                    Modifier
+                        .height(200.dp)
+                        .fillMaxWidth()
+                        .background(Color.Red)
+                        .border(1.dp, Color.Cyan)
+                )
+            }
+            val item2 = @Composable { index: Int ->
+                Text("After $index")
+            }
+            ScrollableColumn(
+                reverseScrollDirection = reverse,
+                verticalArrangement = if (reverse) Arrangement.Bottom else Arrangement.Top,
+                scrollState = scrollState,
+                modifier = Modifier.weight(1f).fillMaxHeight()
+            ) {
+                if (reverse) {
+                    (count downTo 1).forEach {
+                        item2(it)
+                        item1(it)
+                    }
+                } else {
+                    (1..count).forEach {
+                        item1(it)
+                        item2(it)
+                    }
+                }
+            }
+            LazyColumn(
+                reverseLayout = reverse,
+                state = lazyState,
+                modifier = Modifier.weight(1f).fillMaxHeight()
+            ) {
+                items((1..count).toList()) {
+                    item1(it)
+                    item2(it)
+                }
+            }
+        }
+    }
+}
+
+@Composable
+private fun NestedLazyDemo() {
+    val item = @Composable { index: Int ->
+        Box(
+            Modifier.padding(16.dp).size(200.dp).background(Color.LightGray),
+            contentAlignment = Alignment.Center
+        ) {
+            var state by savedInstanceState { 0 }
+            Button(onClick = { state++ }) {
+                Text("Index=$index State=$state")
+            }
+        }
+    }
+    LazyColumn {
+        item {
+            LazyRow {
+                items(List(100) { it }) {
+                    item(it)
+                }
+            }
+        }
+        items(List(100) { it }) {
+            item(it)
+        }
+    }
+}
+
+@Composable
+private fun LazyGridDemo() {
+    val columnModes = listOf(
+        GridCells.Fixed(3),
+        GridCells.Adaptive(minSize = 60.dp)
+    )
+    var currentMode by remember { mutableStateOf(0) }
+    Column {
+        Button(
+            modifier = Modifier.wrapContentSize(),
+            onClick = {
+                currentMode = (currentMode + 1) % columnModes.size
+            }
+        ) {
+            Text("Switch mode")
+        }
+        LazyGridForMode(columnModes[currentMode])
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun LazyGridForMode(mode: GridCells) {
+    LazyVerticalGrid(
+        cells = mode
+    ) {
+        items(
+            items = (1..100).toList()
+        ) {
+            Text(
+                text = "$it",
+                fontSize = 20.sp,
+                modifier = Modifier
+                    .background(Color.Gray.copy(alpha = (it % 10) / 10f))
+                    .padding(8.dp)
+            )
+        }
+    }
+}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/NestedScrollDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/NestedScrollDemos.kt
new file mode 100644
index 0000000..b4fdf7b
--- /dev/null
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/NestedScrollDemos.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.demos
+
+import androidx.compose.foundation.ScrollableColumn
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+@Composable
+fun NestedScrollDemo() {
+    ScrollableColumn(
+        Modifier.fillMaxSize().background(Color.Red),
+        contentPadding = PaddingValues(30.dp)
+    ) {
+        repeat(6) { outerOuterIndex ->
+            LazyColumn(
+                modifier = Modifier.fillMaxSize().border(3.dp, Color.Black).height(350.dp)
+                    .background(Color.Yellow),
+                contentPadding = PaddingValues(60.dp)
+            ) {
+                repeat(3) { outerIndex ->
+                    item {
+                        ScrollableColumn(
+                            Modifier.fillMaxSize().border(3.dp, Color.Blue).height(150.dp)
+                                .background(Color.White),
+                            contentPadding = PaddingValues(30.dp)
+                        ) {
+                            repeat(6) { innerIndex ->
+                                Box(
+                                    Modifier
+                                        .height(38.dp)
+                                        .fillMaxWidth()
+                                        .background(Color.Magenta)
+                                        .border(2.dp, Color.Yellow),
+                                    contentAlignment = Alignment.Center
+                                ) {
+                                    Text(
+                                        text = "$outerOuterIndex : $outerIndex : $innerIndex",
+                                        fontSize = 24.sp
+                                    )
+                                }
+                            }
+                        }
+                    }
+
+                    item {
+                        Spacer(Modifier.height(5.dp))
+                    }
+                }
+            }
+            Spacer(Modifier.height(5.dp))
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt
index 1310c48..517d7af 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt
@@ -51,7 +51,6 @@
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.graphics.graphicsLayer
@@ -61,6 +60,7 @@
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.input.pointer.positionChange
 import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import kotlin.math.atan2
@@ -107,7 +107,6 @@
 /**
  * Gesture detector for tap, double-tap, and long-press.
  */
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 fun CoroutineTapDemo() {
     var tapHue by remember { mutableStateOf(randomHue()) }
@@ -212,7 +211,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 fun TouchSlopDragGestures() {
     Column {
@@ -290,7 +288,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 fun OrientationLockDragGestures() {
     var size by remember { mutableStateOf(IntSize.Zero) }
@@ -335,7 +332,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 fun Drag2DGestures() {
     var size by remember { mutableStateOf(IntSize.Zero) }
@@ -347,7 +343,7 @@
         }.fillMaxSize()
     ) {
         Box(
-            Modifier.offset({ offsetX.value }, { offsetY.value })
+            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                 .background(Color.Blue)
                 .size(50.dp)
                 .pointerInput {
@@ -365,7 +361,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 fun MultitouchArea(
     text: String,
@@ -400,7 +395,7 @@
         }
     ) {
         Box(
-            Modifier.offset({ offsetX }, { offsetY })
+            Modifier.offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
                 .graphicsLayer(
                     scaleX = zoom,
                     scaleY = zoom,
@@ -440,7 +435,6 @@
  * This is a multi-touch gesture detector, including pan, zoom, and rotation.
  * The user can pan, zoom, and rotate once touch slop has been reached.
  */
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 fun MultitouchGestureDetector() {
     MultitouchArea(
@@ -458,7 +452,6 @@
  * It is common to want to lean toward zoom over rotation, so this gesture detector will
  * lock into zoom if the first unless the rotation passes touch slop first.
  */
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 fun MultitouchLockGestureDetector() {
     MultitouchArea(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldFocusTransition.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldFocusTransition.kt
index 183dd1e..d2c13b0 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldFocusTransition.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldFocusTransition.kt
@@ -25,11 +25,10 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.savedinstancestate.savedInstanceState
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color.Companion.Black
 import androidx.compose.ui.graphics.Color.Companion.Red
 import androidx.compose.ui.text.TextStyle
@@ -37,25 +36,23 @@
 import androidx.compose.ui.unit.sp
 
 @Composable
-@OptIn(ExperimentalFocus::class)
 fun TextFieldFocusTransition() {
-    val focusRequesters = List(6) { FocusRequester() }
+    val focusReferences = List(6) { FocusReference() }
 
     ScrollableColumn {
-        TextFieldWithFocusRequesters(focusRequesters[0], focusRequesters[1])
-        TextFieldWithFocusRequesters(focusRequesters[1], focusRequesters[2])
-        TextFieldWithFocusRequesters(focusRequesters[2], focusRequesters[3])
-        TextFieldWithFocusRequesters(focusRequesters[3], focusRequesters[4])
-        TextFieldWithFocusRequesters(focusRequesters[4], focusRequesters[5])
-        TextFieldWithFocusRequesters(focusRequesters[5], focusRequesters[0])
+        TextFieldWithFocusReferences(focusReferences[0], focusReferences[1])
+        TextFieldWithFocusReferences(focusReferences[1], focusReferences[2])
+        TextFieldWithFocusReferences(focusReferences[2], focusReferences[3])
+        TextFieldWithFocusReferences(focusReferences[3], focusReferences[4])
+        TextFieldWithFocusReferences(focusReferences[4], focusReferences[5])
+        TextFieldWithFocusReferences(focusReferences[5], focusReferences[0])
     }
 }
 
-@OptIn(ExperimentalFocus::class)
 @Composable
-private fun TextFieldWithFocusRequesters(
-    focusRequester: FocusRequester,
-    nextFocusRequester: FocusRequester
+private fun TextFieldWithFocusReferences(
+    focusReference: FocusReference,
+    nextFocusReference: FocusReference
 ) {
     val state = savedInstanceState { "Focus Transition Test" }
     var color by remember { mutableStateOf(Black) }
@@ -63,11 +60,11 @@
     BasicTextField(
         value = state.value,
         modifier = demoTextFieldModifiers
-            .focusObserver { color = if (it.isFocused) Red else Black }
-            .focusRequester(focusRequester),
+            .onFocusChanged { color = if (it.isFocused) Red else Black }
+            .focusReference(focusReference),
         textStyle = TextStyle(color = color, fontSize = 32.sp),
         onValueChange = { state.value = it },
         keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
-        onImeActionPerformed = { if (it == ImeAction.Next) nextFocusRequester.requestFocus() }
+        onImeActionPerformed = { if (it == ImeAction.Next) nextFocusReference.requestFocus() }
     )
 }
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BorderSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BorderSamples.kt
index 190bacc..304227b 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BorderSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BorderSamples.kt
@@ -25,8 +25,8 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.HorizontalGradient
 import androidx.compose.ui.graphics.TileMode
 import androidx.compose.ui.unit.dp
 
@@ -42,7 +42,7 @@
 @Composable
 @Sampled
 fun BorderSampleWithBrush() {
-    val gradientBrush = HorizontalGradient(
+    val gradientBrush = Brush.horizontalGradient(
         colors = listOf(Color.Red, Color.Blue, Color.Green),
         startX = 0.0f,
         endX = 500.0f,
@@ -63,7 +63,6 @@
         modifier = Modifier.border(
             border = BorderStroke(2.dp, Color.Blue),
             shape = CutCornerShape(8.dp)
-        )
-            .padding(10.dp)
+        ).padding(10.dp)
     )
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DragGestureDetectorSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DragGestureDetectorSamples.kt
index 4cd2ade..1dc203b 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DragGestureDetectorSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DragGestureDetectorSamples.kt
@@ -48,16 +48,16 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.pointer.consumePositionChange
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.input.pointer.positionChange
 import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.toSize
+import kotlin.math.roundToInt
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun AwaitHorizontalDragOrCancellationSample() {
@@ -69,13 +69,13 @@
             .onSizeChanged { width = it.width.toFloat() }
     ) {
         Box(
-            Modifier.offset({ offsetX.value }, { offsetY.value })
+            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                 .fillMaxHeight()
                 .width(50.dp)
                 .background(Color.Blue)
                 .pointerInput {
                     forEachGesture {
-                        handlePointerInput {
+                        awaitPointerEventScope {
                             val down = awaitFirstDown()
                             var change =
                                 awaitHorizontalTouchSlopOrCancellation(down.id) { change, over ->
@@ -102,7 +102,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun HorizontalDragSample() {
@@ -114,13 +113,13 @@
             .onSizeChanged { width = it.width.toFloat() }
     ) {
         Box(
-            Modifier.offset({ offsetX.value }, { offsetY.value })
+            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                 .fillMaxHeight()
                 .width(50.dp)
                 .background(Color.Blue)
                 .pointerInput {
                     forEachGesture {
-                        handlePointerInput {
+                        awaitPointerEventScope {
                             val down = awaitFirstDown()
                             val change =
                                 awaitHorizontalTouchSlopOrCancellation(down.id) { change, over ->
@@ -146,7 +145,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun DetectHorizontalDragGesturesSample() {
@@ -158,7 +156,7 @@
             .onSizeChanged { width = it.width.toFloat() }
     ) {
         Box(
-            Modifier.offset({ offsetX.value }, { offsetY.value })
+            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                 .fillMaxHeight()
                 .width(50.dp)
                 .background(Color.Blue)
@@ -174,7 +172,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun AwaitVerticalDragOrCancellationSample() {
@@ -186,13 +183,13 @@
             .onSizeChanged { height = it.height.toFloat() }
     ) {
         Box(
-            Modifier.offset({ offsetX.value }, { offsetY.value })
+            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                 .fillMaxWidth()
                 .height(50.dp)
                 .background(Color.Blue)
                 .pointerInput {
                     forEachGesture {
-                        handlePointerInput {
+                        awaitPointerEventScope {
                             val down = awaitFirstDown()
                             var change =
                                 awaitVerticalTouchSlopOrCancellation(down.id) { change, over ->
@@ -219,7 +216,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun VerticalDragSample() {
@@ -231,13 +227,13 @@
             .onSizeChanged { height = it.height.toFloat() }
     ) {
         Box(
-            Modifier.offset({ offsetX.value }, { offsetY.value })
+            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                 .fillMaxWidth()
                 .height(50.dp)
                 .background(Color.Blue)
                 .pointerInput {
                     forEachGesture {
-                        handlePointerInput {
+                        awaitPointerEventScope {
                             val down = awaitFirstDown()
                             val change =
                                 awaitVerticalTouchSlopOrCancellation(down.id) { change, over ->
@@ -263,7 +259,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun DetectVerticalDragGesturesSample() {
@@ -275,7 +270,7 @@
             .onSizeChanged { height = it.height.toFloat() }
     ) {
         Box(
-            Modifier.offset({ offsetX.value }, { offsetY.value })
+            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                 .fillMaxWidth()
                 .height(50.dp)
                 .background(Color.Blue)
@@ -291,7 +286,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun AwaitDragOrCancellationSample() {
@@ -303,12 +297,12 @@
             .onSizeChanged { size = it.toSize() }
     ) {
         Box(
-            Modifier.offset({ offsetX.value }, { offsetY.value })
+            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                 .size(50.dp)
                 .background(Color.Blue)
                 .pointerInput {
                     forEachGesture {
-                        handlePointerInput {
+                        awaitPointerEventScope {
                             val down = awaitFirstDown()
                             var change = awaitTouchSlopOrCancellation(down.id) { change, over ->
                                 val original = Offset(offsetX.value, offsetY.value)
@@ -348,7 +342,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun DragSample() {
@@ -360,12 +353,12 @@
             .onSizeChanged { size = it.toSize() }
     ) {
         Box(
-            Modifier.offset({ offsetX.value }, { offsetY.value })
+            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                 .size(50.dp)
                 .background(Color.Blue)
                 .pointerInput {
                     forEachGesture {
-                        handlePointerInput {
+                        awaitPointerEventScope {
                             val down = awaitFirstDown()
                             val change = awaitTouchSlopOrCancellation(down.id) { change, over ->
                                 val original = Offset(offsetX.value, offsetY.value)
@@ -404,7 +397,6 @@
     }
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun DetectDragGesturesSample() {
@@ -416,7 +408,7 @@
             .onSizeChanged { size = it.toSize() }
     ) {
         Box(
-            Modifier.offset({ offsetX.value }, { offsetY.value })
+            Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                 .size(50.dp)
                 .background(Color.Blue)
                 .pointerInput {
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DraggableSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DraggableSamples.kt
index ca8c36c..5cc2a57 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DraggableSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DraggableSamples.kt
@@ -29,7 +29,9 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
+import kotlin.math.roundToInt
 
 @Sampled
 @Composable
@@ -52,7 +54,9 @@
             .background(Color.Black)
     ) {
         Box(
-            Modifier.offset(x = { offsetPosition.value }).preferredSize(50.dp).background(Color.Red)
+            Modifier.offset { IntOffset(offsetPosition.value.roundToInt(), 0) }
+                .preferredSize(50.dp)
+                .background(Color.Red)
         )
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DrawBackgroundSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DrawBackgroundSamples.kt
index d0686b5..b876f7c 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DrawBackgroundSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DrawBackgroundSamples.kt
@@ -23,8 +23,8 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.HorizontalGradient
 import androidx.compose.ui.unit.dp
 
 @Composable
@@ -39,7 +39,7 @@
 @Composable
 @Sampled
 fun DrawBackgroundShapedBrush() {
-    val gradientBrush = HorizontalGradient(
+    val gradientBrush = Brush.horizontalGradient(
         colors = listOf(Color.Red, Color.Blue, Color.Green),
         startX = 0.0f,
         endX = 500.0f
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/FocusableSample.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/FocusableSample.kt
index 66cd360..aaf0973 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/FocusableSample.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/FocusableSample.kt
@@ -26,16 +26,14 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusReference
 
 @Sampled
 @Composable
-@OptIn(ExperimentalFocus::class)
 fun FocusableSample() {
-    // initialize focus requester to be able to request focus programmatically
-    val requester = FocusRequester()
+    // initialize focus reference to be able to request focus programmatically
+    val focusReference = FocusReference()
     // interaction state to track changes of the component's interactions (like "focused")
     val interactionState = remember { InteractionState() }
 
@@ -51,11 +49,11 @@
         Text(
             text = text,
             modifier = Modifier
-                // add focusRequester modifier before the focusable (or even in the parent)
-                .focusRequester(requester)
+                // add focusReference modifier before the focusable (or even in the parent)
+                .focusReference(focusReference)
                 .focusable(interactionState = interactionState)
         )
-        Button(onClick = { requester.requestFocus() }) {
+        Button(onClick = { focusReference.requestFocus() }) {
             Text("Bring focus to the text above")
         }
     }
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt
index 93a4438..1b71a259 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt
@@ -17,10 +17,17 @@
 package androidx.compose.foundation.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.LazyRow
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
 
 @Sampled
 @Composable
@@ -62,4 +69,25 @@
             Text("Item at index $index is $item")
         }
     }
-}
\ No newline at end of file
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Sampled
+@Composable
+fun StickyHeaderSample() {
+    val sections = listOf("A", "B", "C", "D", "E", "F", "G")
+
+    LazyColumn {
+        sections.forEach { section ->
+            stickyHeader {
+                Text(
+                    "Section $section",
+                    Modifier.fillMaxWidth().background(Color.LightGray).padding(8.dp)
+                )
+            }
+            items((0..9).toList()) {
+                Text("Item $it from the section $section")
+            }
+        }
+    }
+}
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyForSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyForSamples.kt
deleted file mode 100644
index d1b87dd..0000000
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyForSamples.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.samples
-
-import androidx.annotation.Sampled
-import androidx.compose.foundation.lazy.LazyColumnFor
-import androidx.compose.foundation.lazy.LazyColumnForIndexed
-import androidx.compose.foundation.lazy.LazyRowFor
-import androidx.compose.foundation.lazy.LazyRowForIndexed
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
-
-@Sampled
-@Composable
-fun LazyColumnForSample() {
-    val items = listOf("A", "B", "C")
-    LazyColumnFor(items) {
-        Text("Item is $it")
-    }
-}
-
-@Sampled
-@Composable
-fun LazyRowForSample() {
-    val items = listOf("A", "B", "C")
-    LazyRowFor(items) {
-        Text("Item is $it")
-    }
-}
-
-@Sampled
-@Composable
-fun LazyColumnForIndexedSample() {
-    val items = listOf("A", "B", "C")
-    LazyColumnForIndexed(items) { index, item ->
-        Text("Item at index $index is $item")
-    }
-}
-
-@Sampled
-@Composable
-fun LazyRowForIndexedSample() {
-    val items = listOf("A", "B", "C")
-    LazyRowForIndexed(items) { index, item ->
-        Text("Item at index $index is $item")
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/MultitouchGestureSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/MultitouchGestureSamples.kt
index a9c5f93..ff1a5a3 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/MultitouchGestureSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/MultitouchGestureSamples.kt
@@ -37,12 +37,12 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.unit.IntOffset
+import kotlin.math.roundToInt
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun DetectMultitouchGestures() {
@@ -51,7 +51,7 @@
     var offsetX by remember { mutableStateOf(0f) }
     var offsetY by remember { mutableStateOf(0f) }
     Box(
-        Modifier.offset({ offsetX }, { offsetY })
+        Modifier.offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
             .graphicsLayer(
                 scaleX = zoom,
                 scaleY = zoom,
@@ -72,7 +72,6 @@
     )
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun CalculateRotation() {
@@ -83,7 +82,7 @@
             .background(Color.Blue)
             .pointerInput {
                 forEachGesture {
-                    handlePointerInput {
+                    awaitPointerEventScope {
                         awaitFirstDown()
                         do {
                             val event = awaitPointerEvent()
@@ -97,7 +96,6 @@
     )
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun CalculateZoom() {
@@ -108,7 +106,7 @@
             .background(Color.Blue)
             .pointerInput {
                 forEachGesture {
-                    handlePointerInput {
+                    awaitPointerEventScope {
                         awaitFirstDown()
                         do {
                             val event = awaitPointerEvent()
@@ -121,7 +119,6 @@
     )
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun CalculatePan() {
@@ -129,12 +126,12 @@
     val offsetY = remember { mutableStateOf(0f) }
     Box(
         Modifier
-            .offset({ offsetX.value }, { offsetY.value })
+            .offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
             .graphicsLayer()
             .background(Color.Blue)
             .pointerInput {
                 forEachGesture {
-                    handlePointerInput {
+                    awaitPointerEventScope {
                         awaitFirstDown()
                         do {
                             val event = awaitPointerEvent()
@@ -149,7 +146,6 @@
     )
 }
 
-@OptIn(ExperimentalPointerInput::class)
 @Composable
 @Sampled
 fun CalculateCentroidSize() {
@@ -163,7 +159,7 @@
             }
             .pointerInput {
                 forEachGesture {
-                    handlePointerInput {
+                    awaitPointerEventScope {
                         awaitFirstDown().also {
                             position = it.current.position
                         }
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/ScrollerSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/ScrollerSamples.kt
index e2c75d2..e3aa95b 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/ScrollerSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/ScrollerSamples.kt
@@ -36,10 +36,9 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.HorizontalGradient
 import androidx.compose.ui.graphics.TileMode
-import androidx.compose.ui.graphics.VerticalGradient
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 
@@ -88,7 +87,7 @@
 @Composable
 fun HorizontalScrollSample() {
     val scrollState = rememberScrollState()
-    val gradient = HorizontalGradient(
+    val gradient = Brush.horizontalGradient(
         listOf(Color.Red, Color.Blue, Color.Green), 0.0f, 10000.0f, TileMode.Repeated
     )
     Box(
@@ -103,7 +102,7 @@
 @Composable
 fun VerticalScrollExample() {
     val scrollState = rememberScrollState()
-    val gradient = VerticalGradient(
+    val gradient = Brush.verticalGradient(
         listOf(Color.Red, Color.Blue, Color.Green), 0.0f, 10000.0f, TileMode.Repeated
     )
     Box(
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ClickableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ClickableTest.kt
index bb5c386..354009e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ClickableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ClickableTest.kt
@@ -29,7 +29,6 @@
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHasClickAction
-import androidx.compose.ui.test.assertHasNoClickAction
 import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsNotEnabled
 import androidx.compose.ui.test.center
@@ -102,7 +101,7 @@
 
         rule.onNodeWithTag("myClickable")
             .assertIsNotEnabled()
-            .assertHasNoClickAction()
+            .assertHasClickAction()
     }
 
     @Test
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/FocusableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/FocusableTest.kt
index d113774..3c1ab2a 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/FocusableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/FocusableTest.kt
@@ -22,9 +22,8 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
@@ -47,7 +46,6 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalFocus::class)
 class FocusableTest {
 
     @get:Rule
@@ -98,21 +96,20 @@
 
     @Test
     fun focusableTest_focusAcquire() {
-        val requester = FocusRequester()
-        val otherRequester = FocusRequester()
+        val (focusReference, otherFocusReference) = FocusReference.createRefs()
         rule.setContent {
             Box {
                 BasicText(
                     "focusableText",
                     modifier = Modifier
                         .testTag(focusTag)
-                        .focusRequester(requester)
+                        .focusReference(focusReference)
                         .focusable()
                 )
                 BasicText(
                     "otherFocusableText",
                     modifier = Modifier
-                        .focusRequester(otherRequester)
+                        .focusReference(otherFocusReference)
                         .focusable()
                 )
             }
@@ -122,14 +119,14 @@
             .assertIsNotFocused()
 
         rule.runOnIdle {
-            requester.requestFocus()
+            focusReference.requestFocus()
         }
 
         rule.onNodeWithTag(focusTag)
             .assertIsFocused()
 
         rule.runOnIdle {
-            otherRequester.requestFocus()
+            otherFocusReference.requestFocus()
         }
 
         rule.onNodeWithTag(focusTag)
@@ -139,21 +136,20 @@
     @Test
     fun focusableTest_interactionState() {
         val interactionState = InteractionState()
-        val requester = FocusRequester()
-        val otherRequester = FocusRequester()
+        val (focusReference, otherFocusReference) = FocusReference.createRefs()
         rule.setContent {
             Box {
                 BasicText(
                     "focusableText",
                     modifier = Modifier
                         .testTag(focusTag)
-                        .focusRequester(requester)
+                        .focusReference(focusReference)
                         .focusable(interactionState = interactionState)
                 )
                 BasicText(
                     "otherFocusableText",
                     modifier = Modifier
-                        .focusRequester(otherRequester)
+                        .focusReference(otherFocusReference)
                         .focusable()
                 )
             }
@@ -164,7 +160,7 @@
         }
 
         rule.runOnIdle {
-            requester.requestFocus()
+            focusReference.requestFocus()
         }
 
         rule.runOnIdle {
@@ -172,7 +168,7 @@
         }
 
         rule.runOnIdle {
-            otherRequester.requestFocus()
+            otherFocusReference.requestFocus()
         }
 
         rule.runOnIdle {
@@ -183,7 +179,7 @@
     @Test
     fun focusableTest_interactionState_resetWhenDisposed() {
         val interactionState = InteractionState()
-        val requester = FocusRequester()
+        val focusReference = FocusReference()
         var emitFocusableText by mutableStateOf(true)
 
         rule.setContent {
@@ -193,7 +189,7 @@
                         "focusableText",
                         modifier = Modifier
                             .testTag(focusTag)
-                            .focusRequester(requester)
+                            .focusReference(focusReference)
                             .focusable(interactionState = interactionState)
                     )
                 }
@@ -205,7 +201,7 @@
         }
 
         rule.runOnIdle {
-            requester.requestFocus()
+            focusReference.requestFocus()
         }
 
         rule.runOnIdle {
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 591300c..d092269 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
@@ -58,6 +58,7 @@
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.test.performClick
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import org.junit.Assert
@@ -302,6 +303,7 @@
     }
 
     @Test
+    @LargeTest
     fun testImageScalesNonuniformly() {
         val imageComposableWidth = imageWidth * 3
         val imageComposableHeight = imageHeight * 7
@@ -524,6 +526,7 @@
     }
 
     @Test
+    @LargeTest
     fun testPainterResourceWithImage() {
         val testTag = "testTag"
         var imageColor = Color.Black
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
index d4a9fa4..b163cd1 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
@@ -21,6 +21,8 @@
 import androidx.compose.animation.core.ManualFrameClock
 import androidx.compose.animation.core.advanceClockMillis
 import androidx.compose.foundation.animation.FlingConfig
+import androidx.compose.foundation.animation.smoothScrollBy
+import androidx.compose.foundation.gestures.Scrollable
 import androidx.compose.foundation.gestures.ScrollableController
 import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.layout.Box
@@ -32,6 +34,10 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollDispatcher
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollSource
+import androidx.compose.ui.gesture.nestedscroll.nestedScroll
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
@@ -48,6 +54,7 @@
 import androidx.compose.ui.test.swipe
 import androidx.compose.ui.test.swipeWithVelocity
 import androidx.compose.ui.test.up
+import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.milliseconds
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -435,10 +442,10 @@
         rule.awaitIdle()
         assertThat(total).isEqualTo(0f)
 
-        controller.smoothScrollBy(1000f)
+        (controller as Scrollable).smoothScrollBy(1000f)
         assertThat(total).isWithin(0.001f).of(1000f)
 
-        controller.smoothScrollBy(-200f)
+        (controller as Scrollable).smoothScrollBy(-200f)
         assertThat(total).isWithin(0.001f).of(800f)
     }
 
@@ -511,7 +518,6 @@
                 Box(
                     contentAlignment = Alignment.Center,
                     modifier = Modifier
-                        .testTag(scrollableBoxTag)
                         .preferredSize(300.dp)
                         .scrollable(
                             controller = outerState,
@@ -519,15 +525,89 @@
                         )
                 ) {
                     Box(
-                        modifier = Modifier.preferredSize(300.dp).scrollable(
-                            controller = innerState,
-                            orientation = Orientation.Horizontal
-                        )
+                        modifier = Modifier.testTag(scrollableBoxTag)
+                            .preferredSize(300.dp)
+                            .scrollable(
+                                controller = innerState,
+                                orientation = Orientation.Horizontal
+                            )
                     )
                 }
             }
         }
         rule.onNodeWithTag(scrollableBoxTag).performGesture {
+            this.swipeWithVelocity(
+                start = this.center,
+                end = Offset(this.center.x + 200f, this.center.y),
+                duration = 300.milliseconds,
+                endVelocity = 0f
+            )
+        }
+        val lastEqualDrag = rule.runOnIdle {
+            assertThat(innerDrag).isGreaterThan(0f)
+            assertThat(outerDrag).isGreaterThan(0f)
+            // we consumed half delta in child, so exactly half should go to the parent
+            assertThat(outerDrag).isEqualTo(innerDrag)
+            innerDrag
+        }
+        advanceClockWhileAwaitersExist(clock)
+        advanceClockWhileAwaitersExist(clock)
+        rule.runOnIdle {
+            // values should be the same since no fling
+            assertThat(innerDrag).isEqualTo(lastEqualDrag)
+            assertThat(outerDrag).isEqualTo(lastEqualDrag)
+        }
+    }
+
+    @Test
+    @OptIn(ExperimentalTesting::class)
+    fun scrollable_nestedFling() = runBlockingWithManualClock { clock ->
+        var innerDrag = 0f
+        var outerDrag = 0f
+        val animationClock = monotonicFrameAnimationClockOf(coroutineContext, clock)
+        val outerState = ScrollableController(
+            consumeScrollDelta = {
+                outerDrag += it
+                it
+            },
+            flingConfig = FlingConfig(decayAnimation = ExponentialDecay()),
+            animationClock = animationClock
+        )
+        val innerState = ScrollableController(
+            consumeScrollDelta = {
+                innerDrag += it / 2
+                it / 2
+            },
+            flingConfig = FlingConfig(decayAnimation = ExponentialDecay()),
+            animationClock = animationClock
+        )
+
+        rule.setContent {
+            Box {
+                Box(
+                    contentAlignment = Alignment.Center,
+                    modifier = Modifier
+                        .preferredSize(300.dp)
+                        .scrollable(
+                            controller = outerState,
+                            orientation = Orientation.Horizontal
+                        )
+                ) {
+                    Box(
+                        modifier = Modifier
+                            .testTag(scrollableBoxTag)
+                            .preferredSize(300.dp)
+                            .scrollable(
+                                controller = innerState,
+                                orientation = Orientation.Horizontal
+                            )
+                    )
+                }
+            }
+        }
+
+        // swipe again with velocity
+        rule.onNodeWithTag(scrollableBoxTag).performGesture {
             this.swipe(
                 start = this.center,
                 end = Offset(this.center.x + 200f, this.center.y),
@@ -541,16 +621,234 @@
             assertThat(outerDrag).isEqualTo(innerDrag)
             innerDrag
         }
+        // advance clocks, triggering fling
         advanceClockWhileAwaitersExist(clock)
         advanceClockWhileAwaitersExist(clock)
-        // and nothing should change as we don't do nested fling
         rule.runOnIdle {
-            assertThat(outerDrag).isEqualTo(lastEqualDrag)
+            assertThat(innerDrag).isGreaterThan(lastEqualDrag)
+            assertThat(outerDrag).isGreaterThan(lastEqualDrag)
         }
     }
 
     @Test
     @OptIn(ExperimentalTesting::class)
+    fun scrollable_nestedScrollAbove_respectsPreConsumption() =
+        runBlockingWithManualClock { clock ->
+            var value = 0f
+            var lastReceivedPreScrollAvailable = 0f
+            val preConsumeFraction = 0.7f
+            val animationClock = monotonicFrameAnimationClockOf(coroutineContext, clock)
+            val controller = ScrollableController(
+                consumeScrollDelta = {
+                    val expected = lastReceivedPreScrollAvailable * (1 - preConsumeFraction)
+                    assertThat(it - expected).isWithin(0.01f)
+                    value += it
+                    it
+                },
+                flingConfig = FlingConfig(decayAnimation = ExponentialDecay()),
+                animationClock = animationClock
+            )
+            val preConsumingParent = object : NestedScrollConnection {
+                override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                    lastReceivedPreScrollAvailable = available.x
+                    return available * preConsumeFraction
+                }
+
+                override fun onPreFling(available: Velocity): Velocity {
+                    // consume all velocity
+                    return available
+                }
+            }
+
+            rule.setContent {
+                Box {
+                    Box(
+                        contentAlignment = Alignment.Center,
+                        modifier = Modifier
+                            .preferredSize(300.dp)
+                            .nestedScroll(preConsumingParent)
+                    ) {
+                        Box(
+                            modifier = Modifier.preferredSize(300.dp)
+                                .testTag(scrollableBoxTag)
+                                .scrollable(
+                                    controller = controller,
+                                    orientation = Orientation.Horizontal
+                                )
+                        )
+                    }
+                }
+            }
+
+            rule.onNodeWithTag(scrollableBoxTag).performGesture {
+                this.swipe(
+                    start = this.center,
+                    end = Offset(this.center.x + 200f, this.center.y),
+                    duration = 300.milliseconds
+                )
+            }
+
+            val preFlingValue = rule.runOnIdle { value }
+            advanceClockWhileAwaitersExist(clock)
+            advanceClockWhileAwaitersExist(clock)
+            rule.runOnIdle {
+                // if scrollable respects prefling consumption, it should fling 0px since we
+                // preconsume all
+                assertThat(preFlingValue).isEqualTo(value)
+            }
+        }
+
+    @Test
+    @OptIn(ExperimentalTesting::class)
+    fun scrollable_nestedScrollAbove_proxiesPostCycles() =
+        runBlockingWithManualClock { clock ->
+            var value = 0f
+            var expectedLeft = 0f
+            val velocityFlung = 5000f
+            val animationClock = monotonicFrameAnimationClockOf(coroutineContext, clock)
+            val controller = ScrollableController(
+                consumeScrollDelta = {
+                    val toConsume = it * 0.345f
+                    value += toConsume
+                    expectedLeft = it - toConsume
+                    toConsume
+                },
+                flingConfig = FlingConfig(decayAnimation = ExponentialDecay()),
+                animationClock = animationClock
+            )
+            val parent = object : NestedScrollConnection {
+                override fun onPostScroll(
+                    consumed: Offset,
+                    available: Offset,
+                    source: NestedScrollSource
+                ): Offset {
+                    // we should get in post scroll as much as left in controller callback
+                    assertThat(available.x).isEqualTo(expectedLeft)
+                    return available
+                }
+
+                override fun onPostFling(
+                    consumed: Velocity,
+                    available: Velocity,
+                    onFinished: (Velocity) -> Unit
+                ) {
+                    assertThat(available.pixelsPerSecond)
+                        .isEqualTo(
+                            Offset(x = velocityFlung, y = 0f) - consumed.pixelsPerSecond
+                        )
+                    onFinished.invoke(available)
+                }
+            }
+
+            rule.setContent {
+                Box {
+                    Box(
+                        contentAlignment = Alignment.Center,
+                        modifier = Modifier
+                            .preferredSize(300.dp)
+                            .nestedScroll(parent)
+                    ) {
+                        Box(
+                            modifier = Modifier.preferredSize(300.dp)
+                                .testTag(scrollableBoxTag)
+                                .scrollable(
+                                    controller = controller,
+                                    orientation = Orientation.Horizontal
+                                )
+                        )
+                    }
+                }
+            }
+
+            rule.onNodeWithTag(scrollableBoxTag).performGesture {
+                this.swipeWithVelocity(
+                    start = this.center,
+                    end = Offset(this.center.x + 500f, this.center.y),
+                    duration = 300.milliseconds,
+                    endVelocity = velocityFlung
+                )
+            }
+
+            advanceClockWhileAwaitersExist(clock)
+            advanceClockWhileAwaitersExist(clock)
+
+            // all assertions in callback above
+        }
+
+    @Test
+    @OptIn(ExperimentalTesting::class)
+    fun scrollable_nestedScrollBelow_listensDispatches() =
+        runBlockingWithManualClock { clock ->
+            var value = 0f
+            var expectedConsumed = 0f
+            val animationClock = monotonicFrameAnimationClockOf(coroutineContext, clock)
+            val controller = ScrollableController(
+                consumeScrollDelta = {
+                    expectedConsumed = it * 0.3f
+                    value += expectedConsumed
+                    expectedConsumed
+                },
+                flingConfig = FlingConfig(decayAnimation = ExponentialDecay()),
+                animationClock = animationClock
+            )
+            val child = object : NestedScrollConnection {}
+            val dispatcher = NestedScrollDispatcher()
+
+            rule.setContent {
+                Box {
+                    Box(
+                        modifier = Modifier.preferredSize(300.dp)
+
+                            .scrollable(
+                                controller = controller,
+                                orientation = Orientation.Horizontal
+                            )
+                    ) {
+                        Box(
+                            Modifier.preferredSize(200.dp)
+                                .testTag(scrollableBoxTag)
+                                .nestedScroll(child, dispatcher)
+                        )
+                    }
+                }
+            }
+
+            val lastValueBeforeFling = rule.runOnIdle {
+                val preScrollConsumed = dispatcher
+                    .dispatchPreScroll(Offset(20f, 20f), NestedScrollSource.Drag)
+                // scrollable is not interested in pre scroll
+                assertThat(preScrollConsumed).isEqualTo(Offset.Zero)
+
+                val consumed = dispatcher.dispatchPostScroll(
+                    Offset(20f, 20f),
+                    Offset(50f, 50f),
+                    NestedScrollSource.Drag
+                )
+                assertThat(consumed.x - expectedConsumed).isWithin(0.001f)
+
+                val preFlingConsumed = dispatcher
+                    .dispatchPreFling(Velocity(Offset(50f, 50f)))
+                // scrollable won't participate in the pre fling
+                assertThat(preFlingConsumed).isEqualTo(Velocity.Zero)
+
+                dispatcher.dispatchPostFling(
+                    Velocity(Offset(1000f, 1000f)),
+                    Velocity(Offset(2000f, 2000f))
+                )
+                value
+            }
+
+            advanceClockWhileAwaitersExist(clock)
+            advanceClockWhileAwaitersExist(clock)
+
+            rule.runOnIdle {
+                // catch that scrollable caught our post fling and flung
+                assertThat(value).isGreaterThan(lastValueBeforeFling)
+            }
+        }
+
+    @Test
+    @OptIn(ExperimentalTesting::class)
     fun scrollable_interactionState() = runBlocking {
         val interactionState = InteractionState()
         var total = 0f
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 847de67..fc5d1c5 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
@@ -27,9 +27,8 @@
 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.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.RectangleShape
@@ -54,7 +53,7 @@
 import java.util.concurrent.TimeUnit
 
 @LargeTest
-@OptIn(ExperimentalFocus::class, ExperimentalTesting::class)
+@OptIn(ExperimentalTesting::class)
 class TextFieldCursorTest {
 
     @get:Rule
@@ -83,7 +82,7 @@
                 modifier = Modifier
                     .preferredSize(width, height)
                     .background(Color.White)
-                    .focusObserver { if (it.isFocused) latch.countDown() },
+                    .onFocusChanged { if (it.isFocused) latch.countDown() },
                 cursorColor = Color.Red
             )
         }
@@ -118,7 +117,7 @@
                     modifier = Modifier
                         .preferredSize(width, height)
                         .background(Color.White)
-                        .focusObserver { if (it.isFocused) latch.countDown() },
+                        .onFocusChanged { if (it.isFocused) latch.countDown() },
                     cursorColor = Color.Red
                 )
             }
@@ -168,7 +167,7 @@
                     modifier = Modifier
                         .preferredSize(width, height)
                         .background(Color.White)
-                        .focusObserver { if (it.isFocused) latch.countDown() },
+                        .onFocusChanged { if (it.isFocused) latch.countDown() },
                     cursorColor = Color.Unspecified
                 )
             }
@@ -223,8 +222,7 @@
                     modifier = Modifier
                         .preferredSize(width, height)
                         .background(Color.White)
-                        .focusObserver { if (it.isFocused) latch.countDown() },
-
+                        .onFocusChanged { if (it.isFocused) latch.countDown() },
                     cursorColor = Color.Red
                 )
             }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldFocusTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldFocusTest.kt
index 53591c6..cdafb15 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldFocusTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldFocusTest.kt
@@ -22,11 +22,10 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -37,7 +36,6 @@
 import org.junit.runner.RunWith
 
 @LargeTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
 class TextFieldFocusTest {
     @get:Rule
@@ -50,8 +48,8 @@
             BasicTextField(
                 value = editor.value,
                 modifier = Modifier
-                    .focusRequester(data.focusRequester)
-                    .focusObserver { data.focused = it.isFocused }
+                    .focusReference(data.focusReference)
+                    .onFocusChanged { data.focused = it.isFocused }
                     .width(10.dp),
                 onValueChange = {
                     editor.value = it
@@ -60,7 +58,7 @@
         }
     }
 
-    data class FocusTestData(val focusRequester: FocusRequester, var focused: Boolean = false)
+    data class FocusTestData(val focusReference: FocusReference, var focused: Boolean = false)
 
     @Test
     fun requestFocus() {
@@ -69,16 +67,16 @@
         rule.runOnUiThread {
             rule.setContent {
                 testDataList = listOf(
-                    FocusTestData(FocusRequester()),
-                    FocusTestData(FocusRequester()),
-                    FocusTestData(FocusRequester())
+                    FocusTestData(FocusReference()),
+                    FocusTestData(FocusReference()),
+                    FocusTestData(FocusReference())
                 )
 
                 TextFieldApp(testDataList)
             }
         }
 
-        rule.runOnIdle { testDataList[0].focusRequester.requestFocus() }
+        rule.runOnIdle { testDataList[0].focusReference.requestFocus() }
 
         rule.runOnIdle {
             assertThat(testDataList[0].focused).isTrue()
@@ -86,14 +84,14 @@
             assertThat(testDataList[2].focused).isFalse()
         }
 
-        rule.runOnIdle { testDataList[1].focusRequester.requestFocus() }
+        rule.runOnIdle { testDataList[1].focusReference.requestFocus() }
         rule.runOnIdle {
             assertThat(testDataList[0].focused).isFalse()
             assertThat(testDataList[1].focused).isTrue()
             assertThat(testDataList[2].focused).isFalse()
         }
 
-        rule.runOnIdle { testDataList[2].focusRequester.requestFocus() }
+        rule.runOnIdle { testDataList[2].focusReference.requestFocus() }
         rule.runOnIdle {
             assertThat(testDataList[0].focused).isFalse()
             assertThat(testDataList[1].focused).isFalse()
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldScrollTest.kt
index 3ee3b91..0009365 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldScrollTest.kt
@@ -57,6 +57,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
@@ -247,6 +248,7 @@
     }
 
     @Test
+    @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testTextField_horizontal_scrolledAndClipped() {
         val scrollerPosition = TextFieldScrollerPosition()
@@ -294,6 +296,7 @@
     }
 
     @Test
+    @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testTextField_vertical_scrolledAndClipped() {
         val scrollerPosition = TextFieldScrollerPosition()
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 1c589bf..b7e6d80 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
@@ -37,9 +37,8 @@
 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
-import androidx.compose.ui.focusObserver
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.layout.onGloballyPositioned
@@ -97,10 +96,7 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(
-    ExperimentalFocus::class,
-    ExperimentalFoundationApi::class
-)
+@OptIn(ExperimentalFoundationApi::class)
 class TextFieldTest {
     @get:Rule
     val rule = createComposeRule()
@@ -119,7 +115,7 @@
             ) {
                 BasicTextField(
                     value = state.value,
-                    modifier = Modifier.fillMaxSize().focusObserver { isFocused = it.isFocused },
+                    modifier = Modifier.fillMaxSize().onFocusChanged { isFocused = it.isFocused },
                     onValueChange = { state.value = it }
                 )
             }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ToggleableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ToggleableTest.kt
index eb28ac3..89fcd8e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ToggleableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ToggleableTest.kt
@@ -33,7 +33,6 @@
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHasClickAction
-import androidx.compose.ui.test.assertHasNoClickAction
 import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsNotEnabled
 import androidx.compose.ui.test.assertIsOff
@@ -174,7 +173,7 @@
 
         rule.onNode(isToggleable())
             .assertIsNotEnabled()
-            .assertHasNoClickAction()
+            .assertHasClickAction()
     }
 
     @Test
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyArrangementsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyArrangementsTest.kt
new file mode 100644
index 0000000..994dba7
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyArrangementsTest.kt
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.InternalLayoutApi
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Providers
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.AmbientLayoutDirection
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+@OptIn(InternalLayoutApi::class)
+class LazyArrangementsTest {
+
+    private val ContainerTag = "ContainerTag"
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private var itemSize: Dp = Dp.Infinity
+    private var containerSize: Dp = Dp.Infinity
+
+    @Before
+    fun before() {
+        with(rule.density) {
+            itemSize = 50.toDp()
+        }
+        containerSize = itemSize * 5
+    }
+
+    // cases when we have not enough items to fill min constraints:
+
+    @Test
+    fun column_defaultArrangementIsTop() {
+        rule.setContent {
+            LazyColumn(
+                modifier = Modifier.size(containerSize)
+            ) {
+                items((0..1).toList()) {
+                    Box(Modifier.size(itemSize).testTag(it.toString()))
+                }
+            }
+        }
+
+        assertArrangementForTwoItems(Arrangement.Top)
+    }
+
+    @Test
+    fun column_centerArrangement() {
+        composeColumnWith(Arrangement.Center)
+        assertArrangementForTwoItems(Arrangement.Center)
+    }
+
+    @Test
+    fun column_bottomArrangement() {
+        composeColumnWith(Arrangement.Bottom)
+        assertArrangementForTwoItems(Arrangement.Bottom)
+    }
+
+    @Test
+    fun column_spacedArrangementNotFillingViewport() {
+        val arrangement = Arrangement.spacedBy(10.dp)
+        composeColumnWith(arrangement)
+        assertArrangementForTwoItems(arrangement)
+    }
+
+    @Test
+    fun row_defaultArrangementIsStart() {
+        rule.setContent {
+            LazyRow(
+                modifier = Modifier.size(containerSize)
+            ) {
+                items((0..1).toList()) {
+                    Box(Modifier.size(itemSize).testTag(it.toString()))
+                }
+            }
+        }
+
+        assertArrangementForTwoItems(Arrangement.Start, LayoutDirection.Ltr)
+    }
+
+    @Test
+    fun row_centerArrangement() {
+        composeRowWith(Arrangement.Center, LayoutDirection.Ltr)
+        assertArrangementForTwoItems(Arrangement.Center, LayoutDirection.Ltr)
+    }
+
+    @Test
+    fun row_endArrangement() {
+        composeRowWith(Arrangement.End, LayoutDirection.Ltr)
+        assertArrangementForTwoItems(Arrangement.End, LayoutDirection.Ltr)
+    }
+
+    @Test
+    fun row_spacedArrangementNotFillingViewport() {
+        val arrangement = Arrangement.spacedBy(10.dp)
+        composeRowWith(arrangement, LayoutDirection.Ltr)
+        assertArrangementForTwoItems(arrangement, LayoutDirection.Ltr)
+    }
+
+    @Test
+    fun row_rtl_startArrangement() {
+        composeRowWith(Arrangement.Center, LayoutDirection.Rtl)
+        assertArrangementForTwoItems(Arrangement.Center, LayoutDirection.Rtl)
+    }
+
+    @Test
+    fun row_rtl_endArrangement() {
+        composeRowWith(Arrangement.End, LayoutDirection.Rtl)
+        assertArrangementForTwoItems(Arrangement.End, LayoutDirection.Rtl)
+    }
+
+    @Test
+    fun row_rtl_spacedArrangementNotFillingViewport() {
+        val arrangement = Arrangement.spacedBy(10.dp)
+        composeRowWith(arrangement, LayoutDirection.Rtl)
+        assertArrangementForTwoItems(arrangement, LayoutDirection.Rtl)
+    }
+
+    // wrap content and spacing
+
+    @Test
+    fun column_spacing_affects_wrap_content() {
+        rule.setContent {
+            LazyColumn(
+                verticalArrangement = Arrangement.spacedBy(itemSize),
+                modifier = Modifier.testTag(ContainerTag)
+            ) {
+                items((0..1).toList()) {
+                    Box(Modifier.size(itemSize))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(ContainerTag)
+            .assertWidthIsEqualTo(itemSize)
+            .assertHeightIsEqualTo(itemSize * 3)
+    }
+
+    @Test
+    fun row_spacing_affects_wrap_content() {
+        rule.setContent {
+            LazyRow(
+                horizontalArrangement = Arrangement.spacedBy(itemSize),
+                modifier = Modifier.testTag(ContainerTag)
+            ) {
+                items((0..1).toList()) {
+                    Box(Modifier.size(itemSize))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(ContainerTag)
+            .assertWidthIsEqualTo(itemSize * 3)
+            .assertHeightIsEqualTo(itemSize)
+    }
+
+    // spacing added when we have enough items to fill the viewport
+
+    @Test
+    fun column_spacing_scrolledToTheTop() {
+        rule.setContent {
+            LazyColumn(
+                verticalArrangement = Arrangement.spacedBy(itemSize),
+                modifier = Modifier.size(itemSize * 3.5f)
+            ) {
+                items((0..2).toList()) {
+                    Box(Modifier.size(itemSize).testTag(it.toString()))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertTopPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertTopPositionInRootIsEqualTo(itemSize * 2)
+    }
+
+    @Test
+    fun column_spacing_scrolledToTheBottom() {
+        rule.setContent {
+            LazyColumn(
+                verticalArrangement = Arrangement.spacedBy(itemSize),
+                modifier = Modifier.size(itemSize * 3.5f).testTag(ContainerTag)
+            ) {
+                items((0..2).toList()) {
+                    Box(Modifier.size(itemSize).testTag(it.toString()))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(ContainerTag)
+            .scrollBy(y = itemSize * 2, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertTopPositionInRootIsEqualTo(itemSize * 0.5f)
+
+        rule.onNodeWithTag("2")
+            .assertTopPositionInRootIsEqualTo(itemSize * 2.5f)
+    }
+
+    @Test
+    fun row_spacing_scrolledToTheStart() {
+        rule.setContent {
+            LazyRow(
+                horizontalArrangement = Arrangement.spacedBy(itemSize),
+                modifier = Modifier.size(itemSize * 3.5f)
+            ) {
+                items((0..2).toList()) {
+                    Box(Modifier.size(itemSize).testTag(it.toString()))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertLeftPositionInRootIsEqualTo(itemSize * 2)
+    }
+
+    @Test
+    fun row_spacing_scrolledToTheEnd() {
+        rule.setContent {
+            LazyRow(
+                horizontalArrangement = Arrangement.spacedBy(itemSize),
+                modifier = Modifier.size(itemSize * 3.5f).testTag(ContainerTag)
+            ) {
+                items((0..2).toList()) {
+                    Box(Modifier.size(itemSize).testTag(it.toString()))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(ContainerTag)
+            .scrollBy(x = itemSize * 2, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertLeftPositionInRootIsEqualTo(itemSize * 0.5f)
+
+        rule.onNodeWithTag("2")
+            .assertLeftPositionInRootIsEqualTo(itemSize * 2.5f)
+    }
+
+    // with reverseLayout == true
+
+    @Test
+    fun column_defaultArrangementIsBottomWithReverseLayout() {
+        rule.setContent {
+            LazyColumn(
+                reverseLayout = true,
+                modifier = Modifier.size(containerSize)
+            ) {
+                items((0..1).toList()) {
+                    Box(Modifier.size(itemSize).testTag(it.toString()))
+                }
+            }
+        }
+
+        assertArrangementForTwoItems(Arrangement.Bottom, reversedItemsOrder = true)
+    }
+
+    @Test
+    fun row_defaultArrangementIsEndWithReverseLayout() {
+        rule.setContent {
+            LazyRow(
+                reverseLayout = true,
+                modifier = Modifier.size(containerSize)
+            ) {
+                items((0..1).toList()) {
+                    Box(Modifier.size(itemSize).testTag(it.toString()))
+                }
+            }
+        }
+
+        assertArrangementForTwoItems(
+            Arrangement.End, LayoutDirection.Ltr, reversedItemsOrder = true
+        )
+    }
+
+    fun composeColumnWith(arrangement: Arrangement.Vertical) {
+        rule.setContent {
+            LazyColumn(
+                verticalArrangement = arrangement,
+                modifier = Modifier.size(containerSize)
+            ) {
+                items((0..1).toList()) {
+                    Box(Modifier.size(itemSize).testTag(it.toString()))
+                }
+            }
+        }
+    }
+
+    fun composeRowWith(arrangement: Arrangement.Horizontal, layoutDirection: LayoutDirection) {
+        rule.setContent {
+            Providers(AmbientLayoutDirection provides layoutDirection) {
+                LazyRow(
+                    horizontalArrangement = arrangement,
+                    modifier = Modifier.size(containerSize)
+                ) {
+                    items((0..1).toList()) {
+                        Box(Modifier.size(itemSize).testTag(it.toString()))
+                    }
+                }
+            }
+        }
+    }
+
+    fun assertArrangementForTwoItems(
+        arrangement: Arrangement.Vertical,
+        reversedItemsOrder: Boolean = false
+    ) {
+        with(rule.density) {
+            val sizes = IntArray(2) { itemSize.toIntPx() }
+            val outPositions = IntArray(2) { 0 }
+            arrangement.arrange(containerSize.toIntPx(), sizes, this, outPositions)
+
+            outPositions.forEachIndexed { index, position ->
+                val realIndex = if (reversedItemsOrder) if (index == 0) 1 else 0 else index
+                rule.onNodeWithTag("$realIndex")
+                    .assertTopPositionInRootIsEqualTo(position.toDp())
+            }
+        }
+    }
+
+    fun assertArrangementForTwoItems(
+        arrangement: Arrangement.Horizontal,
+        layoutDirection: LayoutDirection,
+        reversedItemsOrder: Boolean = false
+    ) {
+        with(rule.density) {
+            val sizes = IntArray(2) { itemSize.toIntPx() }
+            val outPositions = IntArray(2) { 0 }
+            arrangement.arrange(containerSize.toIntPx(), sizes, layoutDirection, this, outPositions)
+
+            outPositions.forEachIndexed { index, position ->
+                val realIndex = if (reversedItemsOrder) if (index == 0) 1 else 0 else index
+                val expectedPosition = if (layoutDirection == LayoutDirection.Ltr) {
+                    position.toDp()
+                } else {
+                    containerSize - position.toDp() - itemSize
+                }
+                rule.onNodeWithTag("$realIndex")
+                    .assertLeftPositionInRootIsEqualTo(expectedPosition)
+            }
+        }
+    }
+}
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
deleted file mode 100644
index bbd257b..0000000
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnForTest.kt
+++ /dev/null
@@ -1,1136 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.lazy
-
-import androidx.compose.animation.core.ExponentialDecay
-import androidx.compose.animation.core.ManualAnimationClock
-import androidx.compose.animation.core.snap
-import androidx.compose.foundation.animation.FlingConfig
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.preferredHeight
-import androidx.compose.foundation.layout.preferredSize
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.sizeIn
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.text.BasicText
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.onCommit
-import androidx.compose.runtime.onDispose
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.gesture.TouchSlop
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.assertCountEquals
-import androidx.compose.ui.test.assertHeightIsEqualTo
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertIsEqualTo
-import androidx.compose.ui.test.assertIsNotDisplayed
-import androidx.compose.ui.test.assertPositionInRootIsEqualTo
-import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
-import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.center
-import androidx.compose.ui.test.click
-import androidx.compose.ui.test.getUnclippedBoundsInRoot
-import androidx.compose.ui.test.junit4.StateRestorationTester
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onChildren
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.test.performGesture
-import androidx.compose.ui.test.swipeUp
-import androidx.compose.ui.test.swipeWithVelocity
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.LargeTest
-import com.google.common.collect.Range
-import com.google.common.truth.IntegerSubject
-import com.google.common.truth.Truth.assertThat
-import com.google.common.truth.Truth.assertWithMessage
-import kotlinx.coroutines.runBlocking
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import java.util.concurrent.CountDownLatch
-
-@LargeTest
-@RunWith(AndroidJUnit4::class)
-class LazyColumnForTest {
-    private val LazyColumnForTag = "TestLazyColumnFor"
-
-    @get:Rule
-    val rule = createComposeRule()
-
-    @Test
-    fun compositionsAreDisposed_whenNodesAreScrolledOff() {
-        var composed: Boolean
-        var disposed = false
-        // Ten 31dp spacers in a 300dp list
-        val latch = CountDownLatch(10)
-        // Make it long enough that it's _definitely_ taller than the screen
-        val data = (1..50).toList()
-
-        rule.setContent {
-            // Fixed height to eliminate device size as a factor
-            Box(Modifier.testTag(LazyColumnForTag).preferredHeight(300.dp)) {
-                LazyColumnFor(items = data, modifier = Modifier.fillMaxSize()) {
-                    onCommit {
-                        composed = true
-                        // Signal when everything is done composing
-                        latch.countDown()
-                        onDispose {
-                            disposed = true
-                        }
-                    }
-
-                    // There will be 10 of these in the 300dp box
-                    Spacer(Modifier.preferredHeight(31.dp))
-                }
-            }
-        }
-
-        latch.await()
-        composed = false
-
-        assertWithMessage("Compositions were disposed before we did any scrolling")
-            .that(disposed).isFalse()
-
-        // Mostly a validity check, this is not part of the behavior under test
-        assertWithMessage("Additional composition occurred for no apparent reason")
-            .that(composed).isFalse()
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .performGesture { swipeUp() }
-
-        rule.waitForIdle()
-
-        assertWithMessage("No additional items were composed after scroll, scroll didn't work")
-            .that(composed).isTrue()
-
-        // We may need to modify this test once we prefetch/cache items outside the viewport
-        assertWithMessage(
-            "No compositions were disposed after scrolling, compositions were leaked"
-        ).that(disposed).isTrue()
-    }
-
-    @Test
-    fun compositionsAreDisposed_whenDataIsChanged() {
-        var composed = 0
-        var disposals = 0
-        val data1 = (1..3).toList()
-        val data2 = (4..5).toList() // smaller, to ensure removal is handled properly
-
-        var part2 by mutableStateOf(false)
-
-        rule.setContent {
-            LazyColumnFor(
-                items = if (!part2) data1 else data2,
-                modifier = Modifier.testTag(LazyColumnForTag).fillMaxSize()
-            ) {
-                onCommit {
-                    composed++
-                    onDispose {
-                        disposals++
-                    }
-                }
-
-                Spacer(Modifier.height(50.dp))
-            }
-        }
-
-        rule.runOnIdle {
-            assertWithMessage("Not all items were composed")
-                .that(composed).isEqualTo(data1.size)
-            composed = 0
-
-            part2 = true
-        }
-
-        rule.runOnIdle {
-            assertWithMessage(
-                "No additional items were composed after data change, something didn't work"
-            ).that(composed).isEqualTo(data2.size)
-
-            // We may need to modify this test once we prefetch/cache items outside the viewport
-            assertWithMessage(
-                "Not enough compositions were disposed after scrolling, compositions were leaked"
-            ).that(disposals).isEqualTo(data1.size)
-        }
-    }
-
-    @Test
-    fun compositionsAreDisposed_whenAdapterListIsDisposed() {
-        var emitAdapterList by mutableStateOf(true)
-        var disposeCalledOnFirstItem = false
-        var disposeCalledOnSecondItem = false
-
-        rule.setContent {
-            if (emitAdapterList) {
-                LazyColumnFor(
-                    items = listOf(0, 1),
-                    modifier = Modifier.fillMaxSize()
-                ) {
-                    Box(Modifier.size(100.dp))
-                    onDispose {
-                        if (it == 1) {
-                            disposeCalledOnFirstItem = true
-                        } else {
-                            disposeCalledOnSecondItem = true
-                        }
-                    }
-                }
-            }
-        }
-
-        rule.runOnIdle {
-            assertWithMessage("First item is not immediately disposed")
-                .that(disposeCalledOnFirstItem).isFalse()
-            assertWithMessage("Second item is not immediately disposed")
-                .that(disposeCalledOnFirstItem).isFalse()
-            emitAdapterList = false
-        }
-
-        rule.runOnIdle {
-            assertWithMessage("First item is correctly disposed")
-                .that(disposeCalledOnFirstItem).isTrue()
-            assertWithMessage("Second item is correctly disposed")
-                .that(disposeCalledOnSecondItem).isTrue()
-        }
-    }
-
-    @Test
-    fun removeItemsTest() {
-        val startingNumItems = 3
-        var numItems = startingNumItems
-        var numItemsModel by mutableStateOf(numItems)
-        val tag = "List"
-        rule.setContent {
-            LazyColumnFor((1..numItemsModel).toList(), modifier = Modifier.testTag(tag)) {
-                BasicText("$it")
-            }
-        }
-
-        while (numItems >= 0) {
-            // Confirm the number of children to ensure there are no extra items
-            rule.onNodeWithTag(tag)
-                .onChildren()
-                .assertCountEquals(numItems)
-
-            // Confirm the children's content
-            for (i in 1..3) {
-                rule.onNodeWithText("$i").apply {
-                    if (i <= numItems) {
-                        assertExists()
-                    } else {
-                        assertDoesNotExist()
-                    }
-                }
-            }
-            numItems--
-            if (numItems >= 0) {
-                // Don't set the model to -1
-                rule.runOnIdle { numItemsModel = numItems }
-            }
-        }
-    }
-
-    @Test
-    fun changingDataTest() {
-        val dataLists = listOf(
-            (1..3).toList(),
-            (4..8).toList(),
-            (3..4).toList()
-        )
-        var dataModel by mutableStateOf(dataLists[0])
-        val tag = "List"
-        rule.setContent {
-            LazyColumnFor(dataModel, modifier = Modifier.testTag(tag)) {
-                BasicText("$it")
-            }
-        }
-
-        for (data in dataLists) {
-            rule.runOnIdle { dataModel = data }
-
-            // Confirm the number of children to ensure there are no extra items
-            val numItems = data.size
-            rule.onNodeWithTag(tag)
-                .onChildren()
-                .assertCountEquals(numItems)
-
-            // Confirm the children's content
-            for (item in data) {
-                rule.onNodeWithText("$item").assertExists()
-            }
-        }
-    }
-
-    @Test
-    fun whenItemsAreInitiallyCreatedWith0SizeWeCanScrollWhenTheyExpanded() {
-        val thirdTag = "third"
-        val items = (1..3).toList()
-        var thirdHasSize by mutableStateOf(false)
-
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.fillMaxWidth()
-                    .preferredHeight(100.dp)
-                    .testTag(LazyColumnForTag)
-            ) {
-                if (it == 3) {
-                    Spacer(
-                        Modifier.testTag(thirdTag)
-                            .fillParentMaxWidth()
-                            .preferredHeight(if (thirdHasSize) 60.dp else 0.dp)
-                    )
-                } else {
-                    Spacer(Modifier.fillParentMaxWidth().preferredHeight(60.dp))
-                }
-            }
-        }
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 21.dp, density = rule.density)
-
-        rule.onNodeWithTag(thirdTag)
-            .assertExists()
-            .assertIsNotDisplayed()
-
-        rule.runOnIdle {
-            thirdHasSize = true
-        }
-
-        rule.waitForIdle()
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 10.dp, density = rule.density)
-
-        rule.onNodeWithTag(thirdTag)
-            .assertIsDisplayed()
-    }
-
-    @Test
-    fun lazyColumnWrapsContent() = with(rule.density) {
-        val itemInsideLazyColumn = "itemInsideLazyColumn"
-        val itemOutsideLazyColumn = "itemOutsideLazyColumn"
-        var sameSizeItems by mutableStateOf(true)
-
-        rule.setContent {
-            Row {
-                LazyColumnFor(
-                    items = listOf(1, 2),
-                    modifier = Modifier.testTag(LazyColumnForTag)
-                ) {
-                    if (it == 1) {
-                        Spacer(Modifier.preferredSize(50.dp).testTag(itemInsideLazyColumn))
-                    } else {
-                        Spacer(Modifier.preferredSize(if (sameSizeItems) 50.dp else 70.dp))
-                    }
-                }
-                Spacer(Modifier.preferredSize(50.dp).testTag(itemOutsideLazyColumn))
-            }
-        }
-
-        rule.onNodeWithTag(itemInsideLazyColumn)
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag(itemOutsideLazyColumn)
-            .assertIsDisplayed()
-
-        var lazyColumnBounds = rule.onNodeWithTag(LazyColumnForTag)
-            .getUnclippedBoundsInRoot()
-
-        assertThat(lazyColumnBounds.left.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
-        assertThat(lazyColumnBounds.right.toIntPx()).isWithin1PixelFrom(50.dp.toIntPx())
-        assertThat(lazyColumnBounds.top.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
-        assertThat(lazyColumnBounds.bottom.toIntPx()).isWithin1PixelFrom(100.dp.toIntPx())
-
-        rule.runOnIdle {
-            sameSizeItems = false
-        }
-
-        rule.waitForIdle()
-
-        rule.onNodeWithTag(itemInsideLazyColumn)
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag(itemOutsideLazyColumn)
-            .assertIsDisplayed()
-
-        lazyColumnBounds = rule.onNodeWithTag(LazyColumnForTag)
-            .getUnclippedBoundsInRoot()
-
-        assertThat(lazyColumnBounds.left.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
-        assertThat(lazyColumnBounds.right.toIntPx()).isWithin1PixelFrom(70.dp.toIntPx())
-        assertThat(lazyColumnBounds.top.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
-        assertThat(lazyColumnBounds.bottom.toIntPx()).isWithin1PixelFrom(120.dp.toIntPx())
-    }
-
-    private val firstItemTag = "firstItemTag"
-    private val secondItemTag = "secondItemTag"
-
-    private fun prepareLazyColumnsItemsAlignment(horizontalGravity: Alignment.Horizontal) {
-        rule.setContent {
-            LazyColumnFor(
-                items = listOf(1, 2),
-                modifier = Modifier.testTag(LazyColumnForTag).width(100.dp),
-                horizontalAlignment = horizontalGravity
-            ) {
-                if (it == 1) {
-                    Spacer(Modifier.preferredSize(50.dp).testTag(firstItemTag))
-                } else {
-                    Spacer(Modifier.preferredSize(70.dp).testTag(secondItemTag))
-                }
-            }
-        }
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag(secondItemTag)
-            .assertIsDisplayed()
-
-        val lazyColumnBounds = rule.onNodeWithTag(LazyColumnForTag)
-            .getUnclippedBoundsInRoot()
-
-        with(rule.density) {
-            // Verify the width of the column
-            assertThat(lazyColumnBounds.left.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
-            assertThat(lazyColumnBounds.right.toIntPx()).isWithin1PixelFrom(100.dp.toIntPx())
-        }
-    }
-
-    @Test
-    fun lazyColumnAlignmentCenterHorizontally() {
-        prepareLazyColumnsItemsAlignment(Alignment.CenterHorizontally)
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertPositionInRootIsEqualTo(25.dp, 0.dp)
-
-        rule.onNodeWithTag(secondItemTag)
-            .assertPositionInRootIsEqualTo(15.dp, 50.dp)
-    }
-
-    @Test
-    fun lazyColumnAlignmentStart() {
-        prepareLazyColumnsItemsAlignment(Alignment.Start)
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertPositionInRootIsEqualTo(0.dp, 0.dp)
-
-        rule.onNodeWithTag(secondItemTag)
-            .assertPositionInRootIsEqualTo(0.dp, 50.dp)
-    }
-
-    @Test
-    fun lazyColumnAlignmentEnd() {
-        prepareLazyColumnsItemsAlignment(Alignment.End)
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertPositionInRootIsEqualTo(50.dp, 0.dp)
-
-        rule.onNodeWithTag(secondItemTag)
-            .assertPositionInRootIsEqualTo(30.dp, 50.dp)
-    }
-
-    @Test
-    fun itemFillingParentWidth() {
-        rule.setContent {
-            LazyColumnFor(
-                items = listOf(0),
-                modifier = Modifier.size(width = 100.dp, height = 150.dp)
-            ) {
-                Spacer(Modifier.fillParentMaxWidth().height(50.dp).testTag(firstItemTag))
-            }
-        }
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertWidthIsEqualTo(100.dp)
-            .assertHeightIsEqualTo(50.dp)
-    }
-
-    @Test
-    fun itemFillingParentHeight() {
-        rule.setContent {
-            LazyColumnFor(
-                items = listOf(0),
-                modifier = Modifier.size(width = 100.dp, height = 150.dp)
-            ) {
-                Spacer(Modifier.width(50.dp).fillParentMaxHeight().testTag(firstItemTag))
-            }
-        }
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertWidthIsEqualTo(50.dp)
-            .assertHeightIsEqualTo(150.dp)
-    }
-
-    @Test
-    fun itemFillingParentSize() {
-        rule.setContent {
-            LazyColumnFor(
-                items = listOf(0),
-                modifier = Modifier.size(width = 100.dp, height = 150.dp)
-            ) {
-                Spacer(Modifier.fillParentMaxSize().testTag(firstItemTag))
-            }
-        }
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertWidthIsEqualTo(100.dp)
-            .assertHeightIsEqualTo(150.dp)
-    }
-
-    @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 {
-            LazyColumnFor(
-                items = listOf(0),
-                modifier = Modifier.size(parentSize)
-            ) {
-                Spacer(Modifier.fillParentMaxSize().testTag(firstItemTag))
-            }
-        }
-
-        rule.runOnIdle {
-            parentSize = 150.dp
-        }
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertWidthIsEqualTo(150.dp)
-            .assertHeightIsEqualTo(150.dp)
-    }
-
-    @Test
-    fun whenNotAnymoreAvailableItemWasDisplayed() {
-        var items by mutableStateOf((1..30).toList())
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag)
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        // after scroll we will display items 16-20
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 300.dp, density = rule.density)
-
-        rule.runOnIdle {
-            items = (1..10).toList()
-        }
-
-        // there is no item 16 anymore so we will just display the last items 6-10
-        rule.onNodeWithTag("6")
-            .assertTopPositionIsAlmost(0.dp)
-    }
-
-    @Test
-    fun whenFewDisplayedItemsWereRemoved() {
-        var items by mutableStateOf((1..10).toList())
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag)
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        // after scroll we will display items 6-10
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 100.dp, density = rule.density)
-
-        rule.runOnIdle {
-            items = (1..8).toList()
-        }
-
-        // there are no more items 9 and 10, so we have to scroll back
-        rule.onNodeWithTag("4")
-            .assertTopPositionIsAlmost(0.dp)
-    }
-
-    @Test
-    fun whenItemsBecameEmpty() {
-        var items by mutableStateOf((1..10).toList())
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.sizeIn(maxHeight = 100.dp).testTag(LazyColumnForTag)
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        // after scroll we will display items 2-6
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 20.dp, density = rule.density)
-
-        rule.runOnIdle {
-            items = emptyList()
-        }
-
-        // there are no more items so the LazyColumn is zero sized
-        rule.onNodeWithTag(LazyColumnForTag)
-            .assertWidthIsEqualTo(0.dp)
-            .assertHeightIsEqualTo(0.dp)
-
-        // and has no children
-        rule.onNodeWithTag("1")
-            .assertDoesNotExist()
-        rule.onNodeWithTag("2")
-            .assertDoesNotExist()
-    }
-
-    @Test
-    fun scrollBackAndForth() {
-        val items by mutableStateOf((1..20).toList())
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag)
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        // after scroll we will display items 6-10
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 100.dp, density = rule.density)
-
-        // and scroll back
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = (-100).dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertTopPositionIsAlmost(0.dp)
-    }
-
-    @Test
-    fun tryToScrollBackwardWhenAlreadyOnTop() {
-        val items by mutableStateOf((1..20).toList())
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag)
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        // we already displaying the first item, so this should do nothing
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = (-50).dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertTopPositionIsAlmost(0.dp)
-        rule.onNodeWithTag("5")
-            .assertTopPositionIsAlmost(80.dp)
-    }
-
-    @Test
-    fun contentOfNotStableItemsIsNotRecomposedDuringScroll() {
-        val items = listOf(NotStable(1), NotStable(2))
-        var firstItemRecomposed = 0
-        var secondItemRecomposed = 0
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag)
-            ) {
-                if (it.count == 1) {
-                    firstItemRecomposed++
-                } else {
-                    secondItemRecomposed++
-                }
-                Spacer(Modifier.size(75.dp))
-            }
-        }
-
-        rule.runOnIdle {
-            assertThat(firstItemRecomposed).isEqualTo(1)
-            assertThat(secondItemRecomposed).isEqualTo(1)
-        }
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = (50).dp, density = rule.density)
-
-        rule.runOnIdle {
-            assertThat(firstItemRecomposed).isEqualTo(1)
-            assertThat(secondItemRecomposed).isEqualTo(1)
-        }
-    }
-
-    @Test
-    fun onlyOneMeasurePassForScrollEvent() {
-        val items by mutableStateOf((1..20).toList())
-        lateinit var state: LazyListState
-        rule.setContent {
-            state = rememberLazyListState()
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        val initialMeasurePasses = state.numMeasurePasses
-
-        rule.runOnIdle {
-            with(rule.density) {
-                state.onScroll(-110.dp.toPx())
-            }
-        }
-
-        rule.waitForIdle()
-
-        assertThat(state.numMeasurePasses).isEqualTo(initialMeasurePasses + 1)
-    }
-
-    @Test
-    fun stateUpdatedAfterScroll() {
-        val items by mutableStateOf((1..20).toList())
-        lateinit var state: LazyListState
-        rule.setContent {
-            state = rememberLazyListState()
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.runOnIdle {
-            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
-            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
-        }
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 30.dp, density = rule.density)
-
-        rule.runOnIdle {
-            assertThat(state.firstVisibleItemIndex).isEqualTo(1)
-
-            with(rule.density) {
-                // TODO(b/169232491): test scrolling doesn't appear to be scrolling exactly the right
-                //  number of pixels
-                val expectedOffset = 10.dp.toIntPx()
-                val tolerance = 2.dp.toIntPx()
-                assertThat(state.firstVisibleItemScrollOffset).isEqualTo(expectedOffset, tolerance)
-            }
-        }
-    }
-
-    @Test
-    fun isAnimationRunningUpdate() {
-        val items by mutableStateOf((1..20).toList())
-        val clock = ManualAnimationClock(0L)
-        val state = LazyListState(
-            flingConfig = FlingConfig(ExponentialDecay()),
-            animationClock = clock
-        )
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.runOnIdle {
-            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
-            assertThat(state.isAnimationRunning).isEqualTo(false)
-        }
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .performGesture { swipeUp() }
-
-        rule.runOnIdle {
-            clock.clockTimeMillis += 100
-            assertThat(state.firstVisibleItemIndex).isNotEqualTo(0)
-            assertThat(state.isAnimationRunning).isEqualTo(true)
-        }
-
-        // TODO (jelle): this should be down, and not click to be 100% fair
-        rule.onNodeWithTag(LazyColumnForTag)
-            .performGesture { click() }
-
-        rule.runOnIdle {
-            assertThat(state.isAnimationRunning).isEqualTo(false)
-        }
-    }
-
-    @Test
-    fun stateUpdatedAfterScrollWithinTheSameItem() {
-        val items by mutableStateOf((1..20).toList())
-        lateinit var state: LazyListState
-        rule.setContent {
-            state = rememberLazyListState()
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 10.dp, density = rule.density)
-
-        rule.runOnIdle {
-            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
-            with(rule.density) {
-                val expectedOffset = 10.dp.toIntPx()
-                val tolerance = 2.dp.toIntPx()
-                assertThat(state.firstVisibleItemScrollOffset)
-                    .isEqualTo(expectedOffset, tolerance)
-            }
-        }
-    }
-
-    @Test
-    fun initialScrollIsApplied() {
-        val items by mutableStateOf((0..20).toList())
-        lateinit var state: LazyListState
-        val expectedOffset = with(rule.density) { 10.dp.toIntPx() }
-        rule.setContent {
-            state = rememberLazyListState(2, expectedOffset)
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.runOnIdle {
-            assertThat(state.firstVisibleItemIndex).isEqualTo(2)
-            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(expectedOffset)
-        }
-
-        rule.onNodeWithTag("2")
-            .assertTopPositionInRootIsEqualTo((-10).dp)
-    }
-
-    @Test
-    fun stateIsRestored() {
-        val restorationTester = StateRestorationTester(rule)
-        val items by mutableStateOf((1..20).toList())
-        var state: LazyListState? = null
-        restorationTester.setContent {
-            state = rememberLazyListState()
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag),
-                state = state!!
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 30.dp, density = rule.density)
-
-        val (index, scrollOffset) = rule.runOnIdle {
-            state!!.firstVisibleItemIndex to state!!.firstVisibleItemScrollOffset
-        }
-
-        state = null
-
-        restorationTester.emulateSavedInstanceStateRestore()
-
-        rule.runOnIdle {
-            assertThat(state!!.firstVisibleItemIndex).isEqualTo(index)
-            assertThat(state!!.firstVisibleItemScrollOffset).isEqualTo(scrollOffset)
-        }
-    }
-
-    @Test
-    fun scroll_makeListSmaller_scroll() {
-        var items by mutableStateOf((1..100).toList())
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag)
-            ) {
-                Spacer(Modifier.size(10.dp).testTag("$it"))
-            }
-        }
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 300.dp, density = rule.density)
-
-        rule.runOnIdle {
-            items = (1..11).toList()
-        }
-
-        // try to scroll after the data set has been updated. this was causing a crash previously
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = (-10).dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertIsDisplayed()
-    }
-
-    @Test
-    fun snapToItemIndex() {
-        val items by mutableStateOf((1..20).toList())
-        lateinit var state: LazyListState
-        rule.setContent {
-            state = rememberLazyListState()
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.runOnIdle {
-            runBlocking {
-                state.snapToItemIndex(3, 10)
-            }
-            assertThat(state.firstVisibleItemIndex).isEqualTo(3)
-            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
-        }
-    }
-
-    @Test
-    fun itemsAreNotRedrawnDuringScroll() {
-        val items = (0..20).toList()
-        val redrawCount = Array(6) { 0 }
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag)
-            ) {
-                Spacer(
-                    Modifier.size(20.dp)
-                        .drawBehind { redrawCount[it]++ }
-                )
-            }
-        }
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .scrollBy(y = 10.dp, density = rule.density)
-
-        rule.runOnIdle {
-            redrawCount.forEachIndexed { index, i ->
-                assertWithMessage("Item with index $index was redrawn $i times")
-                    .that(i).isEqualTo(1)
-            }
-        }
-    }
-
-    @Test
-    fun itemInvalidationIsNotCausingAnotherItemToRedraw() {
-        val items = (0..1).toList()
-        val redrawCount = Array(2) { 0 }
-        var stateUsedInDrawScope by mutableStateOf(false)
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyColumnForTag)
-            ) {
-                Spacer(
-                    Modifier.size(50.dp)
-                        .drawBehind {
-                            redrawCount[it]++
-                            if (it == 1) {
-                                stateUsedInDrawScope.hashCode()
-                            }
-                        }
-                )
-            }
-        }
-
-        rule.runOnIdle {
-            stateUsedInDrawScope = true
-        }
-
-        rule.runOnIdle {
-            assertWithMessage("First items is not expected to be redrawn")
-                .that(redrawCount[0]).isEqualTo(1)
-            assertWithMessage("Second items is expected to be redrawn")
-                .that(redrawCount[1]).isEqualTo(2)
-        }
-    }
-
-    @Test
-    fun notVisibleAnymoreItemNotAffectingCrossAxisSize() {
-        val items = (0..1).toList()
-        val itemSize = with(rule.density) { 30.toDp() }
-        val itemSizeMinusOne = with(rule.density) { 29.toDp() }
-        lateinit var state: LazyListState
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                state = rememberLazyListState().also { state = it },
-                modifier = Modifier.height(itemSizeMinusOne).testTag(LazyColumnForTag)
-            ) {
-                Spacer(
-                    if (it == 0) {
-                        Modifier.width(30.dp).height(itemSizeMinusOne)
-                    } else {
-                        Modifier.width(20.dp).height(itemSize)
-                    }
-                )
-            }
-        }
-
-        state.scrollBy(itemSize)
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .assertWidthIsEqualTo(20.dp)
-    }
-
-    @Test
-    fun itemStillVisibleAfterOverscrollIsAffectingCrossAxisSize() {
-        val items = (0..2).toList()
-        val itemSize = with(rule.density) { 30.toDp() }
-        lateinit var state: LazyListState
-        rule.setContent {
-            LazyColumnFor(
-                items = items,
-                state = rememberLazyListState().also { state = it },
-                modifier = Modifier.height(itemSize * 1.75f).testTag(LazyColumnForTag)
-            ) {
-                Spacer(
-                    if (it == 0) {
-                        Modifier.width(30.dp).height(itemSize / 2)
-                    } else if (it == 1) {
-                        Modifier.width(20.dp).height(itemSize / 2)
-                    } else {
-                        Modifier.width(20.dp).height(itemSize)
-                    }
-                )
-            }
-        }
-
-        state.scrollBy(itemSize)
-
-        rule.onNodeWithTag(LazyColumnForTag)
-            .assertWidthIsEqualTo(30.dp)
-    }
-
-    private fun SemanticsNodeInteraction.assertTopPositionIsAlmost(expected: Dp) {
-        getUnclippedBoundsInRoot().top.assertIsEqualTo(expected, tolerance = 1.dp)
-    }
-
-    private fun LazyListState.scrollBy(offset: Dp) {
-        runBlocking {
-            smoothScrollBy(with(rule.density) { offset.toIntPx().toFloat() }, snap())
-        }
-    }
-}
-
-data class NotStable(val count: Int)
-
-internal fun IntegerSubject.isWithin1PixelFrom(expected: Int) {
-    isEqualTo(expected, 1)
-}
-
-internal fun IntegerSubject.isEqualTo(expected: Int, tolerance: Int) {
-    isIn(Range.closed(expected - tolerance, expected + tolerance))
-}
-
-internal fun SemanticsNodeInteraction.scrollBy(x: Dp = 0.dp, y: Dp = 0.dp, density: Density) =
-    performGesture {
-        with(density) {
-            val touchSlop = TouchSlop.toIntPx()
-            val xPx = x.toIntPx()
-            val yPx = y.toIntPx()
-            val offsetX = if (xPx > 0) xPx + touchSlop else if (xPx < 0) xPx - touchSlop else 0
-            val offsetY = if (yPx > 0) yPx + touchSlop else if (yPx < 0) yPx - touchSlop else 0
-            swipeWithVelocity(
-                start = center,
-                end = Offset(center.x - offsetX, center.y - offsetY),
-                endVelocity = 0f
-            )
-        }
-    }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
index 7e629eb3..10e9e7a 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
@@ -16,101 +16,78 @@
 
 package androidx.compose.foundation.lazy
 
+import androidx.compose.animation.core.ExponentialDecay
+import androidx.compose.animation.core.ManualAnimationClock
+import androidx.compose.animation.core.snap
+import androidx.compose.foundation.animation.FlingConfig
+import androidx.compose.foundation.animation.smoothScrollBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.onCommit
+import androidx.compose.runtime.onDispose
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.TouchSlop
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.assertCountEquals
+import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEqualTo
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.center
+import androidx.compose.ui.test.click
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onChildren
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performGesture
+import androidx.compose.ui.test.swipeUp
+import androidx.compose.ui.test.swipeWithVelocity
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
+import androidx.test.filters.LargeTest
+import com.google.common.collect.Range
+import com.google.common.truth.IntegerSubject
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.runBlocking
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import java.util.concurrent.CountDownLatch
 
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4::class)
 class LazyColumnTest {
-    private val LazyColumnTag = "LazyColumnTag"
+    private val LazyListTag = "LazyListTag"
 
     @get:Rule
     val rule = createComposeRule()
 
     @Test
-    fun lazyColumnShowsItem() {
-        val itemTestTag = "itemTestTag"
-
-        rule.setContent {
-            LazyColumn {
-                item {
-                    Spacer(
-                        Modifier.preferredHeight(10.dp).fillParentMaxWidth().testTag(itemTestTag)
-                    )
-                }
-            }
-        }
-
-        rule.onNodeWithTag(itemTestTag)
-            .assertIsDisplayed()
-    }
-
-    @Test
-    fun lazyColumnShowsItems() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            LazyColumn(Modifier.preferredHeight(200.dp)) {
-                items(items) {
-                    Spacer(Modifier.preferredHeight(101.dp).fillParentMaxWidth().testTag(it))
-                }
-            }
-        }
-
-        rule.onNodeWithTag("1")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("3")
-            .assertDoesNotExist()
-
-        rule.onNodeWithTag("4")
-            .assertDoesNotExist()
-    }
-
-    @Test
-    fun lazyColumnShowsIndexedItems() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            LazyColumn(Modifier.preferredHeight(200.dp)) {
-                itemsIndexed(items) { index, item ->
-                    Spacer(
-                        Modifier.preferredHeight(101.dp).fillParentMaxWidth()
-                            .testTag("$index-$item")
-                    )
-                }
-            }
-        }
-
-        rule.onNodeWithTag("0-1")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("1-2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("2-3")
-            .assertDoesNotExist()
-
-        rule.onNodeWithTag("3-4")
-            .assertDoesNotExist()
-    }
-
-    @Test
     fun lazyColumnShowsCombinedItems() {
         val itemTestTag = "itemTestTag"
         val items = listOf(1, 2).map { it.toString() }
@@ -155,59 +132,6 @@
     }
 
     @Test
-    fun lazyColumnShowsItemsOnScroll() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            LazyColumn(Modifier.preferredHeight(200.dp).testTag(LazyColumnTag)) {
-                items(items) {
-                    Spacer(Modifier.preferredHeight(101.dp).fillParentMaxWidth().testTag(it))
-                }
-            }
-        }
-
-        rule.onNodeWithTag(LazyColumnTag)
-            .scrollBy(y = 50.dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("3")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("4")
-            .assertDoesNotExist()
-    }
-
-    @Test
-    fun lazyColumnScrollHidesItem() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            LazyColumn(Modifier.preferredHeight(200.dp).testTag(LazyColumnTag)) {
-                items(items) {
-                    Spacer(Modifier.preferredHeight(101.dp).fillParentMaxWidth().testTag(it))
-                }
-            }
-        }
-
-        rule.onNodeWithTag(LazyColumnTag)
-            .scrollBy(y = 103.dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertDoesNotExist()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("3")
-            .assertIsDisplayed()
-    }
-
-    @Test
     fun lazyColumnAllowEmptyListItems() {
         val itemTag = "itemTag"
 
@@ -253,4 +177,1050 @@
         rule.onNodeWithTag("3")
             .assertDoesNotExist()
     }
-}
\ No newline at end of file
+
+    @Test
+    fun compositionsAreDisposed_whenNodesAreScrolledOff() {
+        var composed: Boolean
+        var disposed = false
+        // Ten 31dp spacers in a 300dp list
+        val latch = CountDownLatch(10)
+        // Make it long enough that it's _definitely_ taller than the screen
+        val data = (1..50).toList()
+
+        rule.setContent {
+            // Fixed height to eliminate device size as a factor
+            Box(Modifier.testTag(LazyListTag).preferredHeight(300.dp)) {
+                LazyColumn(Modifier.fillMaxSize()) {
+                    items(data) {
+                        onCommit {
+                            composed = true
+                            // Signal when everything is done composing
+                            latch.countDown()
+                            onDispose {
+                                disposed = true
+                            }
+                        }
+
+                        // There will be 10 of these in the 300dp box
+                        Spacer(Modifier.preferredHeight(31.dp))
+                    }
+                }
+            }
+        }
+
+        latch.await()
+        composed = false
+
+        assertWithMessage("Compositions were disposed before we did any scrolling")
+            .that(disposed).isFalse()
+
+        // Mostly a validity check, this is not part of the behavior under test
+        assertWithMessage("Additional composition occurred for no apparent reason")
+            .that(composed).isFalse()
+
+        rule.onNodeWithTag(LazyListTag)
+            .performGesture { swipeUp() }
+
+        rule.waitForIdle()
+
+        assertWithMessage("No additional items were composed after scroll, scroll didn't work")
+            .that(composed).isTrue()
+
+        // We may need to modify this test once we prefetch/cache items outside the viewport
+        assertWithMessage(
+            "No compositions were disposed after scrolling, compositions were leaked"
+        ).that(disposed).isTrue()
+    }
+
+    @Test
+    fun compositionsAreDisposed_whenDataIsChanged() {
+        var composed = 0
+        var disposals = 0
+        val data1 = (1..3).toList()
+        val data2 = (4..5).toList() // smaller, to ensure removal is handled properly
+
+        var part2 by mutableStateOf(false)
+
+        rule.setContent {
+            LazyColumn(Modifier.testTag(LazyListTag).fillMaxSize()) {
+                items(if (!part2) data1 else data2) {
+                    onCommit {
+                        composed++
+                        onDispose {
+                            disposals++
+                        }
+                    }
+
+                    Spacer(Modifier.height(50.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertWithMessage("Not all items were composed")
+                .that(composed).isEqualTo(data1.size)
+            composed = 0
+
+            part2 = true
+        }
+
+        rule.runOnIdle {
+            assertWithMessage(
+                "No additional items were composed after data change, something didn't work"
+            ).that(composed).isEqualTo(data2.size)
+
+            // We may need to modify this test once we prefetch/cache items outside the viewport
+            assertWithMessage(
+                "Not enough compositions were disposed after scrolling, compositions were leaked"
+            ).that(disposals).isEqualTo(data1.size)
+        }
+    }
+
+    @Test
+    fun compositionsAreDisposed_whenAdapterListIsDisposed() {
+        var emitAdapterList by mutableStateOf(true)
+        var disposeCalledOnFirstItem = false
+        var disposeCalledOnSecondItem = false
+
+        rule.setContent {
+            if (emitAdapterList) {
+                LazyColumn(Modifier.fillMaxSize()) {
+                    items(listOf(0, 1)) {
+                        Box(Modifier.size(100.dp))
+                        onDispose {
+                            if (it == 1) {
+                                disposeCalledOnFirstItem = true
+                            } else {
+                                disposeCalledOnSecondItem = true
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertWithMessage("First item is not immediately disposed")
+                .that(disposeCalledOnFirstItem).isFalse()
+            assertWithMessage("Second item is not immediately disposed")
+                .that(disposeCalledOnFirstItem).isFalse()
+            emitAdapterList = false
+        }
+
+        rule.runOnIdle {
+            assertWithMessage("First item is correctly disposed")
+                .that(disposeCalledOnFirstItem).isTrue()
+            assertWithMessage("Second item is correctly disposed")
+                .that(disposeCalledOnSecondItem).isTrue()
+        }
+    }
+
+    @Test
+    fun removeItemsTest() {
+        val startingNumItems = 3
+        var numItems = startingNumItems
+        var numItemsModel by mutableStateOf(numItems)
+        val tag = "List"
+        rule.setContent {
+            LazyColumn(Modifier.testTag(tag)) {
+                items((1..numItemsModel).toList()) {
+                    BasicText("$it")
+                }
+            }
+        }
+
+        while (numItems >= 0) {
+            // Confirm the number of children to ensure there are no extra items
+            rule.onNodeWithTag(tag)
+                .onChildren()
+                .assertCountEquals(numItems)
+
+            // Confirm the children's content
+            for (i in 1..3) {
+                rule.onNodeWithText("$i").apply {
+                    if (i <= numItems) {
+                        assertExists()
+                    } else {
+                        assertDoesNotExist()
+                    }
+                }
+            }
+            numItems--
+            if (numItems >= 0) {
+                // Don't set the model to -1
+                rule.runOnIdle { numItemsModel = numItems }
+            }
+        }
+    }
+
+    @Test
+    fun changingDataTest() {
+        val dataLists = listOf(
+            (1..3).toList(),
+            (4..8).toList(),
+            (3..4).toList()
+        )
+        var dataModel by mutableStateOf(dataLists[0])
+        val tag = "List"
+        rule.setContent {
+            LazyColumn(Modifier.testTag(tag)) {
+                items(dataModel) {
+                    BasicText("$it")
+                }
+            }
+        }
+
+        for (data in dataLists) {
+            rule.runOnIdle { dataModel = data }
+
+            // Confirm the number of children to ensure there are no extra items
+            val numItems = data.size
+            rule.onNodeWithTag(tag)
+                .onChildren()
+                .assertCountEquals(numItems)
+
+            // Confirm the children's content
+            for (item in data) {
+                rule.onNodeWithText("$item").assertExists()
+            }
+        }
+    }
+
+    @Test
+    fun whenItemsAreInitiallyCreatedWith0SizeWeCanScrollWhenTheyExpanded() {
+        val thirdTag = "third"
+        val items = (1..3).toList()
+        var thirdHasSize by mutableStateOf(false)
+
+        rule.setContent {
+            LazyColumn(
+                Modifier.fillMaxWidth()
+                    .preferredHeight(100.dp)
+                    .testTag(LazyListTag)
+            ) {
+                items(items) {
+                    if (it == 3) {
+                        Spacer(
+                            Modifier.testTag(thirdTag)
+                                .fillParentMaxWidth()
+                                .preferredHeight(if (thirdHasSize) 60.dp else 0.dp)
+                        )
+                    } else {
+                        Spacer(Modifier.fillParentMaxWidth().preferredHeight(60.dp))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 21.dp, density = rule.density)
+
+        rule.onNodeWithTag(thirdTag)
+            .assertExists()
+            .assertIsNotDisplayed()
+
+        rule.runOnIdle {
+            thirdHasSize = true
+        }
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 10.dp, density = rule.density)
+
+        rule.onNodeWithTag(thirdTag)
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun lazyColumnWrapsContent() = with(rule.density) {
+        val itemInsideLazyColumn = "itemInsideLazyColumn"
+        val itemOutsideLazyColumn = "itemOutsideLazyColumn"
+        var sameSizeItems by mutableStateOf(true)
+
+        rule.setContent {
+            Row {
+                LazyColumn(Modifier.testTag(LazyListTag)) {
+                    items(listOf(1, 2)) {
+                        if (it == 1) {
+                            Spacer(Modifier.preferredSize(50.dp).testTag(itemInsideLazyColumn))
+                        } else {
+                            Spacer(Modifier.preferredSize(if (sameSizeItems) 50.dp else 70.dp))
+                        }
+                    }
+                }
+                Spacer(Modifier.preferredSize(50.dp).testTag(itemOutsideLazyColumn))
+            }
+        }
+
+        rule.onNodeWithTag(itemInsideLazyColumn)
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag(itemOutsideLazyColumn)
+            .assertIsDisplayed()
+
+        var lazyColumnBounds = rule.onNodeWithTag(LazyListTag)
+            .getUnclippedBoundsInRoot()
+
+        assertThat(lazyColumnBounds.left.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
+        assertThat(lazyColumnBounds.right.toIntPx()).isWithin1PixelFrom(50.dp.toIntPx())
+        assertThat(lazyColumnBounds.top.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
+        assertThat(lazyColumnBounds.bottom.toIntPx()).isWithin1PixelFrom(100.dp.toIntPx())
+
+        rule.runOnIdle {
+            sameSizeItems = false
+        }
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(itemInsideLazyColumn)
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag(itemOutsideLazyColumn)
+            .assertIsDisplayed()
+
+        lazyColumnBounds = rule.onNodeWithTag(LazyListTag)
+            .getUnclippedBoundsInRoot()
+
+        assertThat(lazyColumnBounds.left.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
+        assertThat(lazyColumnBounds.right.toIntPx()).isWithin1PixelFrom(70.dp.toIntPx())
+        assertThat(lazyColumnBounds.top.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
+        assertThat(lazyColumnBounds.bottom.toIntPx()).isWithin1PixelFrom(120.dp.toIntPx())
+    }
+
+    private val firstItemTag = "firstItemTag"
+    private val secondItemTag = "secondItemTag"
+
+    private fun prepareLazyColumnsItemsAlignment(horizontalGravity: Alignment.Horizontal) {
+        rule.setContent {
+            LazyColumn(
+                Modifier.testTag(LazyListTag).width(100.dp),
+                horizontalAlignment = horizontalGravity
+            ) {
+                items(listOf(1, 2)) {
+                    if (it == 1) {
+                        Spacer(Modifier.preferredSize(50.dp).testTag(firstItemTag))
+                    } else {
+                        Spacer(Modifier.preferredSize(70.dp).testTag(secondItemTag))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag(secondItemTag)
+            .assertIsDisplayed()
+
+        val lazyColumnBounds = rule.onNodeWithTag(LazyListTag)
+            .getUnclippedBoundsInRoot()
+
+        with(rule.density) {
+            // Verify the width of the column
+            assertThat(lazyColumnBounds.left.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
+            assertThat(lazyColumnBounds.right.toIntPx()).isWithin1PixelFrom(100.dp.toIntPx())
+        }
+    }
+
+    @Test
+    fun lazyColumnAlignmentCenterHorizontally() {
+        prepareLazyColumnsItemsAlignment(Alignment.CenterHorizontally)
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertPositionInRootIsEqualTo(25.dp, 0.dp)
+
+        rule.onNodeWithTag(secondItemTag)
+            .assertPositionInRootIsEqualTo(15.dp, 50.dp)
+    }
+
+    @Test
+    fun lazyColumnAlignmentStart() {
+        prepareLazyColumnsItemsAlignment(Alignment.Start)
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+
+        rule.onNodeWithTag(secondItemTag)
+            .assertPositionInRootIsEqualTo(0.dp, 50.dp)
+    }
+
+    @Test
+    fun lazyColumnAlignmentEnd() {
+        prepareLazyColumnsItemsAlignment(Alignment.End)
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertPositionInRootIsEqualTo(50.dp, 0.dp)
+
+        rule.onNodeWithTag(secondItemTag)
+            .assertPositionInRootIsEqualTo(30.dp, 50.dp)
+    }
+
+    @Test
+    fun itemFillingParentWidth() {
+        rule.setContent {
+            LazyColumn(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.fillParentMaxWidth().height(50.dp).testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(100.dp)
+            .assertHeightIsEqualTo(50.dp)
+    }
+
+    @Test
+    fun itemFillingParentHeight() {
+        rule.setContent {
+            LazyColumn(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.width(50.dp).fillParentMaxHeight().testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(50.dp)
+            .assertHeightIsEqualTo(150.dp)
+    }
+
+    @Test
+    fun itemFillingParentSize() {
+        rule.setContent {
+            LazyColumn(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.fillParentMaxSize().testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(100.dp)
+            .assertHeightIsEqualTo(150.dp)
+    }
+
+    @Test
+    fun itemFillingParentWidthFraction() {
+        rule.setContent {
+            LazyColumn(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.fillParentMaxWidth(0.6f).height(50.dp).testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(60.dp)
+            .assertHeightIsEqualTo(50.dp)
+    }
+
+    @Test
+    fun itemFillingParentHeightFraction() {
+        rule.setContent {
+            LazyColumn(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.width(50.dp).fillParentMaxHeight(0.2f).testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(50.dp)
+            .assertHeightIsEqualTo(30.dp)
+    }
+
+    @Test
+    fun itemFillingParentSizeFraction() {
+        rule.setContent {
+            LazyColumn(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    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 {
+            LazyColumn(Modifier.size(parentSize)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.fillParentMaxSize().testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            parentSize = 150.dp
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(150.dp)
+            .assertHeightIsEqualTo(150.dp)
+    }
+
+    @Test
+    fun whenNotAnymoreAvailableItemWasDisplayed() {
+        var items by mutableStateOf((1..30).toList())
+        rule.setContent {
+            LazyColumn(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        // after scroll we will display items 16-20
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 300.dp, density = rule.density)
+
+        rule.runOnIdle {
+            items = (1..10).toList()
+        }
+
+        // there is no item 16 anymore so we will just display the last items 6-10
+        rule.onNodeWithTag("6")
+            .assertTopPositionIsAlmost(0.dp)
+    }
+
+    @Test
+    fun whenFewDisplayedItemsWereRemoved() {
+        var items by mutableStateOf((1..10).toList())
+        rule.setContent {
+            LazyColumn(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        // after scroll we will display items 6-10
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 100.dp, density = rule.density)
+
+        rule.runOnIdle {
+            items = (1..8).toList()
+        }
+
+        // there are no more items 9 and 10, so we have to scroll back
+        rule.onNodeWithTag("4")
+            .assertTopPositionIsAlmost(0.dp)
+    }
+
+    @Test
+    fun whenItemsBecameEmpty() {
+        var items by mutableStateOf((1..10).toList())
+        rule.setContent {
+            LazyColumn(Modifier.sizeIn(maxHeight = 100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        // after scroll we will display items 2-6
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 20.dp, density = rule.density)
+
+        rule.runOnIdle {
+            items = emptyList()
+        }
+
+        // there are no more items so the LazyColumn is zero sized
+        rule.onNodeWithTag(LazyListTag)
+            .assertWidthIsEqualTo(0.dp)
+            .assertHeightIsEqualTo(0.dp)
+
+        // and has no children
+        rule.onNodeWithTag("1")
+            .assertDoesNotExist()
+        rule.onNodeWithTag("2")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun scrollBackAndForth() {
+        val items by mutableStateOf((1..20).toList())
+        rule.setContent {
+            LazyColumn(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        // after scroll we will display items 6-10
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 100.dp, density = rule.density)
+
+        // and scroll back
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = (-100).dp, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertTopPositionIsAlmost(0.dp)
+    }
+
+    @Test
+    fun tryToScrollBackwardWhenAlreadyOnTop() {
+        val items by mutableStateOf((1..20).toList())
+        rule.setContent {
+            LazyColumn(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        // we already displaying the first item, so this should do nothing
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = (-50).dp, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertTopPositionIsAlmost(0.dp)
+        rule.onNodeWithTag("5")
+            .assertTopPositionIsAlmost(80.dp)
+    }
+
+    @Test
+    fun contentOfNotStableItemsIsNotRecomposedDuringScroll() {
+        val items = listOf(NotStable(1), NotStable(2))
+        var firstItemRecomposed = 0
+        var secondItemRecomposed = 0
+        rule.setContent {
+            LazyColumn(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    if (it.count == 1) {
+                        firstItemRecomposed++
+                    } else {
+                        secondItemRecomposed++
+                    }
+                    Spacer(Modifier.size(75.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(firstItemRecomposed).isEqualTo(1)
+            assertThat(secondItemRecomposed).isEqualTo(1)
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = (50).dp, density = rule.density)
+
+        rule.runOnIdle {
+            assertThat(firstItemRecomposed).isEqualTo(1)
+            assertThat(secondItemRecomposed).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun onlyOneMeasurePassForScrollEvent() {
+        val items by mutableStateOf((1..20).toList())
+        lateinit var state: LazyListState
+        rule.setContent {
+            state = rememberLazyListState()
+            LazyColumn(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        val initialMeasurePasses = state.numMeasurePasses
+
+        rule.runOnIdle {
+            with(rule.density) {
+                state.onScroll(-110.dp.toPx())
+            }
+        }
+
+        rule.waitForIdle()
+
+        assertThat(state.numMeasurePasses).isEqualTo(initialMeasurePasses + 1)
+    }
+
+    @Test
+    fun stateUpdatedAfterScroll() {
+        val items by mutableStateOf((1..20).toList())
+        lateinit var state: LazyListState
+        rule.setContent {
+            state = rememberLazyListState()
+            LazyColumn(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 30.dp, density = rule.density)
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(1)
+
+            with(rule.density) {
+                // TODO(b/169232491): test scrolling doesn't appear to be scrolling exactly the right
+                //  number of pixels
+                val expectedOffset = 10.dp.toIntPx()
+                val tolerance = 2.dp.toIntPx()
+                assertThat(state.firstVisibleItemScrollOffset).isEqualTo(expectedOffset, tolerance)
+            }
+        }
+    }
+
+    @Test
+    fun isAnimationRunningUpdate() {
+        val items by mutableStateOf((1..20).toList())
+        val clock = ManualAnimationClock(0L)
+        val state = LazyListState(
+            flingConfig = FlingConfig(ExponentialDecay()),
+            animationClock = clock
+        )
+        rule.setContent {
+            LazyColumn(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+            assertThat(state.isAnimationRunning).isEqualTo(false)
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .performGesture { swipeUp() }
+
+        rule.runOnIdle {
+            clock.clockTimeMillis += 100
+            assertThat(state.firstVisibleItemIndex).isNotEqualTo(0)
+            assertThat(state.isAnimationRunning).isEqualTo(true)
+        }
+
+        // TODO (jelle): this should be down, and not click to be 100% fair
+        rule.onNodeWithTag(LazyListTag)
+            .performGesture { click() }
+
+        rule.runOnIdle {
+            assertThat(state.isAnimationRunning).isEqualTo(false)
+        }
+    }
+
+    @Test
+    fun stateUpdatedAfterScrollWithinTheSameItem() {
+        val items by mutableStateOf((1..20).toList())
+        lateinit var state: LazyListState
+        rule.setContent {
+            state = rememberLazyListState()
+            LazyColumn(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 10.dp, density = rule.density)
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+            with(rule.density) {
+                val expectedOffset = 10.dp.toIntPx()
+                val tolerance = 2.dp.toIntPx()
+                assertThat(state.firstVisibleItemScrollOffset)
+                    .isEqualTo(expectedOffset, tolerance)
+            }
+        }
+    }
+
+    @Test
+    fun initialScrollIsApplied() {
+        val items by mutableStateOf((0..20).toList())
+        lateinit var state: LazyListState
+        val expectedOffset = with(rule.density) { 10.dp.toIntPx() }
+        rule.setContent {
+            state = rememberLazyListState(2, expectedOffset)
+            LazyColumn(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(expectedOffset)
+        }
+
+        rule.onNodeWithTag("2")
+            .assertTopPositionInRootIsEqualTo((-10).dp)
+    }
+
+    @Test
+    fun stateIsRestored() {
+        val restorationTester = StateRestorationTester(rule)
+        val items by mutableStateOf((1..20).toList())
+        var state: LazyListState? = null
+        restorationTester.setContent {
+            state = rememberLazyListState()
+            LazyColumn(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state!!
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 30.dp, density = rule.density)
+
+        val (index, scrollOffset) = rule.runOnIdle {
+            state!!.firstVisibleItemIndex to state!!.firstVisibleItemScrollOffset
+        }
+
+        state = null
+
+        restorationTester.emulateSavedInstanceStateRestore()
+
+        rule.runOnIdle {
+            assertThat(state!!.firstVisibleItemIndex).isEqualTo(index)
+            assertThat(state!!.firstVisibleItemScrollOffset).isEqualTo(scrollOffset)
+        }
+    }
+
+    @Test
+    fun scroll_makeListSmaller_scroll() {
+        var items by mutableStateOf((1..100).toList())
+        rule.setContent {
+            LazyColumn(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(10.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 300.dp, density = rule.density)
+
+        rule.runOnIdle {
+            items = (1..11).toList()
+        }
+
+        // try to scroll after the data set has been updated. this was causing a crash previously
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = (-10).dp, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun snapToItemIndex() {
+        val items by mutableStateOf((1..20).toList())
+        lateinit var state: LazyListState
+        rule.setContent {
+            state = rememberLazyListState()
+            LazyColumn(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            runBlocking {
+                state.snapToItemIndex(3, 10)
+            }
+            assertThat(state.firstVisibleItemIndex).isEqualTo(3)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+        }
+    }
+
+    @Test
+    fun itemsAreNotRedrawnDuringScroll() {
+        val items = (0..20).toList()
+        val redrawCount = Array(6) { 0 }
+        rule.setContent {
+            LazyColumn(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(
+                        Modifier.size(20.dp)
+                            .drawBehind { redrawCount[it]++ }
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 10.dp, density = rule.density)
+
+        rule.runOnIdle {
+            redrawCount.forEachIndexed { index, i ->
+                assertWithMessage("Item with index $index was redrawn $i times")
+                    .that(i).isEqualTo(1)
+            }
+        }
+    }
+
+    @Test
+    fun itemInvalidationIsNotCausingAnotherItemToRedraw() {
+        val items = (0..1).toList()
+        val redrawCount = Array(2) { 0 }
+        var stateUsedInDrawScope by mutableStateOf(false)
+        rule.setContent {
+            LazyColumn(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(
+                        Modifier.size(50.dp)
+                            .drawBehind {
+                                redrawCount[it]++
+                                if (it == 1) {
+                                    stateUsedInDrawScope.hashCode()
+                                }
+                            }
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            stateUsedInDrawScope = true
+        }
+
+        rule.runOnIdle {
+            assertWithMessage("First items is not expected to be redrawn")
+                .that(redrawCount[0]).isEqualTo(1)
+            assertWithMessage("Second items is expected to be redrawn")
+                .that(redrawCount[1]).isEqualTo(2)
+        }
+    }
+
+    @Test
+    fun notVisibleAnymoreItemNotAffectingCrossAxisSize() {
+        val items = (0..1).toList()
+        val itemSize = with(rule.density) { 30.toDp() }
+        val itemSizeMinusOne = with(rule.density) { 29.toDp() }
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                Modifier.height(itemSizeMinusOne).testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it }
+            ) {
+                items(items) {
+                    Spacer(
+                        if (it == 0) {
+                            Modifier.width(30.dp).height(itemSizeMinusOne)
+                        } else {
+                            Modifier.width(20.dp).height(itemSize)
+                        }
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(itemSize)
+
+        rule.onNodeWithTag(LazyListTag)
+            .assertWidthIsEqualTo(20.dp)
+    }
+
+    @Test
+    fun itemStillVisibleAfterOverscrollIsAffectingCrossAxisSize() {
+        val items = (0..2).toList()
+        val itemSize = with(rule.density) { 30.toDp() }
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                Modifier.height(itemSize * 1.75f).testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it }
+            ) {
+                items(items) {
+                    Spacer(
+                        if (it == 0) {
+                            Modifier.width(30.dp).height(itemSize / 2)
+                        } else if (it == 1) {
+                            Modifier.width(20.dp).height(itemSize / 2)
+                        } else {
+                            Modifier.width(20.dp).height(itemSize)
+                        }
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(itemSize)
+
+        rule.onNodeWithTag(LazyListTag)
+            .assertWidthIsEqualTo(30.dp)
+    }
+
+    private fun SemanticsNodeInteraction.assertTopPositionIsAlmost(expected: Dp) {
+        getUnclippedBoundsInRoot().top.assertIsEqualTo(expected, tolerance = 1.dp)
+    }
+
+    private fun LazyListState.scrollBy(offset: Dp) {
+        runBlocking {
+            smoothScrollBy(with(rule.density) { offset.toIntPx().toFloat() }, snap())
+        }
+    }
+}
+
+data class NotStable(val count: Int)
+
+internal fun IntegerSubject.isWithin1PixelFrom(expected: Int) {
+    isEqualTo(expected, 1)
+}
+
+internal fun IntegerSubject.isEqualTo(expected: Int, tolerance: Int) {
+    isIn(Range.closed(expected - tolerance, expected + tolerance))
+}
+
+internal fun SemanticsNodeInteraction.scrollBy(x: Dp = 0.dp, y: Dp = 0.dp, density: Density) =
+    performGesture {
+        with(density) {
+            val touchSlop = TouchSlop.toIntPx()
+            val xPx = x.toIntPx()
+            val yPx = y.toIntPx()
+            val offsetX = if (xPx > 0) xPx + touchSlop else if (xPx < 0) xPx - touchSlop else 0
+            val offsetY = if (yPx > 0) yPx + touchSlop else if (yPx < 0) yPx - touchSlop else 0
+            swipeWithVelocity(
+                start = center,
+                end = Offset(center.x - offsetX, center.y - offsetY),
+                endVelocity = 0f
+            )
+        }
+    }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyForIndexedTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyForIndexedTest.kt
deleted file mode 100644
index 37c72b8..0000000
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyForIndexedTest.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.lazy
-
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.preferredHeight
-import androidx.compose.foundation.layout.preferredWidth
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.text.BasicText
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
-import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.unit.dp
-import org.junit.Rule
-import org.junit.Test
-
-class LazyForIndexedTest {
-
-    @get:Rule
-    val rule = createComposeRule()
-
-    @Test
-    fun columnWithIndexesComposedWithCorrectIndexAndItem() {
-        val items = (0..1).map { it.toString() }
-
-        rule.setContent {
-            LazyColumnForIndexed(items, Modifier.preferredHeight(200.dp)) { index, item ->
-                BasicText("${index}x$item", Modifier.fillParentMaxWidth().height(100.dp))
-            }
-        }
-
-        rule.onNodeWithText("0x0")
-            .assertTopPositionInRootIsEqualTo(0.dp)
-
-        rule.onNodeWithText("1x1")
-            .assertTopPositionInRootIsEqualTo(100.dp)
-    }
-
-    @Test
-    fun rowWithIndexesComposedWithCorrectIndexAndItem() {
-        val items = (0..1).map { it.toString() }
-
-        rule.setContent {
-            LazyRowForIndexed(items, Modifier.preferredWidth(200.dp)) { index, item ->
-                BasicText("${index}x$item", Modifier.fillParentMaxHeight().width(100.dp))
-            }
-        }
-
-        rule.onNodeWithText("0x0")
-            .assertLeftPositionInRootIsEqualTo(0.dp)
-
-        rule.onNodeWithText("1x1")
-            .assertLeftPositionInRootIsEqualTo(100.dp)
-    }
-}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyGridTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyGridTest.kt
index c3b1ac8..af17e7f 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyGridTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyGridTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.lazy
 
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredSize
@@ -23,6 +24,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.dp
@@ -32,6 +34,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalFoundationApi::class)
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 class LazyGridTest {
@@ -45,8 +48,8 @@
         val itemTestTag = "itemTestTag"
 
         rule.setContent {
-            LazyGrid(
-                columns = 3
+            LazyVerticalGrid(
+                cells = GridCells.Fixed(3)
             ) {
                 item {
                     Spacer(
@@ -65,8 +68,8 @@
         val items = (1..5).map { it.toString() }
 
         rule.setContent {
-            LazyGrid(
-                columns = 3,
+            LazyVerticalGrid(
+                cells = GridCells.Fixed(3),
                 modifier = Modifier.preferredHeight(100.dp).preferredWidth(300.dp)
             ) {
                 items(items) {
@@ -96,8 +99,8 @@
         val items = (1..9).map { it.toString() }
 
         rule.setContent {
-            LazyGrid(
-                columns = 3,
+            LazyVerticalGrid(
+                cells = GridCells.Fixed(3),
                 modifier = Modifier.preferredHeight(100.dp).testTag(LazyGridTag)
             ) {
                 items(items) {
@@ -133,8 +136,8 @@
         val items = (1..9).map { it.toString() }
 
         rule.setContent {
-            LazyGrid(
-                columns = 3,
+            LazyVerticalGrid(
+                cells = GridCells.Fixed(3),
                 modifier = Modifier.preferredHeight(200.dp).testTag(LazyGridTag)
             ) {
                 items(items) {
@@ -173,4 +176,60 @@
         rule.onNodeWithTag("9")
             .assertIsDisplayed()
     }
+
+    @Test
+    fun adaptiveLazyGridFillsAllWidth() {
+        val items = (1..5).map { it.toString() }
+
+        rule.setContent {
+            LazyVerticalGrid(
+                cells = GridCells.Adaptive(130.dp),
+                modifier = Modifier.preferredHeight(100.dp).preferredWidth(300.dp)
+            ) {
+                items(items) {
+                    Spacer(Modifier.preferredHeight(101.dp).testTag(it))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("2")
+            .assertLeftPositionInRootIsEqualTo(150.dp)
+
+        rule.onNodeWithTag("3")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("4")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("5")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun adaptiveLazyGridAtLeastOneColumn() {
+        val items = (1..3).map { it.toString() }
+
+        rule.setContent {
+            LazyVerticalGrid(
+                cells = GridCells.Adaptive(301.dp),
+                modifier = Modifier.preferredHeight(100.dp).preferredWidth(300.dp)
+            ) {
+                items(items) {
+                    Spacer(Modifier.preferredHeight(101.dp).testTag(it))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("2")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("3")
+            .assertDoesNotExist()
+    }
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyItemStateRestoration.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyItemStateRestoration.kt
new file mode 100644
index 0000000..5dc3b6f
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyItemStateRestoration.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.onDispose
+import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.StateRestorationTester
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.dp
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Rule
+import org.junit.Test
+
+class LazyItemStateRestoration {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun visibleItemsStateRestored() {
+        val restorationTester = StateRestorationTester(rule)
+        var counter0 = 1
+        var counter1 = 10
+        var counter2 = 100
+        var realState = arrayOf(0, 0, 0)
+        restorationTester.setContent {
+            LazyColumn {
+                item {
+                    realState[0] = rememberSavedInstanceState { counter0++ }
+                    Box(Modifier.size(1.dp))
+                }
+                items((1..2).toList()) {
+                    if (it == 1) {
+                        realState[1] = rememberSavedInstanceState { counter1++ }
+                    } else {
+                        realState[2] = rememberSavedInstanceState { counter2++ }
+                    }
+                    Box(Modifier.size(1.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(realState[0]).isEqualTo(1)
+            assertThat(realState[1]).isEqualTo(10)
+            assertThat(realState[2]).isEqualTo(100)
+            realState = arrayOf(0, 0, 0)
+        }
+
+        restorationTester.emulateSavedInstanceStateRestore()
+
+        rule.runOnIdle {
+            assertThat(realState[0]).isEqualTo(1)
+            assertThat(realState[1]).isEqualTo(10)
+            assertThat(realState[2]).isEqualTo(100)
+        }
+    }
+
+    @Test
+    fun itemsStateRestoredWhenWeScrolledBackToIt() {
+        val restorationTester = StateRestorationTester(rule)
+        var counter0 = 1
+        lateinit var state: LazyListState
+        var itemDisposed = false
+        var realState = 0
+        restorationTester.setContent {
+            LazyColumn(
+                Modifier.size(20.dp),
+                state = rememberLazyListState().also { state = it }
+            ) {
+                items((0..1).toList()) {
+                    if (it == 0) {
+                        realState = rememberSavedInstanceState { counter0++ }
+                        onDispose {
+                            itemDisposed = true
+                        }
+                    }
+                    Box(Modifier.size(30.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(realState).isEqualTo(1)
+            runBlocking {
+                state.snapToItemIndex(1, 5)
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(itemDisposed).isEqualTo(true)
+            realState = 0
+            runBlocking {
+                state.snapToItemIndex(0, 0)
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(realState).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun itemsStateRestoredWhenWeScrolledRestoredAndScrolledBackTo() {
+        val restorationTester = StateRestorationTester(rule)
+        var counter0 = 1
+        var counter1 = 10
+        lateinit var state: LazyListState
+        var realState = arrayOf(0, 0)
+        restorationTester.setContent {
+            LazyColumn(
+                Modifier.size(20.dp),
+                state = rememberLazyListState().also { state = it }
+            ) {
+                items((0..1).toList()) {
+                    if (it == 0) {
+                        realState[0] = rememberSavedInstanceState { counter0++ }
+                    } else {
+                        realState[1] = rememberSavedInstanceState { counter1++ }
+                    }
+                    Box(Modifier.size(30.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(realState[0]).isEqualTo(1)
+            runBlocking {
+                state.snapToItemIndex(1, 5)
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(realState[1]).isEqualTo(10)
+            realState = arrayOf(0, 0)
+        }
+
+        restorationTester.emulateSavedInstanceStateRestore()
+
+        rule.runOnIdle {
+            assertThat(realState[1]).isEqualTo(10)
+            runBlocking {
+                state.snapToItemIndex(0, 0)
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(realState[0]).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun nestedLazy_itemsStateRestoredWhenWeScrolledBackToIt() {
+        val restorationTester = StateRestorationTester(rule)
+        var counter0 = 1
+        lateinit var state: LazyListState
+        var itemDisposed = false
+        var realState = 0
+        restorationTester.setContent {
+            LazyColumn(
+                Modifier.size(20.dp),
+                state = rememberLazyListState().also { state = it }
+            ) {
+                items((0..1).toList()) {
+                    if (it == 0) {
+                        LazyRow {
+                            item {
+                                realState = rememberSavedInstanceState { counter0++ }
+                                onDispose {
+                                    itemDisposed = true
+                                }
+                                Box(Modifier.size(30.dp))
+                            }
+                        }
+                    } else {
+                        Box(Modifier.size(30.dp))
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(realState).isEqualTo(1)
+            runBlocking {
+                state.snapToItemIndex(1, 5)
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(itemDisposed).isEqualTo(true)
+            realState = 0
+            runBlocking {
+                state.snapToItemIndex(0, 0)
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(realState).isEqualTo(1)
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListHeadersTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListHeadersTest.kt
new file mode 100644
index 0000000..171c2911
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListHeadersTest.kt
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.foundation.layout.preferredWidth
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import kotlinx.coroutines.runBlocking
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalFoundationApi::class)
+class LazyListHeadersTest {
+
+    private val LazyListTag = "LazyList"
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun lazyColumnShowsHeader() {
+        val items = (1..2).map { it.toString() }
+        val firstHeaderTag = "firstHeaderTag"
+        val secondHeaderTag = "secondHeaderTag"
+
+        rule.setContent {
+            LazyColumn(Modifier.preferredHeight(300.dp)) {
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredHeight(101.dp).fillParentMaxWidth()
+                            .testTag(firstHeaderTag)
+                    )
+                }
+
+                items(items) {
+                    Spacer(Modifier.preferredHeight(101.dp).fillParentMaxWidth().testTag(it))
+                }
+
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredHeight(101.dp).fillParentMaxWidth()
+                            .testTag(secondHeaderTag)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstHeaderTag)
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("1")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag(secondHeaderTag)
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun lazyColumnShowsHeadersOnScroll() {
+        val items = (1..2).map { it.toString() }
+        val firstHeaderTag = "firstHeaderTag"
+        val secondHeaderTag = "secondHeaderTag"
+
+        rule.setContent {
+            LazyColumn(Modifier.preferredHeight(300.dp).testTag(LazyListTag)) {
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredHeight(101.dp).fillParentMaxWidth()
+                            .testTag(firstHeaderTag)
+                    )
+                }
+
+                items(items) {
+                    Spacer(Modifier.preferredHeight(101.dp).fillParentMaxWidth().testTag(it))
+                }
+
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredHeight(101.dp).fillParentMaxWidth()
+                            .testTag(secondHeaderTag)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 102.dp, density = rule.density)
+
+        rule.onNodeWithTag(firstHeaderTag)
+            .assertIsDisplayed()
+            .assertTopPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag(secondHeaderTag)
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun lazyColumnHeaderIsReplaced() {
+        val items = (1..2).map { it.toString() }
+        val firstHeaderTag = "firstHeaderTag"
+        val secondHeaderTag = "secondHeaderTag"
+
+        rule.setContent {
+            LazyColumn(Modifier.preferredHeight(300.dp).testTag(LazyListTag)) {
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredHeight(101.dp).fillParentMaxWidth()
+                            .testTag(firstHeaderTag)
+                    )
+                }
+
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredHeight(101.dp).fillParentMaxWidth()
+                            .testTag(secondHeaderTag)
+                    )
+                }
+
+                items(items) {
+                    Spacer(Modifier.preferredHeight(101.dp).fillParentMaxWidth().testTag(it))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(y = 102.dp, density = rule.density)
+
+        rule.onNodeWithTag(firstHeaderTag)
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag(secondHeaderTag)
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("1")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun lazyRowShowsHeader() {
+        val items = (1..2).map { it.toString() }
+        val firstHeaderTag = "firstHeaderTag"
+        val secondHeaderTag = "secondHeaderTag"
+
+        rule.setContent {
+            LazyRow(Modifier.preferredWidth(300.dp)) {
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredWidth(101.dp).fillParentMaxHeight()
+                            .testTag(firstHeaderTag)
+                    )
+                }
+
+                items(items) {
+                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
+                }
+
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredWidth(101.dp).fillParentMaxHeight()
+                            .testTag(secondHeaderTag)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstHeaderTag)
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("1")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag(secondHeaderTag)
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun lazyRowShowsHeadersOnScroll() {
+        val items = (1..2).map { it.toString() }
+        val firstHeaderTag = "firstHeaderTag"
+        val secondHeaderTag = "secondHeaderTag"
+
+        rule.setContent {
+            LazyRow(Modifier.preferredWidth(300.dp).testTag(LazyListTag)) {
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredWidth(101.dp).fillParentMaxHeight()
+                            .testTag(firstHeaderTag)
+                    )
+                }
+
+                items(items) {
+                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
+                }
+
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredWidth(101.dp).fillParentMaxHeight()
+                            .testTag(secondHeaderTag)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 102.dp, density = rule.density)
+
+        rule.onNodeWithTag(firstHeaderTag)
+            .assertIsDisplayed()
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag(secondHeaderTag)
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun lazyRowHeaderIsReplaced() {
+        val items = (1..2).map { it.toString() }
+        val firstHeaderTag = "firstHeaderTag"
+        val secondHeaderTag = "secondHeaderTag"
+
+        rule.setContent {
+            LazyRow(Modifier.preferredWidth(300.dp).testTag(LazyListTag)) {
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredWidth(101.dp).fillParentMaxHeight()
+                            .testTag(firstHeaderTag)
+                    )
+                }
+
+                stickyHeader {
+                    Spacer(
+                        Modifier.preferredWidth(101.dp).fillParentMaxHeight()
+                            .testTag(secondHeaderTag)
+                    )
+                }
+
+                items(items) {
+                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 102.dp, density = rule.density)
+
+        rule.onNodeWithTag(firstHeaderTag)
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag(secondHeaderTag)
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("1")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun headerIsDisplayedWhenItIsFullyInContentPadding() {
+        val headerTag = "header"
+        val itemIndexPx = 100
+        val itemIndexDp = with(rule.density) { itemIndexPx.toDp() }
+        lateinit var state: LazyListState
+
+        rule.setContent {
+            LazyColumn(
+                Modifier.size(itemIndexDp * 4),
+                state = rememberLazyListState().also { state = it },
+                contentPadding = PaddingValues(top = itemIndexDp * 2)
+            ) {
+                stickyHeader {
+                    Spacer(Modifier.size(itemIndexDp).testTag(headerTag))
+                }
+
+                items((0..4).toList()) {
+                    Spacer(Modifier.size(itemIndexDp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            runBlocking { state.snapToItemIndex(1, itemIndexPx / 2) }
+        }
+
+        rule.onNodeWithTag(headerTag)
+            .assertTopPositionInRootIsEqualTo(itemIndexDp / 2)
+
+        rule.onNodeWithTag("0")
+            .assertTopPositionInRootIsEqualTo(itemIndexDp * 3 / 2)
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfoTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfoTest.kt
new file mode 100644
index 0000000..c5d040e
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfoTest.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class LazyListLayoutInfoTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private var itemSizePx: Int = 50
+    private var itemSizeDp: Dp = Dp.Infinity
+
+    @Before
+    fun before() {
+        with(rule.density) {
+            itemSizeDp = itemSizePx.toDp()
+        }
+    }
+
+    @Test
+    fun visibleItemsAreCorrect() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSizeDp * 3.5f)
+            ) {
+                items((0..5).toList()) {
+                    Box(Modifier.size(itemSizeDp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            state.layoutInfo.assertVisibleItems(count = 4)
+        }
+    }
+
+    @Test
+    fun visibleItemsAreCorrectAfterScroll() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSizeDp * 3.5f)
+            ) {
+                items((0..5).toList()) {
+                    Box(Modifier.size(itemSizeDp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            runBlocking {
+                state.snapToItemIndex(1, 10)
+            }
+            state.layoutInfo.assertVisibleItems(count = 4, startIndex = 1, startOffset = -10)
+        }
+    }
+
+    @Test
+    fun visibleItemsAreCorrectWithSpacing() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                state = rememberLazyListState().also { state = it },
+                verticalArrangement = Arrangement.spacedBy(itemSizeDp),
+                modifier = Modifier.size(itemSizeDp * 3.5f)
+            ) {
+                items((0..5).toList()) {
+                    Box(Modifier.size(itemSizeDp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            state.layoutInfo.assertVisibleItems(count = 2, spacing = itemSizePx)
+        }
+    }
+
+    @Test
+    fun visibleItemsAreObservableWhenWeScroll() {
+        lateinit var state: LazyListState
+        var currentInfo: LazyListLayoutInfo? = null
+        @Composable
+        fun observingFun() {
+            currentInfo = state.layoutInfo
+        }
+        rule.setContent {
+            LazyColumn(
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSizeDp * 3.5f)
+            ) {
+                items((0..5).toList()) {
+                    Box(Modifier.size(itemSizeDp))
+                }
+            }
+            observingFun()
+        }
+
+        rule.runOnIdle {
+            // empty it here and scrolling should invoke observingFun again
+            currentInfo = null
+            runBlocking {
+                state.snapToItemIndex(1, 0)
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(currentInfo).isNotNull()
+            currentInfo!!.assertVisibleItems(count = 4, startIndex = 1)
+        }
+    }
+
+    @Test
+    fun visibleItemsAreObservableWhenResize() {
+        lateinit var state: LazyListState
+        var size by mutableStateOf(itemSizeDp * 2)
+        var currentInfo: LazyListLayoutInfo? = null
+        @Composable
+        fun observingFun() {
+            currentInfo = state.layoutInfo
+        }
+        rule.setContent {
+            LazyColumn(
+                state = rememberLazyListState().also { state = it }
+            ) {
+                item {
+                    Box(Modifier.size(size))
+                }
+            }
+            observingFun()
+        }
+
+        rule.runOnIdle {
+            assertThat(currentInfo).isNotNull()
+            currentInfo!!.assertVisibleItems(count = 1, expectedSize = itemSizePx * 2)
+            currentInfo = null
+            size = itemSizeDp
+        }
+
+        rule.runOnIdle {
+            assertThat(currentInfo).isNotNull()
+            currentInfo!!.assertVisibleItems(count = 1, expectedSize = itemSizePx)
+        }
+    }
+
+    @Test
+    fun totalCountIsCorrect() {
+        var count by mutableStateOf(10)
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                state = rememberLazyListState().also { state = it }
+            ) {
+                items((0 until count).toList()) {
+                    Box(Modifier.size(10.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.totalItemsCount).isEqualTo(10)
+            count = 20
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.totalItemsCount).isEqualTo(20)
+        }
+    }
+
+    @Test
+    fun viewportOffsetsAreCorrect() {
+        val sizePx = 45
+        val sizeDp = with(rule.density) { sizePx.toDp() }
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                Modifier.size(sizeDp),
+                state = rememberLazyListState().also { state = it }
+            ) {
+                items((0..3).toList()) {
+                    Box(Modifier.size(sizeDp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(0)
+            assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx)
+        }
+    }
+
+    @Test
+    fun viewportOffsetsAreCorrectWithContentPadding() {
+        val sizePx = 45
+        val topPaddingPx = 10
+        val bottomPaddingPx = 15
+        val sizeDp = with(rule.density) { sizePx.toDp() }
+        val topPaddingDp = with(rule.density) { topPaddingPx.toDp() }
+        val bottomPaddingDp = with(rule.density) { bottomPaddingPx.toDp() }
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                Modifier.size(sizeDp),
+                contentPadding = PaddingValues(top = topPaddingDp, bottom = bottomPaddingDp),
+                state = rememberLazyListState().also { state = it }
+            ) {
+                items((0..3).toList()) {
+                    Box(Modifier.size(sizeDp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(-topPaddingPx)
+            assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx - topPaddingPx)
+        }
+    }
+
+    fun LazyListLayoutInfo.assertVisibleItems(
+        count: Int,
+        startIndex: Int = 0,
+        startOffset: Int = 0,
+        expectedSize: Int = itemSizePx,
+        spacing: Int = 0
+    ) {
+        assertThat(visibleItemsInfo.size).isEqualTo(count)
+        var currentIndex = startIndex
+        var currentOffset = startOffset
+        visibleItemsInfo.forEach {
+            assertThat(it.index).isEqualTo(currentIndex)
+            assertThat(it.offset).isEqualTo(currentOffset)
+            assertThat(it.size).isEqualTo(expectedSize)
+            currentIndex++
+            currentOffset += it.size + spacing
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt
index c77dee6..a7d5427 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt
@@ -17,6 +17,7 @@
 package androidx.compose.foundation.lazy
 
 import androidx.compose.animation.core.snap
+import androidx.compose.foundation.animation.smoothScrollBy
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Spacer
@@ -68,11 +69,10 @@
         val smallPaddingSize = itemSize / 4
         val largePaddingSize = itemSize
         rule.setContent {
-            LazyColumnFor(
-                items = listOf(1),
-                state = rememberLazyListState().also { state = it },
+            LazyColumn(
                 modifier = Modifier.size(containerSize)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     start = smallPaddingSize,
                     top = largePaddingSize,
@@ -80,7 +80,9 @@
                     bottom = largePaddingSize
                 )
             ) {
-                Spacer(Modifier.fillParentMaxWidth().preferredHeight(itemSize).testTag(ItemTag))
+                items(listOf(1)) {
+                    Spacer(Modifier.fillParentMaxWidth().preferredHeight(itemSize).testTag(ItemTag))
+                }
             }
         }
 
@@ -101,17 +103,18 @@
     fun column_contentPaddingIsNotAffectingScrollPosition() {
         lateinit var state: LazyListState
         rule.setContent {
-            LazyColumnFor(
-                items = listOf(1),
-                state = rememberLazyListState().also { state = it },
+            LazyColumn(
                 modifier = Modifier.size(itemSize * 2)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     top = itemSize,
                     bottom = itemSize
                 )
             ) {
-                Spacer(Modifier.fillParentMaxWidth().preferredHeight(itemSize).testTag(ItemTag))
+                items(listOf(1)) {
+                    Spacer(Modifier.fillParentMaxWidth().preferredHeight(itemSize).testTag(ItemTag))
+                }
             }
         }
 
@@ -127,17 +130,18 @@
         lateinit var state: LazyListState
         val padding = itemSize * 1.5f
         rule.setContent {
-            LazyColumnFor(
-                items = (0..3).toList(),
-                state = rememberLazyListState().also { state = it },
+            LazyColumn(
                 modifier = Modifier.size(padding * 2 + itemSize)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     top = padding,
                     bottom = padding
                 )
             ) {
-                Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                items((0..3).toList()) {
+                    Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                }
             }
         }
 
@@ -167,17 +171,18 @@
         lateinit var state: LazyListState
         val padding = itemSize * 1.5f
         rule.setContent {
-            LazyColumnFor(
-                items = (0..3).toList(),
-                state = rememberLazyListState().also { state = it },
+            LazyColumn(
                 modifier = Modifier.size(itemSize + padding * 2)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     top = padding,
                     bottom = padding
                 )
             ) {
-                Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                items((0..3).toList()) {
+                    Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                }
             }
         }
 
@@ -201,17 +206,18 @@
         lateinit var state: LazyListState
         val padding = itemSize * 1.5f
         rule.setContent {
-            LazyColumnFor(
-                items = (0..3).toList(),
-                state = rememberLazyListState().also { state = it },
+            LazyColumn(
                 modifier = Modifier.size(padding * 2 + itemSize)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     top = padding,
                     bottom = padding
                 )
             ) {
-                Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                items((0..3).toList()) {
+                    Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                }
             }
         }
 
@@ -244,17 +250,18 @@
         lateinit var state: LazyListState
         val padding = itemSize * 1.5f
         rule.setContent {
-            LazyColumnFor(
-                items = (0..3).toList(),
-                state = rememberLazyListState().also { state = it },
+            LazyColumn(
                 modifier = Modifier.size(padding * 2 + itemSize)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     top = padding,
                     bottom = padding
                 )
             ) {
-                Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                items((0..3).toList()) {
+                    Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                }
             }
         }
 
@@ -275,8 +282,7 @@
     fun column_contentPaddingAndWrapContent() {
         rule.setContent {
             Box(modifier = Modifier.testTag(ContainerTag)) {
-                LazyColumnFor(
-                    items = listOf(1),
+                LazyColumn(
                     contentPadding = PaddingValues(
                         start = 2.dp,
                         top = 4.dp,
@@ -284,7 +290,9 @@
                         bottom = 8.dp
                     )
                 ) {
-                    Spacer(Modifier.size(itemSize).testTag(ItemTag))
+                    items(listOf(1)) {
+                        Spacer(Modifier.size(itemSize).testTag(ItemTag))
+                    }
                 }
             }
         }
@@ -306,8 +314,7 @@
     fun column_contentPaddingAndNoContent() {
         rule.setContent {
             Box(modifier = Modifier.testTag(ContainerTag)) {
-                LazyColumnFor(
-                    items = listOf(0),
+                LazyColumn(
                     contentPadding = PaddingValues(
                         start = 2.dp,
                         top = 4.dp,
@@ -315,6 +322,8 @@
                         bottom = 8.dp
                     )
                 ) {
+                    items(listOf(0)) {
+                    }
                 }
             }
         }
@@ -333,11 +342,10 @@
         val smallPaddingSize = itemSize / 4
         val largePaddingSize = itemSize
         rule.setContent {
-            LazyRowFor(
-                items = listOf(1),
-                state = rememberLazyListState().also { state = it },
+            LazyRow(
                 modifier = Modifier.size(containerSize)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     top = smallPaddingSize,
                     start = largePaddingSize,
@@ -345,7 +353,9 @@
                     end = largePaddingSize
                 )
             ) {
-                Spacer(Modifier.fillParentMaxHeight().preferredWidth(itemSize).testTag(ItemTag))
+                items(listOf(1)) {
+                    Spacer(Modifier.fillParentMaxHeight().preferredWidth(itemSize).testTag(ItemTag))
+                }
             }
         }
 
@@ -369,17 +379,18 @@
             50.dp.toIntPx().toDp()
         }
         rule.setContent {
-            LazyRowFor(
-                items = listOf(1),
-                state = rememberLazyListState().also { state = it },
+            LazyRow(
                 modifier = Modifier.size(itemSize * 2)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     start = itemSize,
                     end = itemSize
                 )
             ) {
-                Spacer(Modifier.fillParentMaxHeight().preferredWidth(itemSize).testTag(ItemTag))
+                items(listOf(1)) {
+                    Spacer(Modifier.fillParentMaxHeight().preferredWidth(itemSize).testTag(ItemTag))
+                }
             }
         }
 
@@ -395,17 +406,18 @@
         lateinit var state: LazyListState
         val padding = itemSize * 1.5f
         rule.setContent {
-            LazyRowFor(
-                items = (0..3).toList(),
-                state = rememberLazyListState().also { state = it },
+            LazyRow(
                 modifier = Modifier.size(padding * 2 + itemSize)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     start = padding,
                     end = padding
                 )
             ) {
-                Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                items((0..3).toList()) {
+                    Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                }
             }
         }
 
@@ -435,17 +447,18 @@
         lateinit var state: LazyListState
         val padding = itemSize * 1.5f
         rule.setContent {
-            LazyRowFor(
-                items = (0..3).toList(),
-                state = rememberLazyListState().also { state = it },
+            LazyRow(
                 modifier = Modifier.size(itemSize + padding * 2)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     start = padding,
                     end = padding
                 )
             ) {
-                Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                items((0..3).toList()) {
+                    Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                }
             }
         }
 
@@ -469,17 +482,18 @@
         lateinit var state: LazyListState
         val padding = itemSize * 1.5f
         rule.setContent {
-            LazyRowFor(
-                items = (0..3).toList(),
-                state = rememberLazyListState().also { state = it },
+            LazyRow(
                 modifier = Modifier.size(padding * 2 + itemSize)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     start = padding,
                     end = padding
                 )
             ) {
-                Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                items((0..3).toList()) {
+                    Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                }
             }
         }
 
@@ -512,17 +526,18 @@
         lateinit var state: LazyListState
         val padding = itemSize * 1.5f
         rule.setContent {
-            LazyRowFor(
-                items = (0..3).toList(),
-                state = rememberLazyListState().also { state = it },
+            LazyRow(
                 modifier = Modifier.size(padding * 2 + itemSize)
                     .testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it },
                 contentPadding = PaddingValues(
                     start = padding,
                     end = padding
                 )
             ) {
-                Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                items((0..3).toList()) {
+                    Spacer(Modifier.size(itemSize).testTag(it.toString()))
+                }
             }
         }
 
@@ -543,8 +558,7 @@
     fun row_contentPaddingAndWrapContent() {
         rule.setContent {
             Box(modifier = Modifier.testTag(ContainerTag)) {
-                LazyRowFor(
-                    items = listOf(1),
+                LazyRow(
                     contentPadding = PaddingValues(
                         start = 2.dp,
                         top = 4.dp,
@@ -552,7 +566,9 @@
                         bottom = 8.dp
                     )
                 ) {
-                    Spacer(Modifier.size(itemSize).testTag(ItemTag))
+                    items(listOf(1)) {
+                        Spacer(Modifier.size(itemSize).testTag(ItemTag))
+                    }
                 }
             }
         }
@@ -574,8 +590,7 @@
     fun row_contentPaddingAndNoContent() {
         rule.setContent {
             Box(modifier = Modifier.testTag(ContainerTag)) {
-                LazyRowFor(
-                    items = listOf(0),
+                LazyRow(
                     contentPadding = PaddingValues(
                         start = 2.dp,
                         top = 4.dp,
@@ -583,6 +598,7 @@
                         bottom = 8.dp
                     )
                 ) {
+                    items(listOf(0)) {}
                 }
             }
         }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsIndexedTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsIndexedTest.kt
new file mode 100644
index 0000000..16bb089
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsIndexedTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.foundation.layout.preferredWidth
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.unit.dp
+import org.junit.Rule
+import org.junit.Test
+
+class LazyListsIndexedTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun lazyColumnShowsIndexedItems() {
+        val items = (1..4).map { it.toString() }
+
+        rule.setContent {
+            LazyColumn(Modifier.preferredHeight(200.dp)) {
+                itemsIndexed(items) { index, item ->
+                    Spacer(
+                        Modifier.preferredHeight(101.dp).fillParentMaxWidth()
+                            .testTag("$index-$item")
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0-1")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("1-2")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("2-3")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("3-4")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun columnWithIndexesComposedWithCorrectIndexAndItem() {
+        val items = (0..1).map { it.toString() }
+
+        rule.setContent {
+            LazyColumn(Modifier.preferredHeight(200.dp)) {
+                itemsIndexed(items) { index, item ->
+                    BasicText("${index}x$item", Modifier.fillParentMaxWidth().height(100.dp))
+                }
+            }
+        }
+
+        rule.onNodeWithText("0x0")
+            .assertTopPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithText("1x1")
+            .assertTopPositionInRootIsEqualTo(100.dp)
+    }
+
+    @Test
+    fun lazyRowShowsIndexedItems() {
+        val items = (1..4).map { it.toString() }
+
+        rule.setContent {
+            LazyRow(Modifier.preferredWidth(200.dp)) {
+                itemsIndexed(items) { index, item ->
+                    Spacer(
+                        Modifier.preferredWidth(101.dp).fillParentMaxHeight()
+                            .testTag("$index-$item")
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0-1")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("1-2")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("2-3")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("3-4")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun rowWithIndexesComposedWithCorrectIndexAndItem() {
+        val items = (0..1).map { it.toString() }
+
+        rule.setContent {
+            LazyRow(Modifier.preferredWidth(200.dp)) {
+                itemsIndexed(items) { index, item ->
+                    BasicText("${index}x$item", Modifier.fillParentMaxHeight().width(100.dp))
+                }
+            }
+        }
+
+        rule.onNodeWithText("0x0")
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithText("1x1")
+            .assertLeftPositionInRootIsEqualTo(100.dp)
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsReverseLayoutTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsReverseLayoutTest.kt
new file mode 100644
index 0000000..9f02efa
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsReverseLayoutTest.kt
@@ -0,0 +1,444 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.InternalLayoutApi
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Providers
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.AmbientLayoutDirection
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+@OptIn(InternalLayoutApi::class)
+class LazyListsReverseLayoutTest {
+
+    private val ContainerTag = "ContainerTag"
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private var itemSize: Dp = Dp.Infinity
+
+    @Before
+    fun before() {
+        with(rule.density) {
+            itemSize = 50.toDp()
+        }
+    }
+
+    @Test
+    fun column_emitTwoElementsAsOneItem_positionedReversed() {
+        rule.setContent {
+            LazyColumn(
+                reverseLayout = true
+            ) {
+                item {
+                    Box(Modifier.size(itemSize).testTag("0"))
+                    Box(Modifier.size(itemSize).testTag("1"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertTopPositionInRootIsEqualTo(0.dp)
+        rule.onNodeWithTag("0")
+            .assertTopPositionInRootIsEqualTo(itemSize)
+    }
+
+    @Test
+    fun column_emitTwoItems_positionedReversed() {
+        rule.setContent {
+            LazyColumn(
+                reverseLayout = true
+            ) {
+                item {
+                    Box(Modifier.size(itemSize).testTag("0"))
+                }
+                item {
+                    Box(Modifier.size(itemSize).testTag("1"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertTopPositionInRootIsEqualTo(0.dp)
+        rule.onNodeWithTag("0")
+            .assertTopPositionInRootIsEqualTo(itemSize)
+    }
+
+    @Test
+    fun column_initialScrollPositionIs0() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                reverseLayout = true,
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
+            ) {
+                items((0..2).toList()) {
+                    Box(Modifier.size(itemSize).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun column_scrollInWrongDirectionDoesNothing() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                reverseLayout = true,
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
+            ) {
+                items((0..2).toList()) {
+                    Box(Modifier.size(itemSize).testTag("$it"))
+                }
+            }
+        }
+
+        // we scroll down and as the scrolling is reversed it shouldn't affect anything
+        rule.onNodeWithTag(ContainerTag)
+            .scrollBy(y = itemSize, density = rule.density)
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+        }
+
+        rule.onNodeWithTag("1")
+            .assertTopPositionInRootIsEqualTo(0.dp)
+        rule.onNodeWithTag("0")
+            .assertTopPositionInRootIsEqualTo(itemSize)
+    }
+
+    @Test
+    fun column_scrollForwardHalfWay() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                reverseLayout = true,
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
+            ) {
+                items((0..2).toList()) {
+                    Box(Modifier.size(itemSize).testTag("$it"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(ContainerTag)
+            .scrollBy(y = -itemSize * 0.5f, density = rule.density)
+
+        val scrolled = rule.runOnIdle {
+            assertThat(state.firstVisibleItemScrollOffset).isGreaterThan(0)
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+            with(rule.density) { state.firstVisibleItemScrollOffset.toDp() }
+        }
+
+        rule.onNodeWithTag("2")
+            .assertTopPositionInRootIsEqualTo(-itemSize + scrolled)
+        rule.onNodeWithTag("1")
+            .assertTopPositionInRootIsEqualTo(scrolled)
+        rule.onNodeWithTag("0")
+            .assertTopPositionInRootIsEqualTo(itemSize + scrolled)
+    }
+
+    @Test
+    fun column_scrollForwardTillTheEnd() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyColumn(
+                reverseLayout = true,
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
+            ) {
+                items((0..3).toList()) {
+                    Box(Modifier.size(itemSize).testTag("$it"))
+                }
+            }
+        }
+
+        // we scroll a bit more than it is possible just to make sure we would stop correctly
+        rule.onNodeWithTag(ContainerTag)
+            .scrollBy(y = -itemSize * 2.2f, density = rule.density)
+
+        rule.runOnIdle {
+            with(rule.density) {
+                val realOffset = state.firstVisibleItemScrollOffset.toDp() +
+                    itemSize * state.firstVisibleItemIndex
+                assertThat(realOffset).isEqualTo(itemSize * 2)
+            }
+        }
+
+        rule.onNodeWithTag("3")
+            .assertTopPositionInRootIsEqualTo(0.dp)
+        rule.onNodeWithTag("2")
+            .assertTopPositionInRootIsEqualTo(itemSize)
+    }
+
+    @Test
+    fun row_emitTwoElementsAsOneItem_positionedReversed() {
+        rule.setContent {
+            LazyRow(
+                reverseLayout = true
+            ) {
+                item {
+                    Box(Modifier.size(itemSize).testTag("0"))
+                    Box(Modifier.size(itemSize).testTag("1"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+        rule.onNodeWithTag("0")
+            .assertLeftPositionInRootIsEqualTo(itemSize)
+    }
+
+    @Test
+    fun row_emitTwoItems_positionedReversed() {
+        rule.setContent {
+            LazyRow(
+                reverseLayout = true
+            ) {
+                item {
+                    Box(Modifier.size(itemSize).testTag("0"))
+                }
+                item {
+                    Box(Modifier.size(itemSize).testTag("1"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+        rule.onNodeWithTag("0")
+            .assertLeftPositionInRootIsEqualTo(itemSize)
+    }
+
+    @Test
+    fun row_initialScrollPositionIs0() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyRow(
+                reverseLayout = true,
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
+            ) {
+                items((0..2).toList()) {
+                    Box(Modifier.size(itemSize).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun row_scrollInWrongDirectionDoesNothing() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyRow(
+                reverseLayout = true,
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
+            ) {
+                items((0..2).toList()) {
+                    Box(Modifier.size(itemSize).testTag("$it"))
+                }
+            }
+        }
+
+        // we scroll down and as the scrolling is reversed it shouldn't affect anything
+        rule.onNodeWithTag(ContainerTag)
+            .scrollBy(x = itemSize, density = rule.density)
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+        }
+
+        rule.onNodeWithTag("1")
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+        rule.onNodeWithTag("0")
+            .assertLeftPositionInRootIsEqualTo(itemSize)
+    }
+
+    @Test
+    fun row_scrollForwardHalfWay() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyRow(
+                reverseLayout = true,
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
+            ) {
+                items((0..2).toList()) {
+                    Box(Modifier.size(itemSize).testTag("$it"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(ContainerTag)
+            .scrollBy(x = -itemSize * 0.5f, density = rule.density)
+
+        val scrolled = rule.runOnIdle {
+            assertThat(state.firstVisibleItemScrollOffset).isGreaterThan(0)
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+            with(rule.density) { state.firstVisibleItemScrollOffset.toDp() }
+        }
+
+        rule.onNodeWithTag("2")
+            .assertLeftPositionInRootIsEqualTo(-itemSize + scrolled)
+        rule.onNodeWithTag("1")
+            .assertLeftPositionInRootIsEqualTo(scrolled)
+        rule.onNodeWithTag("0")
+            .assertLeftPositionInRootIsEqualTo(itemSize + scrolled)
+    }
+
+    @Test
+    fun row_scrollForwardTillTheEnd() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyRow(
+                reverseLayout = true,
+                state = rememberLazyListState().also { state = it },
+                modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
+            ) {
+                items((0..3).toList()) {
+                    Box(Modifier.size(itemSize).testTag("$it"))
+                }
+            }
+        }
+
+        // we scroll a bit more than it is possible just to make sure we would stop correctly
+        rule.onNodeWithTag(ContainerTag)
+            .scrollBy(x = -itemSize * 2.2f, density = rule.density)
+
+        rule.runOnIdle {
+            with(rule.density) {
+                val realOffset = state.firstVisibleItemScrollOffset.toDp() +
+                    itemSize * state.firstVisibleItemIndex
+                assertThat(realOffset).isEqualTo(itemSize * 2)
+            }
+        }
+
+        rule.onNodeWithTag("3")
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+        rule.onNodeWithTag("2")
+            .assertLeftPositionInRootIsEqualTo(itemSize)
+    }
+
+    @Test
+    fun row_rtl_emitTwoElementsAsOneItem_positionedReversed() {
+        rule.setContent {
+            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
+                LazyRow(
+                    reverseLayout = true
+                ) {
+                    item {
+                        Box(Modifier.size(itemSize).testTag("0"))
+                        Box(Modifier.size(itemSize).testTag("1"))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertLeftPositionInRootIsEqualTo(itemSize)
+        rule.onNodeWithTag("0")
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+    }
+
+    @Test
+    fun row_rtl_emitTwoItems_positionedReversed() {
+        rule.setContent {
+            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
+                LazyRow(
+                    reverseLayout = true
+                ) {
+                    item {
+                        Box(Modifier.size(itemSize).testTag("0"))
+                    }
+                    item {
+                        Box(Modifier.size(itemSize).testTag("1"))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertLeftPositionInRootIsEqualTo(itemSize)
+        rule.onNodeWithTag("0")
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+    }
+
+    @Test
+    fun row_rtl_scrollForwardHalfWay() {
+        lateinit var state: LazyListState
+        rule.setContent {
+            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
+                LazyRow(
+                    reverseLayout = true,
+                    state = rememberLazyListState().also { state = it },
+                    modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
+                ) {
+                    items((0..2).toList()) {
+                        Box(Modifier.size(itemSize).testTag("$it"))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(ContainerTag)
+            .scrollBy(x = itemSize * 0.5f, density = rule.density)
+
+        val scrolled = rule.runOnIdle {
+            assertThat(state.firstVisibleItemScrollOffset).isGreaterThan(0)
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+            with(rule.density) { state.firstVisibleItemScrollOffset.toDp() }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertLeftPositionInRootIsEqualTo(-scrolled)
+        rule.onNodeWithTag("1")
+            .assertLeftPositionInRootIsEqualTo(itemSize - scrolled)
+        rule.onNodeWithTag("2")
+            .assertLeftPositionInRootIsEqualTo(itemSize * 2 - scrolled)
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyNestedScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyNestedScrollingTest.kt
index 6b5902a..fca6c58 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyNestedScrollingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyNestedScrollingTest.kt
@@ -16,12 +16,14 @@
 
 package androidx.compose.foundation.lazy
 
-import androidx.compose.foundation.gestures.draggable
+import androidx.compose.foundation.gestures.rememberScrollableController
+import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.size
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.TouchSlop
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.down
@@ -34,6 +36,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth
+import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -46,21 +49,33 @@
     @get:Rule
     val rule = createComposeRule()
 
+    var expectedDragOffset = Float.MAX_VALUE
+
+    @Before
+    fun test() {
+        expectedDragOffset = with(rule.density) {
+            TouchSlop.toPx() + 20
+        }
+    }
+
     @Test
     fun column_nestedScrollingBackwardInitially() {
         val items = (1..3).toList()
         var draggedOffset = 0f
         rule.setContent {
             Box(
-                Modifier.draggable(Orientation.Vertical) {
-                    draggedOffset += it
-                }
+                Modifier.scrollable(
+                    orientation = Orientation.Vertical,
+                    controller = rememberScrollableController {
+                        draggedOffset += it
+                        it
+                    }
+                )
             ) {
-                LazyColumnFor(
-                    items = items,
-                    modifier = Modifier.size(100.dp).testTag(LazyTag)
-                ) {
-                    Spacer(Modifier.size(50.dp).testTag("$it"))
+                LazyColumn(Modifier.size(100.dp).testTag(LazyTag)) {
+                    items(items) {
+                        Spacer(Modifier.size(50.dp).testTag("$it"))
+                    }
                 }
             }
         }
@@ -83,15 +98,18 @@
         var draggedOffset = 0f
         rule.setContent {
             Box(
-                Modifier.draggable(Orientation.Vertical) {
-                    draggedOffset += it
-                }
+                Modifier.scrollable(
+                    orientation = Orientation.Vertical,
+                    controller = rememberScrollableController {
+                        draggedOffset += it
+                        it
+                    }
+                )
             ) {
-                LazyColumnFor(
-                    items = items,
-                    modifier = Modifier.size(100.dp).testTag(LazyTag)
-                ) {
-                    Spacer(Modifier.size(50.dp).testTag("$it"))
+                LazyColumn(Modifier.size(100.dp).testTag(LazyTag)) {
+                    items(items) {
+                        Spacer(Modifier.size(50.dp).testTag("$it"))
+                    }
                 }
             }
         }
@@ -109,12 +127,12 @@
             .performGesture {
                 draggedOffset = 0f
                 down(Offset(x = 10f, y = 10f))
-                moveBy(Offset(x = 0f, y = 50f))
+                moveBy(Offset(x = 0f, y = expectedDragOffset))
                 up()
             }
 
         rule.runOnIdle {
-            Truth.assertThat(draggedOffset).isEqualTo(50f)
+            Truth.assertThat(draggedOffset).isEqualTo(expectedDragOffset)
         }
     }
 
@@ -124,15 +142,18 @@
         var draggedOffset = 0f
         rule.setContent {
             Box(
-                Modifier.draggable(Orientation.Vertical) {
-                    draggedOffset += it
-                }
+                Modifier.scrollable(
+                    orientation = Orientation.Vertical,
+                    controller = rememberScrollableController {
+                        draggedOffset += it
+                        it
+                    }
+                )
             ) {
-                LazyColumnFor(
-                    items = items,
-                    modifier = Modifier.size(100.dp).testTag(LazyTag)
-                ) {
-                    Spacer(Modifier.size(40.dp).testTag("$it"))
+                LazyColumn(Modifier.size(100.dp).testTag(LazyTag)) {
+                    items(items) {
+                        Spacer(Modifier.size(40.dp).testTag("$it"))
+                    }
                 }
             }
         }
@@ -140,12 +161,12 @@
         rule.onNodeWithTag(LazyTag)
             .performGesture {
                 down(Offset(x = 10f, y = 10f))
-                moveBy(Offset(x = 0f, y = -50f))
+                moveBy(Offset(x = 0f, y = -expectedDragOffset))
                 up()
             }
 
         rule.runOnIdle {
-            Truth.assertThat(draggedOffset).isEqualTo(-50f)
+            Truth.assertThat(draggedOffset).isEqualTo(-expectedDragOffset)
         }
     }
 
@@ -155,15 +176,18 @@
         var draggedOffset = 0f
         rule.setContent {
             Box(
-                Modifier.draggable(Orientation.Vertical) {
-                    draggedOffset += it
-                }
+                Modifier.scrollable(
+                    orientation = Orientation.Vertical,
+                    controller = rememberScrollableController {
+                        draggedOffset += it
+                        it
+                    }
+                )
             ) {
-                LazyColumnFor(
-                    items = items,
-                    modifier = Modifier.size(100.dp).testTag(LazyTag)
-                ) {
-                    Spacer(Modifier.size(50.dp).testTag("$it"))
+                LazyColumn(Modifier.size(100.dp).testTag(LazyTag)) {
+                    items(items) {
+                        Spacer(Modifier.size(50.dp).testTag("$it"))
+                    }
                 }
             }
         }
@@ -176,12 +200,12 @@
             .performGesture {
                 draggedOffset = 0f
                 down(Offset(x = 10f, y = 10f))
-                moveBy(Offset(x = 0f, y = -50f))
+                moveBy(Offset(x = 0f, y = -expectedDragOffset))
                 up()
             }
 
         rule.runOnIdle {
-            Truth.assertThat(draggedOffset).isEqualTo(-50f)
+            Truth.assertThat(draggedOffset).isEqualTo(-expectedDragOffset)
         }
     }
 
@@ -191,15 +215,20 @@
         var draggedOffset = 0f
         rule.setContent {
             Box(
-                Modifier.draggable(Orientation.Horizontal) {
-                    draggedOffset += it
-                }
+                Modifier.scrollable(
+                    orientation = Orientation.Horizontal,
+                    controller = rememberScrollableController {
+                        draggedOffset += it
+                        it
+                    }
+                )
             ) {
-                LazyRowFor(
-                    items = items,
+                LazyRow(
                     modifier = Modifier.size(100.dp).testTag(LazyTag)
                 ) {
-                    Spacer(Modifier.size(50.dp).testTag("$it"))
+                    items(items) {
+                        Spacer(Modifier.size(50.dp).testTag("$it"))
+                    }
                 }
             }
         }
@@ -207,12 +236,12 @@
         rule.onNodeWithTag(LazyTag)
             .performGesture {
                 down(Offset(x = 10f, y = 10f))
-                moveBy(Offset(x = 100f, y = 0f))
+                moveBy(Offset(x = expectedDragOffset, y = 0f))
                 up()
             }
 
         rule.runOnIdle {
-            Truth.assertThat(draggedOffset).isEqualTo(100f)
+            Truth.assertThat(draggedOffset).isEqualTo(expectedDragOffset)
         }
     }
 
@@ -222,15 +251,20 @@
         var draggedOffset = 0f
         rule.setContent {
             Box(
-                Modifier.draggable(Orientation.Horizontal) {
-                    draggedOffset += it
-                }
+                Modifier.scrollable(
+                    orientation = Orientation.Horizontal,
+                    controller = rememberScrollableController {
+                        draggedOffset += it
+                        it
+                    }
+                )
             ) {
-                LazyRowFor(
-                    items = items,
+                LazyRow(
                     modifier = Modifier.size(100.dp).testTag(LazyTag)
                 ) {
-                    Spacer(Modifier.size(50.dp).testTag("$it"))
+                    items(items) {
+                        Spacer(Modifier.size(50.dp).testTag("$it"))
+                    }
                 }
             }
         }
@@ -248,12 +282,12 @@
             .performGesture {
                 draggedOffset = 0f
                 down(Offset(x = 10f, y = 10f))
-                moveBy(Offset(x = 50f, y = 0f))
+                moveBy(Offset(x = expectedDragOffset, y = 0f))
                 up()
             }
 
         rule.runOnIdle {
-            Truth.assertThat(draggedOffset).isEqualTo(50f)
+            Truth.assertThat(draggedOffset).isEqualTo(expectedDragOffset)
         }
     }
 
@@ -263,15 +297,20 @@
         var draggedOffset = 0f
         rule.setContent {
             Box(
-                Modifier.draggable(Orientation.Horizontal) {
-                    draggedOffset += it
-                }
+                Modifier.scrollable(
+                    orientation = Orientation.Horizontal,
+                    controller = rememberScrollableController {
+                        draggedOffset += it
+                        it
+                    }
+                )
             ) {
-                LazyRowFor(
-                    items = items,
+                LazyRow(
                     modifier = Modifier.size(100.dp).testTag(LazyTag)
                 ) {
-                    Spacer(Modifier.size(40.dp).testTag("$it"))
+                    items(items) {
+                        Spacer(Modifier.size(40.dp).testTag("$it"))
+                    }
                 }
             }
         }
@@ -279,12 +318,12 @@
         rule.onNodeWithTag(LazyTag)
             .performGesture {
                 down(Offset(x = 10f, y = 10f))
-                moveBy(Offset(x = -50f, y = 0f))
+                moveBy(Offset(x = -expectedDragOffset, y = 0f))
                 up()
             }
 
         rule.runOnIdle {
-            Truth.assertThat(draggedOffset).isEqualTo(-50f)
+            Truth.assertThat(draggedOffset).isEqualTo(-expectedDragOffset)
         }
     }
 
@@ -294,15 +333,20 @@
         var draggedOffset = 0f
         rule.setContent {
             Box(
-                Modifier.draggable(Orientation.Horizontal) {
-                    draggedOffset += it
-                }
+                Modifier.scrollable(
+                    orientation = Orientation.Horizontal,
+                    controller = rememberScrollableController {
+                        draggedOffset += it
+                        it
+                    }
+                )
             ) {
-                LazyRowFor(
-                    items = items,
+                LazyRow(
                     modifier = Modifier.size(100.dp).testTag(LazyTag)
                 ) {
-                    Spacer(Modifier.size(50.dp).testTag("$it"))
+                    items(items) {
+                        Spacer(Modifier.size(50.dp).testTag("$it"))
+                    }
                 }
             }
         }
@@ -315,12 +359,12 @@
             .performGesture {
                 draggedOffset = 0f
                 down(Offset(x = 10f, y = 10f))
-                moveBy(Offset(x = -50f, y = 0f))
+                moveBy(Offset(x = -expectedDragOffset, y = 0f))
                 up()
             }
 
         rule.runOnIdle {
-            Truth.assertThat(draggedOffset).isEqualTo(-50f)
+            Truth.assertThat(draggedOffset).isEqualTo(-expectedDragOffset)
         }
     }
 }
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
deleted file mode 100644
index f4d3fd4..0000000
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowForTest.kt
+++ /dev/null
@@ -1,903 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.lazy
-
-import androidx.compose.animation.core.snap
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.preferredSize
-import androidx.compose.foundation.layout.preferredWidth
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.sizeIn
-import androidx.compose.foundation.layout.width
-import androidx.compose.runtime.Providers
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.platform.AmbientLayoutDirection
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.assertHeightIsEqualTo
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertIsEqualTo
-import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
-import androidx.compose.ui.test.assertPositionInRootIsEqualTo
-import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.getUnclippedBoundsInRoot
-import androidx.compose.ui.test.junit4.StateRestorationTester
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import com.google.common.truth.Truth
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.runBlocking
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-class LazyRowForTest {
-    private val LazyRowForTag = "LazyRowForTag"
-
-    @get:Rule
-    val rule = createComposeRule()
-
-    @Test
-    fun lazyRowOnlyVisibleItemsAdded() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            Box(Modifier.preferredWidth(200.dp)) {
-                LazyRowFor(items) {
-                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
-                }
-            }
-        }
-
-        rule.onNodeWithTag("1")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("3")
-            .assertDoesNotExist()
-
-        rule.onNodeWithTag("4")
-            .assertDoesNotExist()
-    }
-
-    @Test
-    fun lazyRowScrollToShowItems123() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            Box(Modifier.preferredWidth(200.dp)) {
-                LazyRowFor(items, Modifier.testTag(LazyRowForTag)) {
-                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
-                }
-            }
-        }
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 50.dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("3")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("4")
-            .assertDoesNotExist()
-    }
-
-    @Test
-    fun lazyRowScrollToHideFirstItem() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            Box(Modifier.preferredWidth(200.dp)) {
-                LazyRowFor(items, Modifier.testTag(LazyRowForTag)) {
-                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
-                }
-            }
-        }
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 102.dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertDoesNotExist()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("3")
-            .assertIsDisplayed()
-    }
-
-    @Test
-    fun lazyRowScrollToShowItems234() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            Box(Modifier.preferredWidth(200.dp)) {
-                LazyRowFor(items, Modifier.testTag(LazyRowForTag)) {
-                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
-                }
-            }
-        }
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 150.dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertDoesNotExist()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("3")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("4")
-            .assertIsDisplayed()
-    }
-
-    @Test
-    fun lazyRowWrapsContent() = with(rule.density) {
-        val itemInsideLazyRow = "itemInsideLazyRow"
-        val itemOutsideLazyRow = "itemOutsideLazyRow"
-        var sameSizeItems by mutableStateOf(true)
-
-        rule.setContent {
-            Column {
-                LazyRowFor(
-                    items = listOf(1, 2),
-                    modifier = Modifier.testTag(LazyRowForTag)
-                ) {
-                    if (it == 1) {
-                        Spacer(Modifier.preferredSize(50.dp).testTag(itemInsideLazyRow))
-                    } else {
-                        Spacer(Modifier.preferredSize(if (sameSizeItems) 50.dp else 70.dp))
-                    }
-                }
-                Spacer(Modifier.preferredSize(50.dp).testTag(itemOutsideLazyRow))
-            }
-        }
-
-        rule.onNodeWithTag(itemInsideLazyRow)
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag(itemOutsideLazyRow)
-            .assertIsDisplayed()
-
-        var lazyRowBounds = rule.onNodeWithTag(LazyRowForTag)
-            .getUnclippedBoundsInRoot()
-
-        assertThat(lazyRowBounds.left.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
-        assertThat(lazyRowBounds.right.toIntPx()).isWithin1PixelFrom(100.dp.toIntPx())
-        assertThat(lazyRowBounds.top.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
-        assertThat(lazyRowBounds.bottom.toIntPx()).isWithin1PixelFrom(50.dp.toIntPx())
-
-        rule.runOnIdle {
-            sameSizeItems = false
-        }
-
-        rule.waitForIdle()
-
-        rule.onNodeWithTag(itemInsideLazyRow)
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag(itemOutsideLazyRow)
-            .assertIsDisplayed()
-
-        lazyRowBounds = rule.onNodeWithTag(LazyRowForTag)
-            .getUnclippedBoundsInRoot()
-
-        assertThat(lazyRowBounds.left.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
-        assertThat(lazyRowBounds.right.toIntPx()).isWithin1PixelFrom(120.dp.toIntPx())
-        assertThat(lazyRowBounds.top.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
-        assertThat(lazyRowBounds.bottom.toIntPx()).isWithin1PixelFrom(70.dp.toIntPx())
-    }
-
-    private val firstItemTag = "firstItemTag"
-    private val secondItemTag = "secondItemTag"
-
-    private fun prepareLazyRowForAlignment(verticalGravity: Alignment.Vertical) {
-        rule.setContent {
-            LazyRowFor(
-                items = listOf(1, 2),
-                modifier = Modifier.testTag(LazyRowForTag).height(100.dp),
-                verticalAlignment = verticalGravity
-            ) {
-                if (it == 1) {
-                    Spacer(Modifier.preferredSize(50.dp).testTag(firstItemTag))
-                } else {
-                    Spacer(Modifier.preferredSize(70.dp).testTag(secondItemTag))
-                }
-            }
-        }
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag(secondItemTag)
-            .assertIsDisplayed()
-
-        val lazyRowBounds = rule.onNodeWithTag(LazyRowForTag)
-            .getUnclippedBoundsInRoot()
-
-        with(rule.density) {
-            // Verify the height of the row
-            assertThat(lazyRowBounds.top.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
-            assertThat(lazyRowBounds.bottom.toIntPx()).isWithin1PixelFrom(100.dp.toIntPx())
-        }
-    }
-
-    @Test
-    fun lazyRowAlignmentCenterVertically() {
-        prepareLazyRowForAlignment(Alignment.CenterVertically)
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertPositionInRootIsEqualTo(0.dp, 25.dp)
-
-        rule.onNodeWithTag(secondItemTag)
-            .assertPositionInRootIsEqualTo(50.dp, 15.dp)
-    }
-
-    @Test
-    fun lazyRowAlignmentTop() {
-        prepareLazyRowForAlignment(Alignment.Top)
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertPositionInRootIsEqualTo(0.dp, 0.dp)
-
-        rule.onNodeWithTag(secondItemTag)
-            .assertPositionInRootIsEqualTo(50.dp, 0.dp)
-    }
-
-    @Test
-    fun lazyRowAlignmentBottom() {
-        prepareLazyRowForAlignment(Alignment.Bottom)
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertPositionInRootIsEqualTo(0.dp, 50.dp)
-
-        rule.onNodeWithTag(secondItemTag)
-            .assertPositionInRootIsEqualTo(50.dp, 30.dp)
-    }
-
-    @Test
-    fun itemFillingParentWidth() {
-        rule.setContent {
-            LazyRowFor(
-                items = listOf(0),
-                modifier = Modifier.size(width = 100.dp, height = 150.dp)
-            ) {
-                Spacer(Modifier.fillParentMaxWidth().height(50.dp).testTag(firstItemTag))
-            }
-        }
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertWidthIsEqualTo(100.dp)
-            .assertHeightIsEqualTo(50.dp)
-    }
-
-    @Test
-    fun itemFillingParentHeight() {
-        rule.setContent {
-            LazyRowFor(
-                items = listOf(0),
-                modifier = Modifier.size(width = 100.dp, height = 150.dp)
-            ) {
-                Spacer(Modifier.width(50.dp).fillParentMaxHeight().testTag(firstItemTag))
-            }
-        }
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertWidthIsEqualTo(50.dp)
-            .assertHeightIsEqualTo(150.dp)
-    }
-
-    @Test
-    fun itemFillingParentSize() {
-        rule.setContent {
-            LazyRowFor(
-                items = listOf(0),
-                modifier = Modifier.size(width = 100.dp, height = 150.dp)
-            ) {
-                Spacer(Modifier.fillParentMaxSize().testTag(firstItemTag))
-            }
-        }
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertWidthIsEqualTo(100.dp)
-            .assertHeightIsEqualTo(150.dp)
-    }
-
-    @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 {
-            LazyRowFor(
-                items = listOf(0),
-                modifier = Modifier.size(parentSize)
-            ) {
-                Spacer(Modifier.fillParentMaxSize().testTag(firstItemTag))
-            }
-        }
-
-        rule.runOnIdle {
-            parentSize = 150.dp
-        }
-
-        rule.onNodeWithTag(firstItemTag)
-            .assertWidthIsEqualTo(150.dp)
-            .assertHeightIsEqualTo(150.dp)
-    }
-
-    @Test
-    fun scrollsLeftInRtl() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
-                Box(Modifier.preferredWidth(100.dp)) {
-                    LazyRowFor(items, Modifier.testTag(LazyRowForTag)) {
-                        Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
-                    }
-                }
-            }
-        }
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = (-150).dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertDoesNotExist()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-    }
-
-    @Test
-    fun whenNotAnymoreAvailableItemWasDisplayed() {
-        var items by mutableStateOf((1..30).toList())
-        rule.setContent {
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag)
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        // after scroll we will display items 16-20
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 300.dp, density = rule.density)
-
-        rule.runOnIdle {
-            items = (1..10).toList()
-        }
-
-        // there is no item 16 anymore so we will just display the last items 6-10
-        rule.onNodeWithTag("6")
-            .assertLeftPositionIsAlmost(0.dp)
-    }
-
-    @Test
-    fun whenFewDisplayedItemsWereRemoved() {
-        var items by mutableStateOf((1..10).toList())
-        rule.setContent {
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag)
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        // after scroll we will display items 6-10
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 100.dp, density = rule.density)
-
-        rule.runOnIdle {
-            items = (1..8).toList()
-        }
-
-        // there are no more items 9 and 10, so we have to scroll back
-        rule.onNodeWithTag("4")
-            .assertLeftPositionIsAlmost(0.dp)
-    }
-
-    @Test
-    fun whenItemsBecameEmpty() {
-        var items by mutableStateOf((1..10).toList())
-        rule.setContent {
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.sizeIn(maxHeight = 100.dp).testTag(LazyRowForTag)
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        // after scroll we will display items 2-6
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 20.dp, density = rule.density)
-
-        rule.runOnIdle {
-            items = emptyList()
-        }
-
-        // there are no more items so the LazyRow is zero sized
-        rule.onNodeWithTag(LazyRowForTag)
-            .assertWidthIsEqualTo(0.dp)
-            .assertHeightIsEqualTo(0.dp)
-
-        // and has no children
-        rule.onNodeWithTag("1")
-            .assertDoesNotExist()
-        rule.onNodeWithTag("2")
-            .assertDoesNotExist()
-    }
-
-    @Test
-    fun scrollBackAndForth() {
-        val items by mutableStateOf((1..20).toList())
-        rule.setContent {
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag)
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        // after scroll we will display items 6-10
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 100.dp, density = rule.density)
-
-        // and scroll back
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = (-100).dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertLeftPositionIsAlmost(0.dp)
-    }
-
-    @Test
-    fun tryToScrollBackwardWhenAlreadyOnTop() {
-        val items by mutableStateOf((1..20).toList())
-        rule.setContent {
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag)
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        // we already displaying the first item, so this should do nothing
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = (-50).dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertLeftPositionIsAlmost(0.dp)
-        rule.onNodeWithTag("5")
-            .assertLeftPositionIsAlmost(80.dp)
-    }
-
-    private fun SemanticsNodeInteraction.assertLeftPositionIsAlmost(expected: Dp) {
-        getUnclippedBoundsInRoot().left.assertIsEqualTo(expected, tolerance = 1.dp)
-    }
-
-    @Test
-    fun contentOfNotStableItemsIsNotRecomposedDuringScroll() {
-        val items = listOf(NotStable(1), NotStable(2))
-        var firstItemRecomposed = 0
-        var secondItemRecomposed = 0
-        rule.setContent {
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag)
-            ) {
-                if (it.count == 1) {
-                    firstItemRecomposed++
-                } else {
-                    secondItemRecomposed++
-                }
-                Spacer(Modifier.size(75.dp))
-            }
-        }
-
-        rule.runOnIdle {
-            assertThat(firstItemRecomposed).isEqualTo(1)
-            assertThat(secondItemRecomposed).isEqualTo(1)
-        }
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = (50).dp, density = rule.density)
-
-        rule.runOnIdle {
-            assertThat(firstItemRecomposed).isEqualTo(1)
-            assertThat(secondItemRecomposed).isEqualTo(1)
-        }
-    }
-
-    @Test
-    fun onlyOneMeasurePassForScrollEvent() {
-        val items by mutableStateOf((1..20).toList())
-        lateinit var state: LazyListState
-        rule.setContent {
-            state = rememberLazyListState()
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        val initialMeasurePasses = state.numMeasurePasses
-
-        rule.runOnIdle {
-            with(rule.density) {
-                state.onScroll(-110.dp.toPx())
-            }
-        }
-
-        rule.waitForIdle()
-
-        assertThat(state.numMeasurePasses).isEqualTo(initialMeasurePasses + 1)
-    }
-
-    @Test
-    fun stateUpdatedAfterScroll() {
-        val items by mutableStateOf((1..20).toList())
-        lateinit var state: LazyListState
-        rule.setContent {
-            state = rememberLazyListState()
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.runOnIdle {
-            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
-            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
-        }
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 30.dp, density = rule.density)
-
-        rule.runOnIdle {
-            assertThat(state.firstVisibleItemIndex).isEqualTo(1)
-
-            with(rule.density) {
-                // TODO(b/169232491): test scrolling doesn't appear to be scrolling exactly the right
-                //  number of pixels
-                val expectedOffset = 10.dp.toIntPx()
-                val tolerance = 2.dp.toIntPx()
-                assertThat(state.firstVisibleItemScrollOffset).isEqualTo(expectedOffset, tolerance)
-            }
-        }
-    }
-
-    @Test
-    fun stateUpdatedAfterScrollWithinTheSameItem() {
-        val items by mutableStateOf((1..20).toList())
-        lateinit var state: LazyListState
-        rule.setContent {
-            state = rememberLazyListState()
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 10.dp, density = rule.density)
-
-        rule.runOnIdle {
-            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
-            with(rule.density) {
-                val expectedOffset = 10.dp.toIntPx()
-                val tolerance = 2.dp.toIntPx()
-                assertThat(state.firstVisibleItemScrollOffset)
-                    .isEqualTo(expectedOffset, tolerance)
-            }
-        }
-    }
-
-    @Test
-    fun initialScrollIsApplied() {
-        val items by mutableStateOf((0..20).toList())
-        lateinit var state: LazyListState
-        val expectedOffset = with(rule.density) { 10.dp.toIntPx() }
-        rule.setContent {
-            state = rememberLazyListState(2, expectedOffset)
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.runOnIdle {
-            assertThat(state.firstVisibleItemIndex).isEqualTo(2)
-            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(expectedOffset)
-        }
-
-        rule.onNodeWithTag("2")
-            .assertLeftPositionInRootIsEqualTo((-10).dp)
-    }
-
-    @Test
-    fun stateIsRestored() {
-        val restorationTester = StateRestorationTester(rule)
-        val items by mutableStateOf((1..20).toList())
-        var state: LazyListState? = null
-        restorationTester.setContent {
-            state = rememberLazyListState()
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag),
-                state = state!!
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 30.dp, density = rule.density)
-
-        val (index, scrollOffset) = rule.runOnIdle {
-            state!!.firstVisibleItemIndex to state!!.firstVisibleItemScrollOffset
-        }
-
-        state = null
-
-        restorationTester.emulateSavedInstanceStateRestore()
-
-        rule.runOnIdle {
-            assertThat(state!!.firstVisibleItemIndex).isEqualTo(index)
-            assertThat(state!!.firstVisibleItemScrollOffset).isEqualTo(scrollOffset)
-        }
-    }
-
-    @Test
-    fun snapToItemIndex() {
-        val items by mutableStateOf((1..20).toList())
-        lateinit var state: LazyListState
-        rule.setContent {
-            state = rememberLazyListState()
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag),
-                state = state
-            ) {
-                Spacer(Modifier.size(20.dp).testTag("$it"))
-            }
-        }
-
-        rule.runOnIdle {
-            runBlocking {
-                state.snapToItemIndex(3, 10)
-            }
-            assertThat(state.firstVisibleItemIndex).isEqualTo(3)
-            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
-        }
-    }
-
-    @Test
-    fun itemsAreNotRedrawnDuringScroll() {
-        val items = (0..20).toList()
-        val redrawCount = Array(6) { 0 }
-        rule.setContent {
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag)
-            ) {
-                Spacer(
-                    Modifier.size(20.dp)
-                        .drawBehind { redrawCount[it]++ }
-                )
-            }
-        }
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .scrollBy(x = 10.dp, density = rule.density)
-
-        rule.runOnIdle {
-            redrawCount.forEachIndexed { index, i ->
-                Truth.assertWithMessage("Item with index $index was redrawn $i times")
-                    .that(i).isEqualTo(1)
-            }
-        }
-    }
-
-    @Test
-    fun itemInvalidationIsNotCausingAnotherItemToRedraw() {
-        val items = (0..1).toList()
-        val redrawCount = Array(2) { 0 }
-        var stateUsedInDrawScope by mutableStateOf(false)
-        rule.setContent {
-            LazyRowFor(
-                items = items,
-                modifier = Modifier.size(100.dp).testTag(LazyRowForTag)
-            ) {
-                Spacer(
-                    Modifier.size(50.dp)
-                        .drawBehind {
-                            redrawCount[it]++
-                            if (it == 1) {
-                                stateUsedInDrawScope.hashCode()
-                            }
-                        }
-                )
-            }
-        }
-
-        rule.runOnIdle {
-            stateUsedInDrawScope = true
-        }
-
-        rule.runOnIdle {
-            Truth.assertWithMessage("First items is not expected to be redrawn")
-                .that(redrawCount[0]).isEqualTo(1)
-            Truth.assertWithMessage("Second items is expected to be redrawn")
-                .that(redrawCount[1]).isEqualTo(2)
-        }
-    }
-
-    @Test
-    fun notVisibleAnymoreItemNotAffectingCrossAxisSize() {
-        val items = (0..1).toList()
-        val itemSize = with(rule.density) { 30.toDp() }
-        val itemSizeMinusOne = with(rule.density) { 29.toDp() }
-        lateinit var state: LazyListState
-        rule.setContent {
-            LazyRowFor(
-                items = items,
-                state = rememberLazyListState().also { state = it },
-                modifier = Modifier.width(itemSizeMinusOne).testTag(LazyRowForTag)
-            ) {
-                Spacer(
-                    if (it == 0) {
-                        Modifier.height(30.dp).width(itemSizeMinusOne)
-                    } else {
-                        Modifier.height(20.dp).width(itemSize)
-                    }
-                )
-            }
-        }
-
-        state.scrollBy(itemSize)
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .assertHeightIsEqualTo(20.dp)
-    }
-
-    @Test
-    fun itemStillVisibleAfterOverscrollIsAffectingCrossAxisSize() {
-        val items = (0..2).toList()
-        val itemSize = with(rule.density) { 30.toDp() }
-        lateinit var state: LazyListState
-        rule.setContent {
-            LazyRowFor(
-                items = items,
-                state = rememberLazyListState().also { state = it },
-                modifier = Modifier.width(itemSize * 1.75f).testTag(LazyRowForTag)
-            ) {
-                Spacer(
-                    if (it == 0) {
-                        Modifier.height(30.dp).width(itemSize / 2)
-                    } else if (it == 1) {
-                        Modifier.height(20.dp).width(itemSize / 2)
-                    } else {
-                        Modifier.height(20.dp).width(itemSize)
-                    }
-                )
-            }
-        }
-
-        state.scrollBy(itemSize)
-
-        rule.onNodeWithTag(LazyRowForTag)
-            .assertHeightIsEqualTo(30.dp)
-    }
-
-    private fun LazyListState.scrollBy(offset: Dp) {
-        runBlocking {
-            smoothScrollBy(with(rule.density) { offset.toIntPx().toFloat() }, snap())
-        }
-    }
-}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
index fe720d7..ea14a36 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
@@ -16,17 +16,45 @@
 
 package androidx.compose.foundation.lazy
 
+import androidx.compose.animation.core.snap
+import androidx.compose.foundation.animation.smoothScrollBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.preferredWidth
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Providers
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.platform.AmbientLayoutDirection
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEqualTo
+import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -34,83 +62,12 @@
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 class LazyRowTest {
-    private val LazyRowTag = "LazyRowTag"
+    private val LazyListTag = "LazyListTag"
 
     @get:Rule
     val rule = createComposeRule()
 
     @Test
-    fun lazyRowShowsItem() {
-        val itemTestTag = "itemTestTag"
-
-        rule.setContent {
-            LazyRow {
-                item {
-                    Spacer(
-                        Modifier.preferredWidth(10.dp).fillParentMaxHeight().testTag(itemTestTag)
-                    )
-                }
-            }
-        }
-
-        rule.onNodeWithTag(itemTestTag)
-            .assertIsDisplayed()
-    }
-
-    @Test
-    fun lazyRowShowsItems() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            LazyRow(Modifier.preferredWidth(200.dp)) {
-                items(items) {
-                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
-                }
-            }
-        }
-
-        rule.onNodeWithTag("1")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("3")
-            .assertDoesNotExist()
-
-        rule.onNodeWithTag("4")
-            .assertDoesNotExist()
-    }
-
-    @Test
-    fun lazyRowShowsIndexedItems() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            LazyRow(Modifier.preferredWidth(200.dp)) {
-                itemsIndexed(items) { index, item ->
-                    Spacer(
-                        Modifier.preferredWidth(101.dp).fillParentMaxHeight()
-                            .testTag("$index-$item")
-                    )
-                }
-            }
-        }
-
-        rule.onNodeWithTag("0-1")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("1-2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("2-3")
-            .assertDoesNotExist()
-
-        rule.onNodeWithTag("3-4")
-            .assertDoesNotExist()
-    }
-
-    @Test
     fun lazyRowShowsCombinedItems() {
         val itemTestTag = "itemTestTag"
         val items = listOf(1, 2).map { it.toString() }
@@ -155,59 +112,6 @@
     }
 
     @Test
-    fun lazyRowShowsItemsOnScroll() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            LazyRow(Modifier.preferredWidth(200.dp).testTag(LazyRowTag)) {
-                items(items) {
-                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
-                }
-            }
-        }
-
-        rule.onNodeWithTag(LazyRowTag)
-            .scrollBy(x = 50.dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("3")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("4")
-            .assertDoesNotExist()
-    }
-
-    @Test
-    fun lazyRowScrollHidesItem() {
-        val items = (1..4).map { it.toString() }
-
-        rule.setContent {
-            LazyRow(Modifier.preferredWidth(200.dp).testTag(LazyRowTag)) {
-                items(items) {
-                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
-                }
-            }
-        }
-
-        rule.onNodeWithTag(LazyRowTag)
-            .scrollBy(x = 103.dp, density = rule.density)
-
-        rule.onNodeWithTag("1")
-            .assertDoesNotExist()
-
-        rule.onNodeWithTag("2")
-            .assertIsDisplayed()
-
-        rule.onNodeWithTag("3")
-            .assertIsDisplayed()
-    }
-
-    @Test
     fun lazyRowAllowEmptyListItems() {
         val itemTag = "itemTag"
 
@@ -253,4 +157,838 @@
         rule.onNodeWithTag("3")
             .assertDoesNotExist()
     }
-}
\ No newline at end of file
+
+    @Test
+    fun lazyRowOnlyVisibleItemsAdded() {
+        val items = (1..4).map { it.toString() }
+
+        rule.setContent {
+            Box(Modifier.preferredWidth(200.dp)) {
+                LazyRow {
+                    items(items) {
+                        Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("3")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("4")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun lazyRowScrollToShowItems123() {
+        val items = (1..4).map { it.toString() }
+
+        rule.setContent {
+            Box(Modifier.preferredWidth(200.dp)) {
+                LazyRow(Modifier.testTag(LazyListTag)) {
+                    items(items) {
+                        Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 50.dp, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("3")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("4")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun lazyRowScrollToHideFirstItem() {
+        val items = (1..4).map { it.toString() }
+
+        rule.setContent {
+            Box(Modifier.preferredWidth(200.dp)) {
+                LazyRow(Modifier.testTag(LazyListTag)) {
+                    items(items) {
+                        Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 102.dp, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("3")
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun lazyRowScrollToShowItems234() {
+        val items = (1..4).map { it.toString() }
+
+        rule.setContent {
+            Box(Modifier.preferredWidth(200.dp)) {
+                LazyRow(Modifier.testTag(LazyListTag)) {
+                    items(items) {
+                        Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 150.dp, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("3")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("4")
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun lazyRowWrapsContent() = with(rule.density) {
+        val itemInsideLazyRow = "itemInsideLazyRow"
+        val itemOutsideLazyRow = "itemOutsideLazyRow"
+        var sameSizeItems by mutableStateOf(true)
+
+        rule.setContent {
+            Column {
+                LazyRow(Modifier.testTag(LazyListTag)) {
+                    items(listOf(1, 2)) {
+                        if (it == 1) {
+                            Spacer(Modifier.preferredSize(50.dp).testTag(itemInsideLazyRow))
+                        } else {
+                            Spacer(Modifier.preferredSize(if (sameSizeItems) 50.dp else 70.dp))
+                        }
+                    }
+                }
+                Spacer(Modifier.preferredSize(50.dp).testTag(itemOutsideLazyRow))
+            }
+        }
+
+        rule.onNodeWithTag(itemInsideLazyRow)
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag(itemOutsideLazyRow)
+            .assertIsDisplayed()
+
+        var lazyRowBounds = rule.onNodeWithTag(LazyListTag)
+            .getUnclippedBoundsInRoot()
+
+        assertThat(lazyRowBounds.left.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
+        assertThat(lazyRowBounds.right.toIntPx()).isWithin1PixelFrom(100.dp.toIntPx())
+        assertThat(lazyRowBounds.top.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
+        assertThat(lazyRowBounds.bottom.toIntPx()).isWithin1PixelFrom(50.dp.toIntPx())
+
+        rule.runOnIdle {
+            sameSizeItems = false
+        }
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(itemInsideLazyRow)
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag(itemOutsideLazyRow)
+            .assertIsDisplayed()
+
+        lazyRowBounds = rule.onNodeWithTag(LazyListTag)
+            .getUnclippedBoundsInRoot()
+
+        assertThat(lazyRowBounds.left.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
+        assertThat(lazyRowBounds.right.toIntPx()).isWithin1PixelFrom(120.dp.toIntPx())
+        assertThat(lazyRowBounds.top.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
+        assertThat(lazyRowBounds.bottom.toIntPx()).isWithin1PixelFrom(70.dp.toIntPx())
+    }
+
+    private val firstItemTag = "firstItemTag"
+    private val secondItemTag = "secondItemTag"
+
+    private fun prepareLazyRowForAlignment(verticalGravity: Alignment.Vertical) {
+        rule.setContent {
+            LazyRow(
+                Modifier.testTag(LazyListTag).height(100.dp),
+                verticalAlignment = verticalGravity
+            ) {
+                items(listOf(1, 2)) {
+                    if (it == 1) {
+                        Spacer(Modifier.preferredSize(50.dp).testTag(firstItemTag))
+                    } else {
+                        Spacer(Modifier.preferredSize(70.dp).testTag(secondItemTag))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag(secondItemTag)
+            .assertIsDisplayed()
+
+        val lazyRowBounds = rule.onNodeWithTag(LazyListTag)
+            .getUnclippedBoundsInRoot()
+
+        with(rule.density) {
+            // Verify the height of the row
+            assertThat(lazyRowBounds.top.toIntPx()).isWithin1PixelFrom(0.dp.toIntPx())
+            assertThat(lazyRowBounds.bottom.toIntPx()).isWithin1PixelFrom(100.dp.toIntPx())
+        }
+    }
+
+    @Test
+    fun lazyRowAlignmentCenterVertically() {
+        prepareLazyRowForAlignment(Alignment.CenterVertically)
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertPositionInRootIsEqualTo(0.dp, 25.dp)
+
+        rule.onNodeWithTag(secondItemTag)
+            .assertPositionInRootIsEqualTo(50.dp, 15.dp)
+    }
+
+    @Test
+    fun lazyRowAlignmentTop() {
+        prepareLazyRowForAlignment(Alignment.Top)
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+
+        rule.onNodeWithTag(secondItemTag)
+            .assertPositionInRootIsEqualTo(50.dp, 0.dp)
+    }
+
+    @Test
+    fun lazyRowAlignmentBottom() {
+        prepareLazyRowForAlignment(Alignment.Bottom)
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertPositionInRootIsEqualTo(0.dp, 50.dp)
+
+        rule.onNodeWithTag(secondItemTag)
+            .assertPositionInRootIsEqualTo(50.dp, 30.dp)
+    }
+
+    @Test
+    fun itemFillingParentWidth() {
+        rule.setContent {
+            LazyRow(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.fillParentMaxWidth().height(50.dp).testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(100.dp)
+            .assertHeightIsEqualTo(50.dp)
+    }
+
+    @Test
+    fun itemFillingParentHeight() {
+        rule.setContent {
+            LazyRow(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.width(50.dp).fillParentMaxHeight().testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(50.dp)
+            .assertHeightIsEqualTo(150.dp)
+    }
+
+    @Test
+    fun itemFillingParentSize() {
+        rule.setContent {
+            LazyRow(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.fillParentMaxSize().testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(100.dp)
+            .assertHeightIsEqualTo(150.dp)
+    }
+
+    @Test
+    fun itemFillingParentWidthFraction() {
+        rule.setContent {
+            LazyRow(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.fillParentMaxWidth(0.7f).height(50.dp).testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(70.dp)
+            .assertHeightIsEqualTo(50.dp)
+    }
+
+    @Test
+    fun itemFillingParentHeightFraction() {
+        rule.setContent {
+            LazyRow(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.width(50.dp).fillParentMaxHeight(0.3f).testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(50.dp)
+            .assertHeightIsEqualTo(45.dp)
+    }
+
+    @Test
+    fun itemFillingParentSizeFraction() {
+        rule.setContent {
+            LazyRow(Modifier.size(width = 100.dp, height = 150.dp)) {
+                items(listOf(0)) {
+                    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 {
+            LazyRow(Modifier.size(parentSize)) {
+                items(listOf(0)) {
+                    Spacer(Modifier.fillParentMaxSize().testTag(firstItemTag))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            parentSize = 150.dp
+        }
+
+        rule.onNodeWithTag(firstItemTag)
+            .assertWidthIsEqualTo(150.dp)
+            .assertHeightIsEqualTo(150.dp)
+    }
+
+    @Test
+    fun scrollsLeftInRtl() {
+        val items = (1..4).map { it.toString() }
+
+        rule.setContent {
+            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
+                Box(Modifier.preferredWidth(100.dp)) {
+                    LazyRow(Modifier.testTag(LazyListTag)) {
+                        items(items) {
+                            Spacer(
+                                Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag(it)
+                            )
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = (-150).dp, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun whenNotAnymoreAvailableItemWasDisplayed() {
+        var items by mutableStateOf((1..30).toList())
+        rule.setContent {
+            LazyRow(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        // after scroll we will display items 16-20
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 300.dp, density = rule.density)
+
+        rule.runOnIdle {
+            items = (1..10).toList()
+        }
+
+        // there is no item 16 anymore so we will just display the last items 6-10
+        rule.onNodeWithTag("6")
+            .assertLeftPositionIsAlmost(0.dp)
+    }
+
+    @Test
+    fun whenFewDisplayedItemsWereRemoved() {
+        var items by mutableStateOf((1..10).toList())
+        rule.setContent {
+            LazyRow(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        // after scroll we will display items 6-10
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 100.dp, density = rule.density)
+
+        rule.runOnIdle {
+            items = (1..8).toList()
+        }
+
+        // there are no more items 9 and 10, so we have to scroll back
+        rule.onNodeWithTag("4")
+            .assertLeftPositionIsAlmost(0.dp)
+    }
+
+    @Test
+    fun whenItemsBecameEmpty() {
+        var items by mutableStateOf((1..10).toList())
+        rule.setContent {
+            LazyRow(Modifier.sizeIn(maxHeight = 100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        // after scroll we will display items 2-6
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 20.dp, density = rule.density)
+
+        rule.runOnIdle {
+            items = emptyList()
+        }
+
+        // there are no more items so the LazyRow is zero sized
+        rule.onNodeWithTag(LazyListTag)
+            .assertWidthIsEqualTo(0.dp)
+            .assertHeightIsEqualTo(0.dp)
+
+        // and has no children
+        rule.onNodeWithTag("1")
+            .assertDoesNotExist()
+        rule.onNodeWithTag("2")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun scrollBackAndForth() {
+        val items by mutableStateOf((1..20).toList())
+        rule.setContent {
+            LazyRow(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        // after scroll we will display items 6-10
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 100.dp, density = rule.density)
+
+        // and scroll back
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = (-100).dp, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertLeftPositionIsAlmost(0.dp)
+    }
+
+    @Test
+    fun tryToScrollBackwardWhenAlreadyOnTop() {
+        val items by mutableStateOf((1..20).toList())
+        rule.setContent {
+            LazyRow(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        // we already displaying the first item, so this should do nothing
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = (-50).dp, density = rule.density)
+
+        rule.onNodeWithTag("1")
+            .assertLeftPositionIsAlmost(0.dp)
+        rule.onNodeWithTag("5")
+            .assertLeftPositionIsAlmost(80.dp)
+    }
+
+    private fun SemanticsNodeInteraction.assertLeftPositionIsAlmost(expected: Dp) {
+        getUnclippedBoundsInRoot().left.assertIsEqualTo(expected, tolerance = 1.dp)
+    }
+
+    @Test
+    fun contentOfNotStableItemsIsNotRecomposedDuringScroll() {
+        val items = listOf(NotStable(1), NotStable(2))
+        var firstItemRecomposed = 0
+        var secondItemRecomposed = 0
+        rule.setContent {
+            LazyRow(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    if (it.count == 1) {
+                        firstItemRecomposed++
+                    } else {
+                        secondItemRecomposed++
+                    }
+                    Spacer(Modifier.size(75.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(firstItemRecomposed).isEqualTo(1)
+            assertThat(secondItemRecomposed).isEqualTo(1)
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = (50).dp, density = rule.density)
+
+        rule.runOnIdle {
+            assertThat(firstItemRecomposed).isEqualTo(1)
+            assertThat(secondItemRecomposed).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun onlyOneMeasurePassForScrollEvent() {
+        val items by mutableStateOf((1..20).toList())
+        lateinit var state: LazyListState
+        rule.setContent {
+            state = rememberLazyListState()
+            LazyRow(Modifier.size(100.dp), state = state) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        val initialMeasurePasses = state.numMeasurePasses
+
+        rule.runOnIdle {
+            with(rule.density) {
+                state.onScroll(-110.dp.toPx())
+            }
+        }
+
+        rule.waitForIdle()
+
+        assertThat(state.numMeasurePasses).isEqualTo(initialMeasurePasses + 1)
+    }
+
+    @Test
+    fun stateUpdatedAfterScroll() {
+        val items by mutableStateOf((1..20).toList())
+        lateinit var state: LazyListState
+        rule.setContent {
+            state = rememberLazyListState()
+            LazyRow(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 30.dp, density = rule.density)
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(1)
+
+            with(rule.density) {
+                // TODO(b/169232491): test scrolling doesn't appear to be scrolling exactly the right
+                //  number of pixels
+                val expectedOffset = 10.dp.toIntPx()
+                val tolerance = 2.dp.toIntPx()
+                assertThat(state.firstVisibleItemScrollOffset).isEqualTo(expectedOffset, tolerance)
+            }
+        }
+    }
+
+    @Test
+    fun stateUpdatedAfterScrollWithinTheSameItem() {
+        val items by mutableStateOf((1..20).toList())
+        lateinit var state: LazyListState
+        rule.setContent {
+            state = rememberLazyListState()
+            LazyRow(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 10.dp, density = rule.density)
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+            with(rule.density) {
+                val expectedOffset = 10.dp.toIntPx()
+                val tolerance = 2.dp.toIntPx()
+                assertThat(state.firstVisibleItemScrollOffset)
+                    .isEqualTo(expectedOffset, tolerance)
+            }
+        }
+    }
+
+    @Test
+    fun initialScrollIsApplied() {
+        val items by mutableStateOf((0..20).toList())
+        lateinit var state: LazyListState
+        val expectedOffset = with(rule.density) { 10.dp.toIntPx() }
+        rule.setContent {
+            state = rememberLazyListState(2, expectedOffset)
+            LazyRow(Modifier.size(100.dp).testTag(LazyListTag), state = state) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(expectedOffset)
+        }
+
+        rule.onNodeWithTag("2")
+            .assertLeftPositionInRootIsEqualTo((-10).dp)
+    }
+
+    @Test
+    fun stateIsRestored() {
+        val restorationTester = StateRestorationTester(rule)
+        val items by mutableStateOf((1..20).toList())
+        var state: LazyListState? = null
+        restorationTester.setContent {
+            state = rememberLazyListState()
+            LazyRow(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state!!
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 30.dp, density = rule.density)
+
+        val (index, scrollOffset) = rule.runOnIdle {
+            state!!.firstVisibleItemIndex to state!!.firstVisibleItemScrollOffset
+        }
+
+        state = null
+
+        restorationTester.emulateSavedInstanceStateRestore()
+
+        rule.runOnIdle {
+            assertThat(state!!.firstVisibleItemIndex).isEqualTo(index)
+            assertThat(state!!.firstVisibleItemScrollOffset).isEqualTo(scrollOffset)
+        }
+    }
+
+    @Test
+    fun snapToItemIndex() {
+        val items by mutableStateOf((1..20).toList())
+        lateinit var state: LazyListState
+        rule.setContent {
+            state = rememberLazyListState()
+            LazyRow(
+                Modifier.size(100.dp).testTag(LazyListTag),
+                state = state
+            ) {
+                items(items) {
+                    Spacer(Modifier.size(20.dp).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            runBlocking {
+                state.snapToItemIndex(3, 10)
+            }
+            assertThat(state.firstVisibleItemIndex).isEqualTo(3)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+        }
+    }
+
+    @Test
+    fun itemsAreNotRedrawnDuringScroll() {
+        val items = (0..20).toList()
+        val redrawCount = Array(6) { 0 }
+        rule.setContent {
+            LazyRow(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(
+                        Modifier.size(20.dp)
+                            .drawBehind { redrawCount[it]++ }
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag(LazyListTag)
+            .scrollBy(x = 10.dp, density = rule.density)
+
+        rule.runOnIdle {
+            redrawCount.forEachIndexed { index, i ->
+                Truth.assertWithMessage("Item with index $index was redrawn $i times")
+                    .that(i).isEqualTo(1)
+            }
+        }
+    }
+
+    @Test
+    fun itemInvalidationIsNotCausingAnotherItemToRedraw() {
+        val items = (0..1).toList()
+        val redrawCount = Array(2) { 0 }
+        var stateUsedInDrawScope by mutableStateOf(false)
+        rule.setContent {
+            LazyRow(Modifier.size(100.dp).testTag(LazyListTag)) {
+                items(items) {
+                    Spacer(
+                        Modifier.size(50.dp)
+                            .drawBehind {
+                                redrawCount[it]++
+                                if (it == 1) {
+                                    stateUsedInDrawScope.hashCode()
+                                }
+                            }
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            stateUsedInDrawScope = true
+        }
+
+        rule.runOnIdle {
+            Truth.assertWithMessage("First items is not expected to be redrawn")
+                .that(redrawCount[0]).isEqualTo(1)
+            Truth.assertWithMessage("Second items is expected to be redrawn")
+                .that(redrawCount[1]).isEqualTo(2)
+        }
+    }
+
+    @Test
+    fun notVisibleAnymoreItemNotAffectingCrossAxisSize() {
+        val items = (0..1).toList()
+        val itemSize = with(rule.density) { 30.toDp() }
+        val itemSizeMinusOne = with(rule.density) { 29.toDp() }
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyRow(
+                Modifier.width(itemSizeMinusOne).testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it }
+            ) {
+                items(items) {
+                    Spacer(
+                        if (it == 0) {
+                            Modifier.height(30.dp).width(itemSizeMinusOne)
+                        } else {
+                            Modifier.height(20.dp).width(itemSize)
+                        }
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(itemSize)
+
+        rule.onNodeWithTag(LazyListTag)
+            .assertHeightIsEqualTo(20.dp)
+    }
+
+    @Test
+    fun itemStillVisibleAfterOverscrollIsAffectingCrossAxisSize() {
+        val items = (0..2).toList()
+        val itemSize = with(rule.density) { 30.toDp() }
+        lateinit var state: LazyListState
+        rule.setContent {
+            LazyRow(
+                Modifier.width(itemSize * 1.75f).testTag(LazyListTag),
+                state = rememberLazyListState().also { state = it }
+            ) {
+                items(items) {
+                    Spacer(
+                        if (it == 0) {
+                            Modifier.height(30.dp).width(itemSize / 2)
+                        } else if (it == 1) {
+                            Modifier.height(20.dp).width(itemSize / 2)
+                        } else {
+                            Modifier.height(20.dp).width(itemSize)
+                        }
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(itemSize)
+
+        rule.onNodeWithTag(LazyListTag)
+            .assertHeightIsEqualTo(30.dp)
+    }
+
+    private fun LazyListState.scrollBy(offset: Dp) {
+        runBlocking {
+            smoothScrollBy(with(rule.density) { offset.toIntPx().toFloat() }, snap())
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt
index 8f60ce8c..f720630 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt
@@ -17,18 +17,18 @@
 package androidx.compose.foundation.lazy
 
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.animation.smoothScrollBy
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
-import androidx.compose.ui.test.ExperimentalTesting
-import androidx.compose.ui.test.TestUiDispatcher
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.unit.dp
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
 import org.junit.Before
@@ -39,11 +39,8 @@
 import kotlin.math.roundToInt
 
 @MediumTest
-@OptIn(ExperimentalTesting::class)
 @RunWith(Parameterized::class)
 class LazyScrollTest(private val orientation: Orientation) {
-    private val LazyListTag = "LazyListTag"
-
     @get:Rule
     val rule = createComposeRule()
 
@@ -69,7 +66,7 @@
 
     @Test
     fun snapToItemTest() = runBlocking {
-        withContext(TestUiDispatcher.Main) {
+        withContext(Dispatchers.Main) {
             state.snapToItemIndex(3)
         }
         assertThat(state.firstVisibleItemIndex).isEqualTo(3)
@@ -79,29 +76,39 @@
     @ExperimentalFoundationApi
     @Test
     fun smoothScrollByTest() = runBlocking {
-        withContext(TestUiDispatcher.Main) {
-            state.smoothScrollBy(with(rule.density) { 320.dp.toPx() })
+        fun Int.dpToPx(): Int = with(rule.density) { dp.toPx().roundToInt() }
+        val scrollDistance = 320.dpToPx()
+        val itemSize = 101.dpToPx()
+
+        val expectedIndex = scrollDistance / itemSize // resolves to 3
+        val expectedOffset = scrollDistance % itemSize // resolves to ~17.dp.toIntPx()
+
+        withContext(Dispatchers.Main) {
+            state.smoothScrollBy(scrollDistance.toFloat())
         }
-        assertThat(state.firstVisibleItemIndex).isEqualTo(3)
-        assertThat(state.firstVisibleItemScrollOffset)
-            .isEqualTo(with(rule.density) { 17.dp.toPx().roundToInt() })
+        assertThat(state.firstVisibleItemIndex).isEqualTo(expectedIndex)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(expectedOffset)
     }
 
     @Composable
     private fun TestContent() {
         if (vertical) {
-            LazyColumnFor(items, Modifier.preferredHeight(300.dp), state) {
-                ItemContent()
+            LazyColumn(Modifier.preferredHeight(300.dp), state) {
+                items(items) {
+                    ItemContent()
+                }
             }
         } else {
-            LazyRowFor(items, Modifier.preferredWidth(300.dp), state) {
-                ItemContent()
+            LazyRow(Modifier.preferredWidth(300.dp), state) {
+                items(items) {
+                    ItemContent()
+                }
             }
         }
     }
 
     @Composable
-    private fun LazyItemScope.ItemContent() {
+    private fun ItemContent() {
         val modifier = if (vertical) {
             Modifier.preferredHeight(101.dp)
         } else {
@@ -115,4 +122,4 @@
         @Parameterized.Parameters(name = "{0}")
         fun params() = arrayOf(Orientation.Vertical, Orientation.Horizontal)
     }
-}
\ No newline at end of file
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt
index fafdf33..44215c9 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.foundation.shape
 
-import androidx.compose.ui.geometry.RoundRect
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.geometry.toRect
 import androidx.compose.ui.graphics.Outline
@@ -163,46 +162,6 @@
     }
 
     @Test
-    fun theSameImplsWithTheSameCornersAreEquals() {
-        @Suppress("ReplaceCallWithBinaryOperator")
-        assertThat(
-            Impl2(
-                topLeft = CornerSize(4.0f),
-                topRight = CornerSize(3.0f),
-                bottomRight = CornerSize(3.dp),
-                bottomLeft = CornerSize(50)
-            ).equals(
-                Impl2(
-                    topLeft = CornerSize(4.0f),
-                    topRight = CornerSize(3.0f),
-                    bottomRight = CornerSize(3.dp),
-                    bottomLeft = CornerSize(50)
-                )
-            )
-        ).isTrue()
-    }
-
-    @Test
-    fun differentImplWithTheSameCornersAreNotEquals() {
-        @Suppress("ReplaceCallWithBinaryOperator")
-        assertThat(
-            Impl(
-                topLeft = CornerSize(4.0f),
-                topRight = CornerSize(3.0f),
-                bottomRight = CornerSize(3.dp),
-                bottomLeft = CornerSize(50)
-            ).equals(
-                Impl2(
-                    topLeft = CornerSize(4.0f),
-                    topRight = CornerSize(3.0f),
-                    bottomRight = CornerSize(3.dp),
-                    bottomLeft = CornerSize(50)
-                )
-            )
-        ).isFalse()
-    }
-
-    @Test
     fun copyingUsesCorrectDefaults() {
         val impl = Impl(
             topLeft = CornerSize(4.0f),
@@ -246,29 +205,24 @@
         bottomRight: CornerSize,
         bottomLeft: CornerSize
     ) = Impl(topLeft, topRight, bottomRight, bottomLeft, onOutlineRequested)
-}
 
-private class Impl2(
-    topLeft: CornerSize,
-    topRight: CornerSize,
-    bottomRight: CornerSize,
-    bottomLeft: CornerSize
-) : CornerBasedShape(topLeft, topRight, bottomRight, bottomLeft) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Impl) return false
 
-    override fun createOutline(
-        size: Size,
-        topLeft: Float,
-        topRight: Float,
-        bottomRight: Float,
-        bottomLeft: Float
-    ): Outline {
-        return Outline.Rounded(RoundRect(size.toRect()))
+        if (topLeft != other.topLeft) return false
+        if (topRight != other.topRight) return false
+        if (bottomRight != other.bottomRight) return false
+        if (bottomLeft != other.bottomLeft) return false
+
+        return true
     }
 
-    override fun copy(
-        topLeft: CornerSize,
-        topRight: CornerSize,
-        bottomRight: CornerSize,
-        bottomLeft: CornerSize
-    ) = Impl2(topLeft, topRight, bottomRight, bottomLeft)
-}
\ No newline at end of file
+    override fun hashCode(): Int {
+        var result = topLeft.hashCode()
+        result = 31 * result + topRight.hashCode()
+        result = 31 * result + bottomRight.hashCode()
+        result = 31 * result + bottomLeft.hashCode()
+        return result
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CutCornerShapeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CutCornerShapeTest.kt
index 476d9ef..834d3e4 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CutCornerShapeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CutCornerShapeTest.kt
@@ -137,6 +137,46 @@
         )
     }
 
+    @Test
+    fun objectsWithTheSameCornersAreEquals() {
+        @Suppress("ReplaceCallWithBinaryOperator")
+        assertThat(
+            CutCornerShape(
+                topLeft = CornerSize(4.0f),
+                topRight = CornerSize(3.0f),
+                bottomRight = CornerSize(3.dp),
+                bottomLeft = CornerSize(50)
+            ).equals(
+                CutCornerShape(
+                    topLeft = CornerSize(4.0f),
+                    topRight = CornerSize(3.0f),
+                    bottomRight = CornerSize(3.dp),
+                    bottomLeft = CornerSize(50)
+                )
+            )
+        ).isTrue()
+    }
+
+    @Test
+    fun objectsWithDifferentCornersAreNotEquals() {
+        @Suppress("ReplaceCallWithBinaryOperator")
+        assertThat(
+            CutCornerShape(
+                topLeft = CornerSize(4.0f),
+                topRight = CornerSize(3.0f),
+                bottomRight = CornerSize(3.dp),
+                bottomLeft = CornerSize(50)
+            ).equals(
+                CutCornerShape(
+                    topLeft = CornerSize(4.0f),
+                    topRight = CornerSize(5.0f),
+                    bottomRight = CornerSize(3.dp),
+                    bottomLeft = CornerSize(50)
+                )
+            )
+        ).isFalse()
+    }
+
     private fun Shape.toOutline() = createOutline(size, density)
 }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/RoundedCornerShapeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/RoundedCornerShapeTest.kt
index 0212478..13b823e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/RoundedCornerShapeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/RoundedCornerShapeTest.kt
@@ -132,5 +132,64 @@
         )
     }
 
+    @Test
+    fun objectsWithTheSameCornersAreEquals() {
+        @Suppress("ReplaceCallWithBinaryOperator")
+        assertThat(
+            RoundedCornerShape(
+                topLeft = CornerSize(4.0f),
+                topRight = CornerSize(3.0f),
+                bottomRight = CornerSize(3.dp),
+                bottomLeft = CornerSize(50)
+            ).equals(
+                RoundedCornerShape(
+                    topLeft = CornerSize(4.0f),
+                    topRight = CornerSize(3.0f),
+                    bottomRight = CornerSize(3.dp),
+                    bottomLeft = CornerSize(50)
+                )
+            )
+        ).isTrue()
+    }
+
+    @Test
+    fun objectsWithDifferentCornersAreNotEquals() {
+        @Suppress("ReplaceCallWithBinaryOperator")
+        assertThat(
+            RoundedCornerShape(
+                topLeft = CornerSize(4.0f),
+                topRight = CornerSize(3.0f),
+                bottomRight = CornerSize(3.dp),
+                bottomLeft = CornerSize(50)
+            ).equals(
+                RoundedCornerShape(
+                    topLeft = CornerSize(4.0f),
+                    topRight = CornerSize(5.0f),
+                    bottomRight = CornerSize(3.dp),
+                    bottomLeft = CornerSize(50)
+                )
+            )
+        ).isFalse()
+    }
+
+    fun notEqualsToCutCornersWithTheSameSizes() {
+        @Suppress("ReplaceCallWithBinaryOperator")
+        assertThat(
+            RoundedCornerShape(
+                topLeft = CornerSize(4.0f),
+                topRight = CornerSize(3.0f),
+                bottomRight = CornerSize(3.dp),
+                bottomLeft = CornerSize(50)
+            ).equals(
+                CutCornerShape(
+                    topLeft = CornerSize(4.0f),
+                    topRight = CornerSize(3.0f),
+                    bottomRight = CornerSize(3.dp),
+                    bottomLeft = CornerSize(50)
+                )
+            )
+        ).isFalse()
+    }
+
     private fun Shape.toOutline() = createOutline(size, density)
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt
index 441aa85..c1ccc04 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt
@@ -17,9 +17,8 @@
 package androidx.compose.foundation.text
 
 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.focus.onFocusChanged
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -48,8 +47,7 @@
 
 @OptIn(
     ExperimentalTextApi::class,
-    InternalTextApi::class,
-    ExperimentalFocus::class
+    InternalTextApi::class
 )
 @LargeTest
 @RunWith(AndroidJUnit4::class)
@@ -82,7 +80,7 @@
                 imeOptions = imeOptions,
                 modifier = Modifier
                     .testTag(testTag)
-                    .focusObserver { focused = it.isFocused },
+                    .onFocusChanged { focused = it.isFocused },
                 onValueChange = {}
             )
         }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldSoftKeyboardTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldSoftKeyboardTest.kt
new file mode 100644
index 0000000..b51ab7f
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldSoftKeyboardTest.kt
@@ -0,0 +1,255 @@
+/*
+ * 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.text
+
+import android.os.Build
+import android.view.View
+import android.view.WindowInsets
+import android.view.WindowInsetsAnimation
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.layout.Column
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusManager
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusReference
+import androidx.compose.ui.platform.AmbientFocusManager
+import androidx.compose.ui.platform.AmbientView
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.text.InternalTextApi
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(InternalTextApi::class)
+class CoreTextFieldSoftKeyboardTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun keyboardShownOnInitialClick() {
+        // Arrange.
+        lateinit var view: View
+        rule.setContent {
+            view = AmbientView.current
+            CoreTextField(
+                value = TextFieldValue("Hello"),
+                onValueChange = {},
+                modifier = Modifier.testTag("TextField1")
+            )
+        }
+        view.ensureKeyboardIsHidden()
+
+        // Act.
+        val isSoftKeyboardShown = view.runAndWaitUntil({ view.isSoftwareKeyboardShown() }) {
+            rule.onNodeWithTag("TextField1").performClick()
+        }
+
+        // Assert.
+        assertThat(isSoftKeyboardShown).isTrue()
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun keyboardShownOnInitialFocus() {
+        // Arrange.
+        val focusReference = FocusReference()
+        lateinit var view: View
+        rule.setContent {
+            view = AmbientView.current
+            CoreTextField(
+                value = TextFieldValue("Hello"),
+                onValueChange = {},
+                modifier = Modifier.focusReference(focusReference)
+            )
+        }
+        view.ensureKeyboardIsHidden()
+
+        // Act.
+        val isSoftKeyboardShown = view.runAndWaitUntil({ view.isSoftwareKeyboardShown() }) {
+            rule.runOnIdle { focusReference.requestFocus() }
+        }
+
+        // Assert.
+        assertThat(isSoftKeyboardShown).isTrue()
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun keyboardHiddenWhenFocusIsLost() {
+        // Arrange.
+        lateinit var focusManager: FocusManager
+        lateinit var view: View
+        val focusReference = FocusReference()
+        rule.setContent {
+            view = AmbientView.current
+            focusManager = AmbientFocusManager.current
+            CoreTextField(
+                value = TextFieldValue("Hello"),
+                onValueChange = {},
+                modifier = Modifier.focusReference(focusReference)
+            )
+        }
+        view.ensureKeyboardIsHidden()
+        // Request focus and wait for keyboard.
+        view.runAndWaitUntil({ view.isSoftwareKeyboardShown() }) {
+            rule.runOnIdle { focusReference.requestFocus() }
+        }
+
+        // Act.
+        val isSoftKeyboardHidden = view.runAndWaitUntil({ !view.isSoftwareKeyboardShown() }) {
+            rule.runOnIdle { focusManager.clearFocus() }
+        }
+
+        // Assert.
+        assertThat(isSoftKeyboardHidden).isTrue()
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun keyboardShownAfterDismissingKeyboardAndClickingAgain() {
+        // Arrange.
+        lateinit var view: View
+        rule.setContent {
+            view = AmbientView.current
+            CoreTextField(
+                value = TextFieldValue("Hello"),
+                onValueChange = {},
+                modifier = Modifier.testTag("TextField1")
+            )
+        }
+        view.ensureKeyboardIsHidden()
+        view.runAndWaitUntil({ view.isSoftwareKeyboardShown() }) {
+            rule.onNodeWithTag("TextField1").performClick()
+        }
+        view.runAndWaitUntil({ !view.isSoftwareKeyboardShown() }) {
+            rule.runOnIdle { view.hideKeyboard() }
+        }
+
+        // Act.
+        val isSoftKeyboardVisible = view.runAndWaitUntil({ view.isSoftwareKeyboardShown() }) {
+            rule.onNodeWithTag("TextField1").performClick()
+        }
+
+        // Assert.
+        assertThat(isSoftKeyboardVisible).isTrue()
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+    @Test
+    fun keyboardStaysVisibleWhenMovingFromOneTextFieldToAnother() {
+        // Arrange.
+        val focusReference1 = FocusReference()
+        val focusReference2 = FocusReference()
+        lateinit var view: View
+        rule.setContent {
+            view = AmbientView.current
+            Column {
+                CoreTextField(
+                    value = TextFieldValue("Hello"),
+                    onValueChange = {},
+                    modifier = Modifier.focusReference(focusReference1)
+                )
+                CoreTextField(
+                    value = TextFieldValue("Hello"),
+                    onValueChange = {},
+                    modifier = Modifier.focusReference(focusReference2)
+                )
+            }
+        }
+        view.ensureKeyboardIsHidden()
+        view.runAndWaitUntil({ view.isSoftwareKeyboardShown() }) {
+            rule.runOnIdle { focusReference1.requestFocus() }
+        }
+
+        // Act.
+        val wasKeyboardHidden = view.runAndWaitUntil({ !view.isSoftwareKeyboardShown() }) {
+            rule.runOnIdle { focusReference2.requestFocus() }
+        }
+
+        // Assert.
+        assertThat(wasKeyboardHidden).isFalse()
+    }
+
+    @RequiresApi(Build.VERSION_CODES.R)
+    private fun View.runAndWaitUntil(condition: () -> Boolean, block: () -> Unit): Boolean {
+        val latch = CountDownLatch(1)
+        rule.runOnIdle {
+            rootView.setWindowInsetsAnimationCallback(
+                InsetAnimationCallback {
+                    if (condition()) { latch.countDown() }
+                }
+            )
+        }
+        rule.waitForIdle()
+        block()
+        rule.waitForIdle()
+        return latch.await(15L, TimeUnit.SECONDS)
+    }
+
+    // We experienced some flakiness in tests if the keyboard was visible at the start of the test.
+    // This function makes sure the keyboard is hidden at the start of every test.
+    @RequiresApi(Build.VERSION_CODES.R)
+    private fun View.ensureKeyboardIsHidden() {
+        rule.waitForIdle()
+        if (isSoftwareKeyboardShown()) {
+            runAndWaitUntil({ !isSoftwareKeyboardShown() }) {
+                rule.runOnIdle { hideKeyboard() }
+            }
+        }
+        rule.waitForIdle()
+    }
+
+    @RequiresApi(Build.VERSION_CODES.R)
+    private class InsetAnimationCallback(val block: () -> Unit) :
+        WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
+
+        override fun onProgress(
+            insets: WindowInsets,
+            runningAnimations: MutableList<WindowInsetsAnimation>
+        ) = insets
+
+        override fun onEnd(animation: WindowInsetsAnimation) {
+            block()
+            super.onEnd(animation)
+        }
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.R)
+private fun View.isSoftwareKeyboardShown(): Boolean {
+    checkNotNull(rootWindowInsets)
+    return rootWindowInsets.isVisible(WindowInsets.Type.ime())
+}
+
+@RequiresApi(Build.VERSION_CODES.R)
+private fun View.hideKeyboard() {
+    windowInsetsController?.hide(WindowInsets.Type.ime())
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextFieldInteractionsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextFieldInteractionsTest.kt
index 8050c7c..9f37ca5 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextFieldInteractionsTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextFieldInteractionsTest.kt
@@ -23,9 +23,8 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.cancel
@@ -49,7 +48,7 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(InternalTextApi::class, ExperimentalFocus::class)
+@OptIn(InternalTextApi::class)
 class TextFieldInteractionsTest {
 
     @get:Rule
@@ -111,7 +110,7 @@
     fun coreTextField_interaction_focused() {
         val state = mutableStateOf(TextFieldValue(""))
         val interactionState = InteractionState()
-        val otherRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setContent {
             BasicTextField(
                 modifier = Modifier.testTag(testTag),
@@ -120,7 +119,7 @@
                 interactionState = interactionState
             )
             Box(
-                modifier = Modifier.size(10.dp).focusRequester(otherRequester).focusable(),
+                modifier = Modifier.size(10.dp).focusReference(focusReference).focusable(),
             )
         }
         assertThat(interactionState.value).doesNotContain(Interaction.Focused)
@@ -129,7 +128,7 @@
         assertThat(interactionState.value).contains(Interaction.Focused)
         rule.runOnIdle {
             // request focus on the box so TextField will lose it
-            otherRequester.requestFocus()
+            focusReference.requestFocus()
         }
         assertThat(interactionState.value).doesNotContain(Interaction.Focused)
     }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextLayoutDirectionTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextLayoutDirectionTest.kt
new file mode 100644
index 0000000..e7196f5
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextLayoutDirectionTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text
+
+import androidx.compose.runtime.Providers
+import androidx.compose.ui.platform.AmbientLayoutDirection
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.text.InternalTextApi
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+class TextLayoutDirectionTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun testCoreTextField_getsCorrectLayoutDirection() {
+        var layoutDirection: LayoutDirection? = null
+
+        rule.setContent {
+            Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
+                @OptIn(InternalTextApi::class)
+                CoreTextField(
+                    value = TextFieldValue("..."),
+                    onValueChange = {},
+                    onTextLayout = { result ->
+                        layoutDirection = result.layoutInput.layoutDirection
+                    }
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(layoutDirection).isNotNull()
+            assertThat(layoutDirection!!).isEqualTo(LayoutDirection.Rtl)
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt
index 036f2c4..c338a45 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt
@@ -25,6 +25,7 @@
 import androidx.compose.ui.selection.Selection
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextDelegate
@@ -60,7 +61,10 @@
     style = FontStyle.Normal
 )
 
-@OptIn(InternalTextApi::class)
+@OptIn(
+    InternalTextApi::class,
+    ExperimentalTextApi::class
+)
 @RunWith(AndroidJUnit4::class)
 @MediumTest
 class MultiWidgetSelectionDelegateTest {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionHandleTestUtils.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionHandleTestUtils.kt
index 1570a3a..119d458 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionHandleTestUtils.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionHandleTestUtils.kt
@@ -17,7 +17,7 @@
 package androidx.compose.foundation.text.selection
 
 import android.view.View
-import androidx.compose.ui.node.Owner
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.test.junit4.ComposeTestRule
 import androidx.compose.ui.window.isPopupLayout
 import androidx.test.espresso.Espresso
@@ -34,7 +34,7 @@
 ) {
     // Make sure that current measurement/drawing is finished
     runOnIdle { }
-    Espresso.onView(CoreMatchers.instanceOf(Owner::class.java))
+    Espresso.onView(CoreMatchers.instanceOf(ViewRootForTest::class.java))
         .inRoot(DoubleSelectionHandleMatcher(index))
         .check(ViewAssertions.matches(viewMatcher))
 }
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingCalculator.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingCalculator.kt
index bb7cbcf..6bb3c64 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingCalculator.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingCalculator.kt
@@ -123,7 +123,7 @@
         fun velocity(time: Long): Float {
             val splinePos = if (duration > 0) time / duration.toFloat() else 1f
             return AndroidFlingSpline.flingPosition(splinePos).velocityCoefficient *
-                distance / duration * 1000.0f
+                sign(initialVelocity) * distance / duration * 1000.0f
         }
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BaseTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BaseTextField.kt
index b87870cf..fb9396c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BaseTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BaseTextField.kt
@@ -23,7 +23,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.useOrElse
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.text.SoftwareKeyboardController
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextStyle
@@ -119,7 +119,7 @@
     onTextInputStarted: (SoftwareKeyboardController) -> Unit = {},
     cursorColor: Color = AmbientContentColor.current
 ) {
-    val color = textColor.useOrElse { textStyle.color.useOrElse { AmbientContentColor.current } }
+    val color = textColor.takeOrElse { textStyle.color.takeOrElse { AmbientContentColor.current } }
     val mergedStyle = textStyle.merge(TextStyle(color = color))
 
     BasicTextField(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
index 75f7632..ef2bfeb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
@@ -65,13 +65,12 @@
 ) = composed(
     factory = {
         val semanticModifier = Modifier.semantics(mergeDescendants = true) {
-            if (enabled) {
-                // b/156468846:  add long click semantics and double click if needed
-                onClick(action = { onClick(); true }, label = onClickLabel)
-                if (onLongClick != null) {
-                    onLongClick(action = { onLongClick(); true }, label = onLongClickLabel)
-                }
-            } else {
+            // b/156468846:  add long click semantics and double click if needed
+            onClick(action = { onClick(); true }, label = onClickLabel)
+            if (onLongClick != null) {
+                onLongClick(action = { onLongClick(); true }, label = onLongClickLabel)
+            }
+            if (!enabled) {
                 disabled()
             }
         }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
index 627923d..da616af 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
@@ -24,10 +24,9 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
-import androidx.compose.ui.focus
-import androidx.compose.ui.focus.ExperimentalFocus
+import androidx.compose.ui.focus.focusModifier
 import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focusObserver
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.semantics.focused
 import androidx.compose.ui.semantics.semantics
@@ -43,7 +42,6 @@
  * @param interactionState [InteractionState] that will be updated to contain [Interaction.Focused]
  * when this focusable is focused
  */
-@OptIn(ExperimentalFocus::class)
 fun Modifier.focusable(
     enabled: Boolean = true,
     interactionState: InteractionState? = null,
@@ -69,7 +67,7 @@
             .semantics {
                 this.focused = isFocused
             }
-            .focusObserver {
+            .onFocusChanged {
                 isFocused = it.isFocused
                 if (isFocused) {
                     interactionState?.addInteraction(Interaction.Focused)
@@ -77,8 +75,8 @@
                     interactionState?.removeInteraction(Interaction.Focused)
                 }
             }
-            .focus()
+            .focusModifier()
     } else {
         Modifier
     }
-}
\ No newline at end of file
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt
index b5b0c00..409e25d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt
@@ -19,8 +19,8 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.semantics.AccessibilityRangeInfo
-import androidx.compose.ui.semantics.accessibilityValue
-import androidx.compose.ui.semantics.accessibilityValueRange
+import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.semantics.stateDescriptionRange
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.util.annotation.FloatRange
 import androidx.compose.ui.util.format
@@ -52,8 +52,8 @@
     }
 
     return semantics {
-        accessibilityValue = Strings.TemplatePercent.format(percent)
-        accessibilityValueRange = AccessibilityRangeInfo(progress, 0f..1f)
+        stateDescription = Strings.TemplatePercent.format(percent)
+        stateDescriptionRange = AccessibilityRangeInfo(progress, 0f..1f)
     }
 }
 
@@ -69,5 +69,5 @@
  */
 @Stable
 fun Modifier.progressSemantics(): Modifier {
-    return semantics { accessibilityValue = Strings.InProgress }
+    return semantics { stateDescription = Strings.InProgress }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
index c30a7c6..a40ca51 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
@@ -25,6 +25,7 @@
 import androidx.compose.foundation.animation.FlingConfig
 import androidx.compose.foundation.animation.defaultFlingConfig
 import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.foundation.gestures.Scrollable
 import androidx.compose.foundation.gestures.ScrollableController
 import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.layout.Arrangement
@@ -122,7 +123,7 @@
     internal val flingConfig: FlingConfig,
     animationClock: AnimationClockObservable,
     interactionState: InteractionState? = null
-) {
+) : Scrollable {
 
     /**
      * current scroll position value in pixels
@@ -170,8 +171,7 @@
      *
      * If [scroll] is called from elsewhere, this will be canceled.
      */
-    @OptIn(ExperimentalFoundationApi::class)
-    suspend fun scroll(
+    override suspend fun scroll(
         block: suspend ScrollScope.() -> Unit
     ): Unit = scrollableController.scroll(block)
 
@@ -478,7 +478,7 @@
     if (isVertical) {
         check(maxHeight != Constraints.Infinity) {
             "Nesting scrollable in the same direction layouts like ScrollableContainer and " +
-                "LazyColumnFor is not allowed. If you want to add a header before the list of" +
+                "LazyColumn is not allowed. If you want to add a header before the list of" +
                 " items please take a look on LazyColumn component which has a DSL api which" +
                 " allows to first add a header via item() function and then the list of " +
                 "items via items()."
@@ -486,7 +486,7 @@
     } else {
         check(maxWidth != Constraints.Infinity) {
             "Nesting scrollable in the same direction layouts like ScrollableRow and " +
-                "LazyRowFor is not allowed. If you want to add a header before the list of " +
+                "LazyRow is not allowed. If you want to add a header before the list of " +
                 "items please take a look on LazyRow component which has a DSL api which " +
                 "allows to first add a fixed element via item() function and then the " +
                 "list of items via items()."
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Strings.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Strings.kt
index 65f7b7d..27c432b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Strings.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Strings.kt
@@ -27,4 +27,5 @@
     val NotSelected = "Not selected"
     val InProgress = "In progress"
     val TemplatePercent = "%d percent"
+    val Toggle = "Toggle"
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Text.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Text.kt
index 0051606..0f13853 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Text.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Text.kt
@@ -28,7 +28,7 @@
 import androidx.compose.runtime.structuralEqualityPolicy
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.useOrElse
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.Paragraph
 import androidx.compose.ui.text.TextLayoutResult
@@ -217,7 +217,7 @@
     onTextLayout: (TextLayoutResult) -> Unit = {},
     style: TextStyle = AmbientTextStyle.current
 ) {
-    val textColor = color.useOrElse { style.color.useOrElse { AmbientContentColor.current } }
+    val textColor = color.takeOrElse { style.color.takeOrElse { AmbientContentColor.current } }
     val mergedStyle = style.merge(
         TextStyle(
             color = textColor,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/SmoothScroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/SmoothScroll.kt
new file mode 100644
index 0000000..feb7832
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/SmoothScroll.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.animation
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.gestures.Scrollable
+import androidx.compose.runtime.dispatch.withFrameMillis
+
+/**
+ * Smooth scroll by [value] pixels.
+ *
+ * Cancels the currently running scroll, if any, and suspends until the cancellation is
+ * complete.
+ *
+ * @param value delta to scroll by
+ * @param spec [AnimationSpec] to be used for this smooth scrolling
+ *
+ * @return the amount of scroll consumed
+ */
+suspend fun Scrollable.smoothScrollBy(
+    value: Float,
+    spec: AnimationSpec<Float> = spring()
+): Float {
+    val animSpec = spec.vectorize(Float.VectorConverter)
+    val conv = Float.VectorConverter
+    val zeroVector = conv.convertToVector(0f)
+    val targetVector = conv.convertToVector(value)
+    var previousValue = 0f
+
+    scroll {
+        val startTimeMillis = withFrameMillis { it }
+        do {
+            val finished = withFrameMillis { frameTimeMillis ->
+                val newValue = conv.convertFromVector(
+                    animSpec.getValue(
+                        playTime = frameTimeMillis - startTimeMillis,
+                        start = zeroVector,
+                        end = targetVector,
+                        // TODO: figure out if/how we should incorporate existing velocity
+                        startVelocity = zeroVector
+                    )
+                )
+                val delta = newValue - previousValue
+                val consumed = scrollBy(delta)
+
+                if (consumed != delta) {
+                    previousValue += consumed
+                    true
+                } else {
+                    previousValue = newValue
+                    previousValue == value
+                }
+            }
+        } while (!finished)
+    }
+    return previousValue
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt
index 4f6652d..a131f9b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt
@@ -17,8 +17,7 @@
 package androidx.compose.foundation.gestures
 
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.gesture.ExperimentalPointerInput
-import androidx.compose.ui.input.pointer.HandlePointerInputScope
+import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerId
@@ -26,7 +25,6 @@
 import androidx.compose.ui.input.pointer.PointerInputScope
 import androidx.compose.ui.input.pointer.anyPositionChangeConsumed
 import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
-import androidx.compose.ui.input.pointer.consumePositionChange
 import androidx.compose.ui.input.pointer.positionChange
 import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed
 import androidx.compose.ui.input.pointer.positionChangedIgnoreConsumed
@@ -56,8 +54,7 @@
  * @see awaitHorizontalTouchSlopOrCancellation
  * @see awaitVerticalTouchSlopOrCancellation
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.awaitTouchSlopOrCancellation(
+suspend fun AwaitPointerEventScope.awaitTouchSlopOrCancellation(
     pointerId: PointerId,
     onTouchSlopReached: (change: PointerInputChange, overSlop: Offset) -> Unit
 ): PointerInputChange? {
@@ -124,8 +121,7 @@
  * @see horizontalDrag
  * @see verticalDrag
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.drag(
+suspend fun AwaitPointerEventScope.drag(
     pointerId: PointerId,
     onDrag: (PointerInputChange) -> Unit
 ): Boolean {
@@ -159,8 +155,7 @@
  * @see awaitHorizontalDragOrCancellation
  * @see drag
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.awaitDragOrCancellation(
+suspend fun AwaitPointerEventScope.awaitDragOrCancellation(
     pointerId: PointerId,
 ): PointerInputChange? {
     if (currentEvent.isPointerUp(pointerId)) {
@@ -184,14 +179,13 @@
  * @see detectVerticalDragGestures
  * @see detectHorizontalDragGestures
  */
-@ExperimentalPointerInput
 suspend fun PointerInputScope.detectDragGestures(
     onDragEnd: () -> Unit = { },
     onDragCancel: () -> Unit = { },
     onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit
 ) {
     forEachGesture {
-        handlePointerInput {
+        awaitPointerEventScope {
             val down = awaitFirstDown()
             var drag: PointerInputChange?
             do {
@@ -235,18 +229,13 @@
  * @see awaitHorizontalTouchSlopOrCancellation
  * @see awaitTouchSlopOrCancellation
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.awaitVerticalTouchSlopOrCancellation(
+suspend fun AwaitPointerEventScope.awaitVerticalTouchSlopOrCancellation(
     pointerId: PointerId,
     onTouchSlopReached: (change: PointerInputChange, overSlop: Float) -> Unit
 ) = awaitTouchSlopOrCancellation(
     pointerId = pointerId,
     onTouchSlopReached = onTouchSlopReached,
-    getDragDirectionValue = { it.y },
-    consumeMotion = { change, consumed ->
-        change.consumePositionChange(0f, consumed)
-    },
-    getCrossDirectionValue = { it.x }
+    getDragDirectionValue = { it.y }
 )
 
 /**
@@ -265,8 +254,7 @@
  * @see horizontalDrag
  * @see drag
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.verticalDrag(
+suspend fun AwaitPointerEventScope.verticalDrag(
     pointerId: PointerId,
     onDrag: (PointerInputChange) -> Unit
 ): Boolean = drag(
@@ -293,8 +281,7 @@
  * @see awaitDragOrCancellation
  * @see verticalDrag
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.awaitVerticalDragOrCancellation(
+suspend fun AwaitPointerEventScope.awaitVerticalDragOrCancellation(
     pointerId: PointerId,
 ): PointerInputChange? {
     if (currentEvent.isPointerUp(pointerId)) {
@@ -322,14 +309,13 @@
  * @see detectDragGestures
  * @see detectHorizontalDragGestures
  */
-@ExperimentalPointerInput
 suspend fun PointerInputScope.detectVerticalDragGestures(
     onDragEnd: () -> Unit = { },
     onDragCancel: () -> Unit = { },
     onVerticalDrag: (change: PointerInputChange, dragAmount: Float) -> Unit
 ) {
     forEachGesture {
-        handlePointerInput {
+        awaitPointerEventScope {
             val down = awaitFirstDown()
             val drag = awaitVerticalTouchSlopOrCancellation(down.id, onVerticalDrag)
             if (drag != null) {
@@ -369,18 +355,13 @@
  * @see awaitVerticalTouchSlopOrCancellation
  * @see awaitTouchSlopOrCancellation
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.awaitHorizontalTouchSlopOrCancellation(
+suspend fun AwaitPointerEventScope.awaitHorizontalTouchSlopOrCancellation(
     pointerId: PointerId,
     onTouchSlopReached: (change: PointerInputChange, overSlop: Float) -> Unit
 ) = awaitTouchSlopOrCancellation(
     pointerId = pointerId,
     onTouchSlopReached = onTouchSlopReached,
-    getDragDirectionValue = { it.x },
-    consumeMotion = { change, consumed ->
-        change.consumePositionChange(consumed, 0f)
-    },
-    getCrossDirectionValue = { it.y }
+    getDragDirectionValue = { it.x }
 )
 
 /**
@@ -396,8 +377,7 @@
  * @see verticalDrag
  * @see drag
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.horizontalDrag(
+suspend fun AwaitPointerEventScope.horizontalDrag(
     pointerId: PointerId,
     onDrag: (PointerInputChange) -> Unit
 ): Boolean = drag(
@@ -424,8 +404,7 @@
  * @see awaitVerticalDragOrCancellation
  * @see awaitDragOrCancellation
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.awaitHorizontalDragOrCancellation(
+suspend fun AwaitPointerEventScope.awaitHorizontalDragOrCancellation(
     pointerId: PointerId,
 ): PointerInputChange? {
     if (currentEvent.isPointerUp(pointerId)) {
@@ -453,14 +432,13 @@
  * @see detectVerticalDragGestures
  * @see detectDragGestures
  */
-@ExperimentalPointerInput
 suspend fun PointerInputScope.detectHorizontalDragGestures(
     onDragEnd: () -> Unit = { },
     onDragCancel: () -> Unit = { },
     onHorizontalDrag: (change: PointerInputChange, dragAmount: Float) -> Unit
 ) {
     forEachGesture {
-        handlePointerInput {
+        awaitPointerEventScope {
             val down = awaitFirstDown()
             val drag = awaitHorizontalTouchSlopOrCancellation(down.id, onHorizontalDrag)
             if (drag != null) {
@@ -488,8 +466,7 @@
  * @return `true` when the gesture ended with all pointers up and `false` when the gesture
  * was canceled.
  */
-@ExperimentalPointerInput
-private suspend inline fun HandlePointerInputScope.drag(
+private suspend inline fun AwaitPointerEventScope.drag(
     pointerId: PointerId,
     onDrag: (PointerInputChange) -> Unit,
     motionFromChange: (PointerInputChange) -> Float,
@@ -522,8 +499,7 @@
  * returned. When a drag is detected, that [PointerInputChange] is returned. A drag is
  * only detected when [hasDragged] returns `true`.
  */
-@ExperimentalPointerInput
-private suspend inline fun HandlePointerInputScope.awaitDragOrUp(
+private suspend inline fun AwaitPointerEventScope.awaitDragOrUp(
     pointerId: PointerId,
     hasDragged: (PointerInputChange) -> Boolean
 ): PointerInputChange {
@@ -554,27 +530,21 @@
  *
  * When touch slop is detected, [onTouchSlopReached] is called with the change and the distance
  * beyond the touch slop. [getDragDirectionValue] should return the position change in the direction
- * of the drag and [getCrossDirectionValue] should return the position change along the
- * perpendicular axis. If [onTouchSlopReached] does not consume the position change, touch slop
+ * of the drag axis. If [onTouchSlopReached] does not consume the position change, touch slop
  * will not have been considered detected and the detection will continue or, if it is consumed,
  * the [PointerInputChange] that was consumed will be returned.
  *
  * This works with [awaitTouchSlopOrCancellation] for the other axis to ensure that only horizontal
  * or vertical dragging is done, but not both.
  *
- * [consumeMotion] should consume the position change along the drag axis.
- *
  * @return The [PointerInputChange] of the event that was consumed in [onTouchSlopReached] or
  * `null` if all pointers are raised or the position change was consumed by another gesture
  * detector.
  */
-@ExperimentalPointerInput
-private suspend inline fun HandlePointerInputScope.awaitTouchSlopOrCancellation(
+private suspend inline fun AwaitPointerEventScope.awaitTouchSlopOrCancellation(
     pointerId: PointerId,
     onTouchSlopReached: (PointerInputChange, Float) -> Unit,
-    getDragDirectionValue: (Offset) -> Float,
-    consumeMotion: (PointerInputChange, Float) -> Unit,
-    getCrossDirectionValue: (Offset) -> Float
+    getDragDirectionValue: (Offset) -> Float
 ): PointerInputChange? {
     if (currentEvent.isPointerUp(pointerId)) {
         return null // The pointer has already been lifted, so the gesture is canceled
@@ -582,7 +552,6 @@
     val touchSlop = viewConfiguration.touchSlop
     var pointer: PointerId = pointerId
     var totalPositionChange = 0f
-    var totalCrossPositionChange = 0f
 
     while (true) {
         val event = awaitPointerEvent()
@@ -603,11 +572,8 @@
             val positionChange = getDragDirectionValue(currentPosition) -
                 getDragDirectionValue(previousPosition)
             totalPositionChange += positionChange
-            totalCrossPositionChange +=
-                getCrossDirectionValue(currentPosition) - getCrossDirectionValue(previousPosition)
 
             val inDirection = abs(totalPositionChange)
-            val crossDirection = abs(totalCrossPositionChange)
             if (inDirection < touchSlop) {
                 // verify that nothing else consumed the drag event
                 awaitPointerEvent(PointerEventPass.Final)
@@ -615,22 +581,6 @@
                     return null
                 }
             } else {
-                if (crossDirection > inDirection) {
-                    // Consume the position change in the direction that we care about
-                    consumeMotion(dragEvent, positionChange)
-
-                    // give the other direction a chance to consume
-                    awaitPointerEvent(PointerEventPass.Final)
-
-                    // Unconsume the position change
-                    consumeMotion(dragEvent, -positionChange)
-
-                    if (dragEvent.anyPositionChangeConsumed()) {
-                        return null
-                    } else {
-                        totalCrossPositionChange = 0f
-                    }
-                }
                 onTouchSlopReached(
                     dragEvent,
                     totalPositionChange - (sign(totalPositionChange) * touchSlop)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ForEachGesture.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ForEachGesture.kt
index d3cd6e1..1af4d78 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ForEachGesture.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ForEachGesture.kt
@@ -15,8 +15,7 @@
  */
 package androidx.compose.foundation.gestures
 
-import androidx.compose.ui.gesture.ExperimentalPointerInput
-import androidx.compose.ui.input.pointer.HandlePointerInputScope
+import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerInputScope
 import androidx.compose.ui.util.fastAny
@@ -37,7 +36,6 @@
  * exits if [isActive] is `false`.
  */
 @OptIn(InternalCoroutinesApi::class, ExperimentalStdlibApi::class)
-@ExperimentalPointerInput
 suspend fun PointerInputScope.forEachGesture(block: suspend PointerInputScope.() -> Unit) {
     while (isActive) {
         try {
@@ -59,23 +57,20 @@
  * Returns `true` if the current state of the pointer events has all pointers up and `false`
  * if any of the pointers are down.
  */
-@ExperimentalPointerInput
-internal fun HandlePointerInputScope.allPointersUp(): Boolean =
+internal fun AwaitPointerEventScope.allPointersUp(): Boolean =
     !currentEvent.changes.fastAny { it.current.down }
 
 /**
  * Waits for all pointers to be up before returning.
  */
-@ExperimentalPointerInput
 internal suspend fun PointerInputScope.awaitAllPointersUp() {
-    handlePointerInput { awaitAllPointersUp() }
+    awaitPointerEventScope { awaitAllPointersUp() }
 }
 
 /**
  * Waits for all pointers to be up before returning.
  */
-@ExperimentalPointerInput
-internal suspend fun HandlePointerInputScope.awaitAllPointersUp() {
+internal suspend fun AwaitPointerEventScope.awaitAllPointersUp() {
     if (!allPointersUp()) {
         do {
             val events = awaitPointerEvent(PointerEventPass.Final)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/MultitouchGestureDetector.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/MultitouchGestureDetector.kt
index e7188c3..45b94d4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/MultitouchGestureDetector.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/MultitouchGestureDetector.kt
@@ -17,7 +17,6 @@
 package androidx.compose.foundation.gestures
 
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerInputChange
 import androidx.compose.ui.input.pointer.PointerInputScope
@@ -48,13 +47,12 @@
  * Example Usage:
  * @sample androidx.compose.foundation.samples.DetectMultitouchGestures
  */
-@ExperimentalPointerInput
 suspend fun PointerInputScope.detectMultitouchGestures(
     panZoomLock: Boolean = false,
     onGesture: (centroid: Offset, pan: Offset, zoom: Float, rotation: Float) -> Unit
 ) {
     forEachGesture {
-        handlePointerInput {
+        awaitPointerEventScope {
             var rotation = 0f
             var zoom = 1f
             var pan = Offset.Zero
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index 830e5bc..481ecd2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -24,9 +24,6 @@
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.SpringSpec
-import androidx.compose.animation.core.VectorConverter
-import androidx.compose.animation.core.spring
-import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Interaction
 import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.animation.FlingConfig
@@ -34,18 +31,25 @@
 import androidx.compose.foundation.animation.fling
 import androidx.compose.runtime.AtomicReference
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.dispatch.withFrameMillis
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.onDispose
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.gesture.Direction
 import androidx.compose.ui.gesture.ScrollCallback
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollDispatcher
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollSource
+import androidx.compose.ui.gesture.nestedscroll.nestedScroll
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.minus
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.sync.Mutex
@@ -75,18 +79,6 @@
 }
 
 /**
- * Scope used for suspending scroll blocks
- */
-interface ScrollScope {
-    /**
-     * Attempts to scroll forward by [pixels] px.
-     *
-     * @return the amount of the requested scroll that was consumed (that is, how far it scrolled)
-     */
-    fun scrollBy(pixels: Float): Float
-}
-
-/**
  * Controller to control the [scrollable] modifier with. Contains necessary information about the
  * ongoing fling and provides smooth scrolling capabilities.
  *
@@ -104,7 +96,7 @@
     internal val flingConfig: FlingConfig,
     animationClock: AnimationClockObservable,
     internal val interactionState: InteractionState? = null
-) {
+) : Scrollable {
     /**
      * Smooth scroll by [value] amount of pixels
      *
@@ -121,57 +113,6 @@
         animatedFloat.animateTo(to, anim = spec, onEnd = onEnd)
     }
 
-    /**
-     * Smooth scroll by [value] pixels.
-     *
-     * Cancels the currently running scroll, if any, and suspends until the cancellation is
-     * complete.
-     *
-     * @param value delta to scroll by
-     * @param spec [AnimationSpec] to be used for this smooth scrolling
-     *
-     * @return the amount of scroll consumed
-     */
-    @OptIn(ExperimentalFoundationApi::class)
-    suspend fun smoothScrollBy(
-        value: Float,
-        spec: AnimationSpec<Float> = spring()
-    ): Float {
-        val animSpec = spec.vectorize(Float.VectorConverter)
-        val conv = Float.VectorConverter
-        val zeroVector = conv.convertToVector(0f)
-        val targetVector = conv.convertToVector(value)
-        var previousValue = 0f
-
-        scroll {
-            val startTimeMillis = withFrameMillis { it }
-            do {
-                val finished = withFrameMillis { frameTimeMillis ->
-                    val newValue = conv.convertFromVector(
-                        animSpec.getValue(
-                            playTime = frameTimeMillis - startTimeMillis,
-                            start = zeroVector,
-                            end = targetVector,
-                            // TODO: figure out if/how we should incorporate existing velocity
-                            startVelocity = zeroVector
-                        )
-                    )
-                    val delta = newValue - previousValue
-                    val consumed = scrollBy(delta)
-
-                    if (consumed != delta) {
-                        previousValue += consumed
-                        true
-                    } else {
-                        previousValue = newValue
-                        previousValue == value
-                    }
-                }
-            } while (!finished)
-        }
-        return previousValue
-    }
-
     private val scrollControlJob = AtomicReference<Job?>(null)
     private val scrollControlMutex = Mutex()
 
@@ -190,7 +131,7 @@
      *
      * If [scroll] is called from elsewhere, this will be canceled.
      */
-    suspend fun scroll(
+    override suspend fun scroll(
         block: suspend ScrollScope.() -> Unit
     ): Unit = coroutineScope {
         stopFlingAnimation()
@@ -258,24 +199,85 @@
     }
 
     private val animatedFloat =
-        DeltaAnimatedFloat(0f, clocksProxy, consumeScrollDelta)
+        DeltaAnimatedFloat(0f, clocksProxy) {
+            dispatchScroll(it.reverseIfNeeded(), NestedScrollSource.Fling)
+        }
 
-    /**
-     * current position for scrollable
-     */
-    internal var value: Float
-        get() = animatedFloat.value
-        set(value) = animatedFloat.snapTo(value)
+    private var orientation by mutableStateOf(Orientation.Vertical)
+    private var reverseDirection by mutableStateOf(false)
 
-    internal fun fling(velocity: Float, onScrollEnd: (Float) -> Unit) {
+    // this is not good, should be gone when we have sync (suspend) animation and scroll
+    internal fun update(orientation: Orientation, reverseDirection: Boolean) {
+        this.orientation = orientation
+        this.reverseDirection = reverseDirection
+    }
+
+    internal val nestedScrollDispatcher = NestedScrollDispatcher()
+
+    internal val nestedScrollConnection = object : NestedScrollConnection {
+        override fun onPostScroll(
+            consumed: Offset,
+            available: Offset,
+            source: NestedScrollSource
+        ): Offset = performDeltaConsumption(available)
+
+        override fun onPostFling(
+            consumed: Velocity,
+            available: Velocity,
+            onFinished: (Velocity) -> Unit
+        ) {
+            performFlingInternal(available.pixelsPerSecond) { leftAfterUs ->
+                onFinished.invoke(available - Velocity(leftAfterUs))
+            }
+        }
+    }
+
+    internal fun dispatchScroll(scrollDelta: Float, source: NestedScrollSource) {
+        val scrollOffset = scrollDelta.toOffset()
+        val preConsumedByParent = nestedScrollDispatcher.dispatchPreScroll(scrollOffset, source)
+
+        val scrollAvailable = scrollOffset - preConsumedByParent
+        val consumed = performDeltaConsumption(scrollAvailable)
+        val leftForParent = scrollAvailable - consumed
+        nestedScrollDispatcher.dispatchPostScroll(consumed, leftForParent, source)
+    }
+
+    private fun performDeltaConsumption(delta: Offset): Offset {
+        // reverse once for users if needed and then back to the original axis system
+        return consumeScrollDelta(delta.toFloat().reverseIfNeeded()).reverseIfNeeded().toOffset()
+    }
+
+    internal fun dispatchFling(velocity: Float, onScrollEnd: (Float) -> Unit) {
+        val consumedByParent =
+            nestedScrollDispatcher.dispatchPreFling(Velocity(velocity.toOffset()))
+        val available = velocity.toOffset() - consumedByParent.pixelsPerSecond
+        performFlingInternal(available) { velocityLeft ->
+            // when notifying users code -- reverse if needed to obey their setting
+            onScrollEnd(velocityLeft.toFloat().reverseIfNeeded())
+            nestedScrollDispatcher.dispatchPostFling(
+                Velocity(available - velocityLeft),
+                Velocity(velocityLeft)
+            )
+        }
+    }
+
+    private fun performFlingInternal(velocity: Offset, onScrollEnd: (Offset) -> Unit) {
         animatedFloat.fling(
             config = flingConfig,
-            startVelocity = velocity,
+            startVelocity = velocity.toFloat().reverseIfNeeded(),
             onAnimationEnd = { _, _, velocityLeft ->
-                onScrollEnd(velocityLeft)
+                onScrollEnd(velocityLeft.reverseIfNeeded().toOffset())
             }
         )
     }
+
+    private fun Float.toOffset(): Offset =
+        if (orientation == Orientation.Horizontal) Offset(this, 0f) else Offset(0f, this)
+
+    private fun Offset.toFloat(): Float =
+        if (orientation == Orientation.Horizontal) this.x else this.y
+
+    private fun Float.reverseIfNeeded(): Float = if (reverseDirection) this * -1 else this
 }
 
 /**
@@ -315,6 +317,7 @@
     onScrollStopped: (velocity: Float) -> Unit = {}
 ): Modifier = composed(
     factory = {
+        controller.update(orientation, reverseDirection)
         onDispose {
             controller.stopAnimation()
             controller.interactionState?.removeInteraction(Interaction.Dragged)
@@ -333,10 +336,9 @@
             override fun onScroll(scrollDistance: Float): Float {
                 if (!enabled) return 0f
                 controller.stopFlingAnimation()
-                val toConsume = if (reverseDirection) scrollDistance * -1 else scrollDistance
-                val consumed = controller.consumeScrollDelta(toConsume)
-                controller.value = controller.value + consumed
-                return if (reverseDirection) consumed * -1 else consumed
+                controller.dispatchScroll(scrollDistance, NestedScrollSource.Drag)
+                // consume everything since we handle nested scrolling separately
+                return scrollDistance
             }
 
             override fun onCancel() {
@@ -349,8 +351,8 @@
             override fun onStop(velocity: Float) {
                 controller.interactionState?.removeInteraction(Interaction.Dragged)
                 if (enabled) {
-                    controller.fling(
-                        velocity = if (reverseDirection) velocity * -1 else velocity,
+                    controller.dispatchFling(
+                        velocity = velocity,
                         onScrollEnd = onScrollStopped
                     )
                 }
@@ -365,7 +367,7 @@
         ).mouseScrollable(
             scrollCallback,
             orientation
-        )
+        ).nestedScroll(controller.nestedScrollConnection, controller.nestedScrollDispatcher)
     },
     inspectorInfo = debugInspectorInfo {
         name = "scrollable"
@@ -398,7 +400,7 @@
 private class DeltaAnimatedFloat(
     initial: Float,
     clock: AnimationClockObservable,
-    private val onDelta: (Float) -> Float
+    private val onDelta: (Float) -> Unit
 ) : AnimatedFloat(clock, Spring.DefaultDisplacementThreshold) {
 
     override var value = initial
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ScrollableInterface.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ScrollableInterface.kt
new file mode 100644
index 0000000..aaa5e85
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ScrollableInterface.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.gestures
+
+import androidx.compose.foundation.ScrollableColumn
+// ktlint doesn't detect this being used in a doc comment
+import androidx.compose.foundation.animation.smoothScrollBy // ktlint-disable
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+
+/**
+ * Scope used for suspending scroll blocks
+ */
+interface ScrollScope {
+    /**
+     * Attempts to scroll forward by [pixels] px.
+     *
+     * @return the amount of the requested scroll that was consumed (that is, how far it scrolled)
+     */
+    fun scrollBy(pixels: Float): Float
+}
+
+/**
+ * A an object representing something that can be scrolled. This interface is implemented by states
+ * of scrollable containers such as [ScrollableColumn] and [LazyColumn] in order to provide
+ * low-level scrolling control via [scroll], as well as allowing for higher-level scrolling
+ * functions like  [Scrollable.smoothScrollBy] to be implemented as extension functions on
+ * [Scrollable].
+ *
+ * Subclasses may also have their own methods that are specific to their interaction paradigm, such
+ * as [LazyListState.snapToItemIndex].
+ *
+ * @see ScrollableController
+ * @see Scrollable.smoothScrollBy
+ */
+interface Scrollable {
+    /**
+     * Call this function to take control of scrolling and gain the ability to send scroll events
+     * via [ScrollScope.scrollBy]. All actions that change the logical scroll position must be
+     * performed within a [scroll] block (even if they don't call any other methods on this
+     * object) in order to guarantee that mutual exclusion is enforced.
+     *
+     * Cancels the currently running scroll, if any, and suspends until the cancellation is
+     * complete.
+     *
+     * If [scroll] is called from elsewhere, this will be canceled.
+     */
+    suspend fun scroll(
+        block: suspend ScrollScope.() -> Unit
+    )
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt
index d9f141b..3f97af9 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt
@@ -17,8 +17,7 @@
 package androidx.compose.foundation.gestures
 
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.gesture.ExperimentalPointerInput
-import androidx.compose.ui.input.pointer.HandlePointerInputScope
+import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerInputChange
@@ -86,7 +85,6 @@
  * the gestures are considered canceled. [onDoubleTap], [onLongPress], and [onTap] will not be
  * called after a gesture has been canceled.
  */
-@ExperimentalPointerInput
 suspend fun PointerInputScope.detectTapGestures(
     onDoubleTap: (() -> Unit)? = null,
     onLongPress: (() -> Unit)? = null,
@@ -97,7 +95,7 @@
     forEachGesture {
         coroutineScope {
             pressScope.reset()
-            val down = handlePointerInput {
+            val down = awaitPointerEventScope {
                 awaitFirstDown().also {
                     it.consumeDownChange()
                 }
@@ -117,7 +115,7 @@
             try {
                 // wait for first tap up or long press
                 up = withTimeout(longPressTimeout.inMilliseconds()) {
-                    handlePointerInput {
+                    awaitPointerEventScope {
                         waitForUpOrCancellation()?.also { it.consumeDownChange() }
                     }
                 }
@@ -153,7 +151,7 @@
                         try {
                             // Might have a long second press as the second tap
                             withTimeout(longPressTimeout.inMilliseconds()) {
-                                handlePointerInput {
+                                awaitPointerEventScope {
                                     val secondUp = waitForUpOrCancellation()
                                     if (secondUp == null) {
                                         pressScope.cancel()
@@ -186,8 +184,7 @@
  * Reads events until the first down is received. If [requireUnconsumed] is `true` and the first
  * down is consumed in the [PointerEventPass.Main] pass, that gesture is ignored.
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.awaitFirstDown(
+suspend fun AwaitPointerEventScope.awaitFirstDown(
     requireUnconsumed: Boolean = true
 ): PointerInputChange {
     var event: PointerEvent
@@ -208,8 +205,7 @@
  * pass. If the gesture was not canceled, the final up change is returned or `null` if the
  * event was canceled.
  */
-@ExperimentalPointerInput
-suspend fun HandlePointerInputScope.waitForUpOrCancellation(): PointerInputChange? {
+suspend fun AwaitPointerEventScope.waitForUpOrCancellation(): PointerInputChange? {
     while (true) {
         val event = awaitPointerEvent(PointerEventPass.Main)
         if (event.changes.fastAll { it.changedToUp() }) {
@@ -233,9 +229,8 @@
 /**
  * Consumes all event changes in the [PointerEventPass.Initial] until all pointers are up.
  */
-@ExperimentalPointerInput
 private suspend fun PointerInputScope.consumeAllEventsUntilUp() {
-    handlePointerInput {
+    awaitPointerEventScope {
         if (!allPointersUp()) {
             do {
                 val event = awaitPointerEvent(PointerEventPass.Initial)
@@ -251,12 +246,11 @@
  * not detected within [ViewConfiguration.doubleTapTimeout] of [upTime], `null` is returned.
  * Otherwise, the down event is returned.
  */
-@ExperimentalPointerInput
 private suspend fun PointerInputScope.detectSecondTapDown(
     upTime: Uptime
 ): PointerInputChange? {
     return withTimeoutOrNull(viewConfiguration.doubleTapTimeout.inMilliseconds()) {
-        handlePointerInput {
+        awaitPointerEventScope {
             val minUptime = upTime + viewConfiguration.doubleTapMinTime
             var change: PointerInputChange
             // The second tap doesn't count if it happens before DoubleTapMinTime of the first tap
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/IntervalList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/IntervalList.kt
new file mode 100644
index 0000000..1a61ac0
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/IntervalList.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+internal class IntervalHolder<T>(
+    val startIndex: Int,
+    val size: Int,
+    val content: T
+)
+
+internal class IntervalList<T> {
+    private val intervals = mutableListOf<IntervalHolder<T>>()
+    internal var totalSize = 0
+        private set
+
+    fun add(size: Int, content: T) {
+        if (size == 0) {
+            return
+        }
+
+        val interval = IntervalHolder(
+            startIndex = totalSize,
+            size = size,
+            content = content
+        )
+        totalSize += size
+        intervals.add(interval)
+    }
+
+    fun intervalForIndex(index: Int) =
+        if (index < 0 || index >= totalSize) {
+            throw IndexOutOfBoundsException("Index $index, size $totalSize")
+        } else {
+            intervals[findIndexOfHighestValueLesserThan(intervals, index)]
+        }
+
+    /**
+     * Finds the index of the [list] which contains the highest value of [IntervalHolder.startIndex]
+     * that is less than or equal to the given [value].
+     */
+    private fun findIndexOfHighestValueLesserThan(list: List<IntervalHolder<T>>, value: Int): Int {
+        var left = 0
+        var right = list.lastIndex
+
+        while (left < right) {
+            val middle = (left + right) / 2
+
+            val middleValue = list[middle].startIndex
+            if (middleValue == value) {
+                return middle
+            }
+
+            if (middleValue < value) {
+                left = middle + 1
+
+                // Verify that the left will not be bigger than our value
+                if (value < list[left].startIndex) {
+                    return middle
+                }
+            } else {
+                right = middle - 1
+            }
+        }
+
+        return left
+    }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
index e3ebd07..bc5160d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
@@ -16,6 +16,9 @@
 
 package androidx.compose.foundation.lazy
 
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.InternalLayoutApi
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
@@ -54,21 +57,27 @@
         items: List<T>,
         itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
     )
+
+    /**
+     * Adds a sticky header item, which will remain pinned even when scrolling after it.
+     * The header will remain pinned until the next header will take its place.
+     *
+     * @sample androidx.compose.foundation.samples.StickyHeaderSample
+     *
+     * @param content the content of the header
+     */
+    @ExperimentalFoundationApi
+    fun stickyHeader(content: @Composable LazyItemScope.() -> Unit)
 }
 
-internal class IntervalHolder(
-    val startIndex: Int,
-    val content: LazyItemScope.(Int) -> (@Composable () -> Unit)
-)
-
 internal class LazyListScopeImpl : LazyListScope {
-    private val intervals = mutableListOf<IntervalHolder>()
-    var totalSize = 0
+    private val intervals = IntervalList<LazyItemScope.(Int) -> (@Composable () -> Unit)>()
+    val totalSize get() = intervals.totalSize
+    var headersIndexes: MutableList<Int>? = null
+        private set
 
     fun contentFor(index: Int, scope: LazyItemScope): @Composable () -> Unit {
-        val intervalIndex = findIndexOfHighestValueLesserThan(intervals, index)
-
-        val interval = intervals[intervalIndex]
+        val interval = intervals.intervalForIndex(index)
         val localIntervalIndex = index - interval.startIndex
 
         return interval.content(scope, localIntervalIndex)
@@ -78,84 +87,34 @@
         items: List<T>,
         itemContent: @Composable LazyItemScope.(item: T) -> Unit
     ) {
-        // There aren't any items to display
-        if (items.isEmpty()) { return }
-
-        val interval = IntervalHolder(
-            startIndex = totalSize,
-            content = { index ->
-                val item = items[index]
-
-                { itemContent(item) }
-            }
-        )
-
-        totalSize += items.size
-
-        intervals.add(interval)
+        intervals.add(items.size) { index ->
+            val item = items[index]
+            @Composable { itemContent(item) }
+        }
     }
 
     override fun item(content: @Composable LazyItemScope.() -> Unit) {
-        val interval = IntervalHolder(
-            startIndex = totalSize,
-            content = { { content() } }
-        )
-
-        totalSize += 1
-
-        intervals.add(interval)
+        intervals.add(1) { @Composable { content() } }
     }
 
     override fun <T> itemsIndexed(
         items: List<T>,
         itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
     ) {
-        // There aren't any items to display
-        if (items.isEmpty()) { return }
-
-        val interval = IntervalHolder(
-            startIndex = totalSize,
-            content = { index ->
-                val item = items[index]
-
-                { itemContent(index, item) }
-            }
-        )
-
-        totalSize += items.size
-
-        intervals.add(interval)
+        intervals.add(items.size) { index ->
+            val item = items[index]
+            @Composable { itemContent(index, item) }
+        }
     }
 
-    /**
-     * Finds the index of the [list] which contains the highest value of [IntervalHolder.startIndex]
-     * that is less than or equal to the given [value].
-     */
-    private fun findIndexOfHighestValueLesserThan(list: List<IntervalHolder>, value: Int): Int {
-        var left = 0
-        var right = list.lastIndex
-
-        while (left < right) {
-            val middle = (left + right) / 2
-
-            val middleValue = list[middle].startIndex
-            if (middleValue == value) {
-                return middle
-            }
-
-            if (middleValue < value) {
-                left = middle + 1
-
-                // Verify that the left will not be bigger than our value
-                if (value < list[left].startIndex) {
-                    return middle
-                }
-            } else {
-                right = middle - 1
-            }
+    @ExperimentalFoundationApi
+    override fun stickyHeader(content: @Composable LazyItemScope.() -> Unit) {
+        val headersIndexes = headersIndexes ?: mutableListOf<Int>().also {
+            headersIndexes = it
         }
+        headersIndexes.add(totalSize)
 
-        return left
+        item(content)
     }
 }
 
@@ -170,17 +129,28 @@
  * @param modifier the modifier to apply to this layout
  * @param state the state object to be used to control or observe the list's state
  * @param contentPadding a padding around the whole content. This will add padding for the
- * content after it has been clipped, which is not possible via [modifier] param. Note that it is
- * **not** a padding applied for each item's content
+ * content after it has been clipped, which is not possible via [modifier] param. You can use it
+ * to add a padding before the first item or after the last one. If you want to add a spacing
+ * between each item use [horizontalArrangement].
+ * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be
+ * composed from the end to the start and [LazyListState.firstVisibleItemIndex] == 0 will mean
+ * the first item is located at the end.
+ * @param horizontalArrangement The horizontal arrangement of the layout's children. This allows
+ * to add a spacing between items and specify the arrangement of the items when we have not enough
+ * of them to fill the whole minimum size.
  * @param verticalAlignment the vertical alignment applied to the items
  * @param content a block which describes the content. Inside this block you can use methods like
  * [LazyListScope.item] to add a single item or [LazyListScope.items] to add a list of items.
  */
+@OptIn(InternalLayoutApi::class)
 @Composable
 fun LazyRow(
     modifier: Modifier = Modifier,
     state: LazyListState = rememberLazyListState(),
     contentPadding: PaddingValues = PaddingValues(0.dp),
+    reverseLayout: Boolean = false,
+    horizontalArrangement: Arrangement.Horizontal =
+        if (!reverseLayout) Arrangement.Start else Arrangement.End,
     verticalAlignment: Alignment.Vertical = Alignment.Top,
     content: LazyListScope.() -> Unit
 ) {
@@ -193,7 +163,10 @@
         state = state,
         contentPadding = contentPadding,
         verticalAlignment = verticalAlignment,
-        isVertical = false
+        horizontalArrangement = horizontalArrangement,
+        isVertical = false,
+        reverseLayout = reverseLayout,
+        headerIndexes = scope.headersIndexes ?: emptyList()
     ) { index ->
         scope.contentFor(index, this)
     }
@@ -207,20 +180,31 @@
  *
  * @sample androidx.compose.foundation.samples.LazyColumnSample
  *
- * @param modifier the modifier to apply to this layout
- * @param state the state object to be used to control or observe the list's state
- * @param contentPadding a padding around the whole content. This will add padding for the
- * content after it has been clipped, which is not possible via [modifier] param. Note that it is
- * **not** a padding applied for each item's content
- * @param horizontalAlignment the horizontal alignment applied to the items
+ * @param modifier the modifier to apply to this layout.
+ * @param state the state object to be used to control or observe the list's state.
+ * @param contentPadding a padding around the whole content. This will add padding for the.
+ * content after it has been clipped, which is not possible via [modifier] param. You can use it
+ * to add a padding before the first item or after the last one. If you want to add a spacing
+ * between each item use [verticalArrangement].
+ * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be
+ * composed from the bottom to the top and [LazyListState.firstVisibleItemIndex] == 0 will mean
+ * we scrolled to the bottom.
+ * @param verticalArrangement The vertical arrangement of the layout's children. This allows
+ * to add a spacing between items and specify the arrangement of the items when we have not enough
+ * of them to fill the whole minimum size.
+ * @param horizontalAlignment the horizontal alignment applied to the items.
  * @param content a block which describes the content. Inside this block you can use methods like
  * [LazyListScope.item] to add a single item or [LazyListScope.items] to add a list of items.
  */
+@OptIn(InternalLayoutApi::class)
 @Composable
 fun LazyColumn(
     modifier: Modifier = Modifier,
     state: LazyListState = rememberLazyListState(),
     contentPadding: PaddingValues = PaddingValues(0.dp),
+    reverseLayout: Boolean = false,
+    verticalArrangement: Arrangement.Vertical =
+        if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
     horizontalAlignment: Alignment.Horizontal = Alignment.Start,
     content: LazyListScope.() -> Unit
 ) {
@@ -233,7 +217,10 @@
         state = state,
         contentPadding = contentPadding,
         horizontalAlignment = horizontalAlignment,
-        isVertical = true
+        verticalArrangement = verticalArrangement,
+        isVertical = true,
+        reverseLayout = reverseLayout,
+        headerIndexes = scope.headersIndexes ?: emptyList()
     ) { index ->
         scope.contentFor(index, this)
     }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyFor.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyFor.kt
index 2031273..89b9186 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyFor.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyFor.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.foundation.lazy
 
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.InternalLayoutApi
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.runtime.Composable
@@ -29,14 +31,19 @@
  * See [LazyColumnForIndexed] if you need to have both item and index params in [itemContent].
  * See [LazyRowFor] if you are looking for a horizontally scrolling version.
  *
- * @sample androidx.compose.foundation.samples.LazyColumnForSample
- *
  * @param items the backing list of data to display
  * @param modifier the modifier to apply to this layout
  * @param state the state object to be used to control or observe the list's state
- * @param contentPadding a padding around the whole content. This will add padding for the
- * content after it has been clipped, which is not possible via [modifier] param. Note that it is
- * **not** a padding applied for each item's content
+ * @param contentPadding a padding around the whole content. This will add padding for the.
+ * content after it has been clipped, which is not possible via [modifier] param. You can use it
+ * to add a padding before the first item or after the last one. If you want to add a spacing
+ * between each item use [verticalArrangement].
+ * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be
+ * composed from the bottom to the top and [LazyListState.firstVisibleItemIndex] == 0 will mean
+ * we scrolled to the bottom.
+ * @param verticalArrangement The vertical arrangement of the layout's children. This allows
+ * to add a spacing between items and specify the arrangement of the items when we have not enough
+ * of them to fill the whole minimum size.
  * @param horizontalAlignment the horizontal alignment applied to the items
  * @param itemContent emits the UI for an item from [items] list. May emit any number of components,
  * which will be stacked vertically. Note that [LazyColumnFor] can start scrolling incorrectly
@@ -44,12 +51,24 @@
  * content asynchronously please reserve some space for the item, for example using [Spacer].
  * Use [LazyColumnForIndexed] if you need to have both index and item params.
  */
+@OptIn(InternalLayoutApi::class)
 @Composable
+@Deprecated(
+    "Use LazyColumn instead",
+    ReplaceWith(
+        "LazyColumn(modifier, state, contentPadding, horizontalAlignment = " +
+            "horizontalAlignment) { \n items(items, itemContent) \n }",
+        "androidx.compose.foundation.lazy.LazyColumn"
+    )
+)
 fun <T> LazyColumnFor(
     items: List<T>,
     modifier: Modifier = Modifier,
     state: LazyListState = rememberLazyListState(),
     contentPadding: PaddingValues = PaddingValues(0.dp),
+    reverseLayout: Boolean = false,
+    verticalArrangement: Arrangement.Vertical =
+        if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
     horizontalAlignment: Alignment.Horizontal = Alignment.Start,
     itemContent: @Composable LazyItemScope.(T) -> Unit
 ) {
@@ -57,7 +76,9 @@
         modifier = modifier,
         state = state,
         contentPadding = contentPadding,
-        horizontalAlignment = horizontalAlignment
+        horizontalAlignment = horizontalAlignment,
+        verticalArrangement = verticalArrangement,
+        reverseLayout = reverseLayout
     ) {
         items(items, itemContent)
     }
@@ -71,14 +92,19 @@
  *
  * See [LazyRowForIndexed] if you are looking for a horizontally scrolling version.
  *
- * @sample androidx.compose.foundation.samples.LazyColumnForIndexedSample
- *
  * @param items the backing list of data to display
  * @param modifier the modifier to apply to this layout
  * @param state the state object to be used to control or observe the list's state
- * @param contentPadding a padding around the whole content. This will add padding for the
- * content after it has been clipped, which is not possible via [modifier] param. Note that it is
- * **not** a padding applied for each item's content
+ * @param contentPadding a padding around the whole content. This will add padding for the.
+ * content after it has been clipped, which is not possible via [modifier] param. You can use it
+ * to add a padding before the first item or after the last one. If you want to add a spacing
+ * between each item use [verticalArrangement].
+ * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be
+ * composed from the bottom to the top and [LazyListState.firstVisibleItemIndex] == 0 will mean
+ * we scrolled to the bottom.
+ * @param verticalArrangement The vertical arrangement of the layout's children. This allows
+ * to add a spacing between items and specify the arrangement of the items when we have not enough
+ * of them to fill the whole minimum size.
  * @param horizontalAlignment the horizontal alignment applied to the items
  * @param itemContent emits the UI for an item from [items] list. It has two params: first one is
  * an index in the [items] list, and the second one is the item at this index from [items] list.
@@ -87,12 +113,24 @@
  * recompose with the real content, so even if you load the content asynchronously please reserve
  * some space for the item, for example using [Spacer].
  */
+@OptIn(InternalLayoutApi::class)
 @Composable
+@Deprecated(
+    "Use LazyColumn instead",
+    ReplaceWith(
+        "LazyColumn(modifier, state, contentPadding, horizontalAlignment = " +
+            "horizontalAlignment) { \n itemsIndexed(items, itemContent) \n }",
+        "androidx.compose.foundation.lazy.LazyColumn"
+    )
+)
 fun <T> LazyColumnForIndexed(
     items: List<T>,
     modifier: Modifier = Modifier,
     state: LazyListState = rememberLazyListState(),
     contentPadding: PaddingValues = PaddingValues(0.dp),
+    reverseLayout: Boolean = false,
+    verticalArrangement: Arrangement.Vertical =
+        if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
     horizontalAlignment: Alignment.Horizontal = Alignment.Start,
     itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
 ) {
@@ -100,7 +138,9 @@
         modifier = modifier,
         state = state,
         contentPadding = contentPadding,
-        horizontalAlignment = horizontalAlignment
+        horizontalAlignment = horizontalAlignment,
+        verticalArrangement = verticalArrangement,
+        reverseLayout = reverseLayout
     ) {
         itemsIndexed(items, itemContent)
     }
@@ -112,27 +152,44 @@
  * See [LazyRowForIndexed] if you need to have both item and index params in [itemContent].
  * See [LazyColumnFor] if you are looking for a vertically scrolling version.
  *
- * @sample androidx.compose.foundation.samples.LazyRowForSample
- *
- * @param items the backing list of data to display
- * @param modifier the modifier to apply to this layout
- * @param state the state object to be used to control or observe the list's state
+ * @param items the backing list of data to display.
+ * @param modifier the modifier to apply to this layout.
+ * @param state the state object to be used to control or observe the list's state.
  * @param contentPadding a padding around the whole content. This will add padding for the
- * content after it has been clipped, which is not possible via [modifier] param. Note that it is
- * **not** a padding applied for each item's content
- * @param verticalAlignment the vertical alignment applied to the items
+ * content after it has been clipped, which is not possible via [modifier] param. You can use it
+ * to add a padding before the first item or after the last one. If you want to add a spacing
+ * between each item use [horizontalArrangement].
+ * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be
+ * composed from the end to the start and [LazyListState.firstVisibleItemIndex] == 0 will mean
+ * the first item is located at the end.
+ * @param horizontalArrangement The horizontal arrangement of the layout's children. This allows
+ * to add a spacing between items and specify the arrangement of the items when we have not enough
+ * of them to fill the whole minimum size.
+ * @param verticalAlignment the vertical alignment applied to the items.
  * @param itemContent emits the UI for an item from [items] list. May emit any number of components,
  * which will be stacked horizontally. Note that [LazyRowFor] can start scrolling incorrectly
  * if you emit nothing and then lazily recompose with the real content, so even if you load the
  * content asynchronously please reserve some space for the item, for example using [Spacer].
  * Use [LazyRowForIndexed] if you need to have both index and item params.
  */
+@OptIn(InternalLayoutApi::class)
 @Composable
+@Deprecated(
+    "Use LazyRow instead",
+    ReplaceWith(
+        "LazyRow(modifier, state, contentPadding, verticalAlignment = " +
+            "verticalAlignment) { \n items(items, itemContent) \n }",
+        "androidx.compose.foundation.lazy.LazyColumn"
+    )
+)
 fun <T> LazyRowFor(
     items: List<T>,
     modifier: Modifier = Modifier,
     state: LazyListState = rememberLazyListState(),
     contentPadding: PaddingValues = PaddingValues(0.dp),
+    reverseLayout: Boolean = false,
+    horizontalArrangement: Arrangement.Horizontal =
+        if (!reverseLayout) Arrangement.Start else Arrangement.End,
     verticalAlignment: Alignment.Vertical = Alignment.Top,
     itemContent: @Composable LazyItemScope.(T) -> Unit
 ) {
@@ -141,6 +198,8 @@
         state = state,
         contentPadding = contentPadding,
         verticalAlignment = verticalAlignment,
+        horizontalArrangement = horizontalArrangement,
+        reverseLayout = reverseLayout
     ) {
         items(items, itemContent)
     }
@@ -153,15 +212,20 @@
  *
  * See [LazyColumnForIndexed] if you are looking for a vertically scrolling version.
  *
- * @sample androidx.compose.foundation.samples.LazyRowForIndexedSample
- *
- * @param items the backing list of data to display
- * @param modifier the modifier to apply to this layout
- * @param state the state object to be used to control or observe the list's state
+ * @param items the backing list of data to display.
+ * @param modifier the modifier to apply to this layout.
+ * @param state the state object to be used to control or observe the list's state.
  * @param contentPadding a padding around the whole content. This will add padding for the
- * content after it has been clipped, which is not possible via [modifier] param. Note that it is
- * **not** a padding applied for each item's content
- * @param verticalAlignment the vertical alignment applied to the items
+ * content after it has been clipped, which is not possible via [modifier] param. You can use it
+ * to add a padding before the first item or after the last one. If you want to add a spacing
+ * between each item use [horizontalArrangement].
+ * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be
+ * composed from the end to the start and [LazyListState.firstVisibleItemIndex] == 0 will mean
+ * the first item is located at the end.
+ * @param horizontalArrangement The horizontal arrangement of the layout's children. This allows
+ * to add a spacing between items and specify the arrangement of the items when we have not enough
+ * of them to fill the whole minimum size.
+ * @param verticalAlignment the vertical alignment applied to the items.
  * @param itemContent emits the UI for an item from [items] list. It has two params: first one is
  * an index in the [items] list, and the second one is the item at this index from [items] list.
  * May emit any number of components, which will be stacked horizontally. Note that
@@ -169,12 +233,24 @@
  * recompose with the real content, so even if you load the content asynchronously please reserve
  * some space for the item, for example using [Spacer].
  */
+@OptIn(InternalLayoutApi::class)
 @Composable
+@Deprecated(
+    "Use LazyRow instead",
+    ReplaceWith(
+        "LazyRow(modifier, state, contentPadding, verticalAlignment = " +
+            "verticalAlignment) { \n itemsIndexed(items, itemContent) \n }",
+        "androidx.compose.foundation.lazy.LazyColumn"
+    )
+)
 fun <T> LazyRowForIndexed(
     items: List<T>,
     modifier: Modifier = Modifier,
     state: LazyListState = rememberLazyListState(),
     contentPadding: PaddingValues = PaddingValues(0.dp),
+    reverseLayout: Boolean = false,
+    horizontalArrangement: Arrangement.Horizontal =
+        if (!reverseLayout) Arrangement.Start else Arrangement.End,
     verticalAlignment: Alignment.Vertical = Alignment.Top,
     itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
 ) {
@@ -183,6 +259,8 @@
         state = state,
         contentPadding = contentPadding,
         verticalAlignment = verticalAlignment,
+        horizontalArrangement = horizontalArrangement,
+        reverseLayout = reverseLayout
     ) {
         itemsIndexed(items, itemContent)
     }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt
index a8f98a8..a4bc0b6 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt
@@ -16,52 +16,157 @@
 
 package androidx.compose.foundation.lazy
 
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.layout.WithConstraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.unit.dp
 
 /**
  * The DSL implementation of a lazy grid layout. It composes only visible rows of the grid.
- * This API is not stable, please consider using stable components like [LazyColumnFor] and [Row]
+ * This API is not stable, please consider using stable components like [LazyColumn] and [Row]
  * to achieve the same result.
  *
- * @param columns a fixed number of columns of the grid
+ * @param cells a class describing how cells form columns, see [GridCells] doc for more information
  * @param modifier the modifier to apply to this layout
  * @param contentPadding specify a padding around the whole content
  * @param content the [LazyListScope] which describes the content
  */
+@ExperimentalFoundationApi
 @Composable
-internal fun LazyGrid(
-    columns: Int,
+fun LazyVerticalGrid(
+    cells: GridCells,
     modifier: Modifier = Modifier,
     state: LazyListState = rememberLazyListState(),
     contentPadding: PaddingValues = PaddingValues(0.dp),
-    content: LazyListScope.() -> Unit
+    content: LazyGridScope.() -> Unit
 ) {
-    val scope = LazyListScopeImpl()
+    val scope = LazyGridScopeImpl()
     scope.apply(content)
 
-    val rows = (scope.totalSize + columns - 1) / columns
+    when (cells) {
+        is GridCells.Fixed ->
+            FixedLazyGrid(
+                nColumns = cells.count,
+                modifier = modifier,
+                state = state,
+                contentPadding = contentPadding,
+                scope = scope
+            )
+        is GridCells.Adaptive ->
+            WithConstraints(
+                modifier = modifier
+            ) {
+                val nColumns = maxOf((maxWidth / cells.minSize).toInt(), 1)
+                FixedLazyGrid(
+                    nColumns = nColumns,
+                    state = state,
+                    contentPadding = contentPadding,
+                    scope = scope
+                )
+            }
+    }
+}
+
+/**
+ * This class describes how cells form columns in vertical grids or rows in horizontal grids.
+ */
+sealed class GridCells {
+    /**
+     * Combines cells with fixed number rows or columns.
+     *
+     * For example, for the vertical [LazyVerticalGrid] Fixed(3) would mean that there are 3 columns 1/3
+     * of the parent wide.
+     */
+    class Fixed(val count: Int) : GridCells()
+
+    /**
+     * Combines cells with adaptive number of rows or columns. It will try to position as many rows
+     * or columns as possible on the condition that every cell has at least [minSize] space and
+     * all extra space distributed evenly.
+     *
+     * For example, for the vertical [LazyVerticalGrid] Adaptive(20.dp) would mean that there will be as
+     * many columns as possible and every column will be at least 20.dp and all the columns will
+     * have equal width. If the screen is 88.dp wide then there will be 4 columns 22.dp each.
+     */
+    class Adaptive(val minSize: Dp) : GridCells()
+}
+
+/**
+ * Receiver scope which is used by [LazyVerticalGrid].
+ */
+interface LazyGridScope {
+    /**
+     * Adds a list of items and their content to the scope.
+     *
+     * @param items the data list
+     * @param itemContent the content displayed by a single item
+     */
+    fun <T> items(
+        items: List<T>,
+        itemContent: @Composable LazyItemScope.(item: T) -> Unit
+    )
+
+    /**
+     * Adds a single item to the scope.
+     *
+     * @param content the content of the item
+     */
+    fun item(content: @Composable LazyItemScope.() -> Unit)
+
+    /**
+     * Adds a list of items to the scope where the content of an item is aware of its index.
+     *
+     * @param items the data list
+     * @param itemContent the content displayed by a single item
+     */
+    fun <T> itemsIndexed(
+        items: List<T>,
+        itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
+    )
+}
+
+@Composable
+private fun FixedLazyGrid(
+    nColumns: Int,
+    modifier: Modifier = Modifier,
+    state: LazyListState = rememberLazyListState(),
+    contentPadding: PaddingValues = PaddingValues(0.dp),
+    scope: LazyGridScopeImpl
+) {
+    val rows = (scope.totalSize + nColumns - 1) / nColumns
     LazyList(
         itemsCount = rows,
         modifier = modifier,
         state = state,
         contentPadding = contentPadding,
-        isVertical = true
+        isVertical = true,
+        horizontalAlignment = Alignment.Start,
+        verticalArrangement = Arrangement.Top,
+        reverseLayout = false
     ) { rowIndex ->
-        {
-            GridRow {
-                for (columnIndex in 0 until columns) {
-                    val itemIndex = rowIndex * columns + columnIndex
+        @Composable {
+            Row {
+                for (columnIndex in 0 until nColumns) {
+                    val itemIndex = rowIndex * nColumns + columnIndex
                     if (itemIndex < scope.totalSize) {
-                        scope.contentFor(itemIndex, this).invoke()
+                        GridCellBox(
+                            modifier = Modifier.weight(1f, fill = true)
+                        ) {
+                            scope.contentFor(itemIndex, this@LazyList).invoke()
+                        }
                     } else {
-                        Spacer(Modifier)
+                        Spacer(Modifier.weight(1f, fill = true))
                     }
                 }
             }
@@ -69,33 +174,57 @@
     }
 }
 
+/**
+ * TODO: Remove when the Box component supports fixed constraints.
+ */
 @Composable
-private fun GridRow(
-    content: @Composable () -> Unit
-) {
-    // TODO: Implement customisable column widths.
-    Layout(
-        content = content
-    ) { measurables, constraints ->
-
-        // TODO: Avoid int rounding to fill all the width pixels.
-        val itemConstraint = Constraints.fixedWidth(constraints.maxWidth / measurables.size)
-        var maxItemHeight = 0
-        val placeables = measurables.map { measurable ->
-            measurable.measure(itemConstraint)
-                .also {
-                    if (it.height > maxItemHeight) {
-                        maxItemHeight = it.height
-                    }
-                }
+private fun GridCellBox(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
+    Layout(content, modifier) { measurables, constraints ->
+        val placeables = measurables.map { it.measure(constraints) }
+        val size = placeables.fold(IntSize.Zero) { size, item ->
+            IntSize(maxOf(size.width, item.width), maxOf(size.height, item.height))
         }
-
-        layout(constraints.maxWidth, maxItemHeight) {
-            var currentXPosition = 0
-            placeables.forEach { placeable ->
-                placeable.placeRelative(x = currentXPosition, y = 0)
-                currentXPosition += placeable.width
+        layout(constraints.constrainWidth(size.width), constraints.constrainHeight(size.height)) {
+            placeables.forEach {
+                it.place(0, 0)
             }
         }
     }
 }
+
+internal class LazyGridScopeImpl : LazyGridScope {
+    private val intervals = IntervalList<LazyItemScope.(Int) -> (@Composable () -> Unit)>()
+
+    val totalSize get() = intervals.totalSize
+
+    fun contentFor(index: Int, scope: LazyItemScope): @Composable () -> Unit {
+        val interval = intervals.intervalForIndex(index)
+        val localIntervalIndex = index - interval.startIndex
+
+        return interval.content(scope, localIntervalIndex)
+    }
+
+    override fun <T> items(
+        items: List<T>,
+        itemContent: @Composable LazyItemScope.(item: T) -> Unit
+    ) {
+        intervals.add(items.size) { index ->
+            val item = items[index]
+            @Composable { itemContent(item) }
+        }
+    }
+
+    override fun item(content: @Composable LazyItemScope.() -> Unit) {
+        intervals.add(1) { @Composable { content() } }
+    }
+
+    override fun <T> itemsIndexed(
+        items: List<T>,
+        itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
+    ) {
+        intervals.add(items.size) { index ->
+            val item = items[index]
+            @Composable { itemContent(index, item) }
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
index 10aaf67..5f110cb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
@@ -18,10 +18,14 @@
 
 import androidx.compose.foundation.assertNotNestingScrollableContainers
 import androidx.compose.foundation.gestures.scrollable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.InternalLayoutApi
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.savedinstancestate.ExperimentalRestorableStateHolder
+import androidx.compose.runtime.savedinstancestate.rememberRestorableStateHolder
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clipToBounds
@@ -29,25 +33,43 @@
 import androidx.compose.ui.layout.SubcomposeLayout
 import androidx.compose.ui.platform.AmbientLayoutDirection
 import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.constrainHeight
-import androidx.compose.ui.unit.constrainWidth
-import androidx.compose.ui.util.fastForEach
 
+@OptIn(InternalLayoutApi::class)
 @Composable
 internal fun LazyList(
+    /** The total size of the list */
     itemsCount: Int,
-    modifier: Modifier = Modifier,
+    /** Modifier to be applied for the inner layout */
+    modifier: Modifier,
+    /** State controlling the scroll position */
     state: LazyListState,
+    /** The inner padding to be added for the whole content(nor for each individual item) */
     contentPadding: PaddingValues,
-    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
-    verticalAlignment: Alignment.Vertical = Alignment.Top,
+    /** reverse the direction of scrolling and layout */
+    reverseLayout: Boolean,
+    /** The layout orientation of the list */
     isVertical: Boolean,
-    itemContentFactory: LazyItemScope.(Int) -> @Composable () -> Unit
+    /** The alignment to align items horizontally. Required when isVertical is true */
+    horizontalAlignment: Alignment.Horizontal? = null,
+    /** The vertical arrangement for items. Required when isVertical is true */
+    verticalArrangement: Arrangement.Vertical? = null,
+    /** The alignment to align items vertically. Required when isVertical is false */
+    verticalAlignment: Alignment.Vertical? = null,
+    /** The horizontal arrangement for items. Required when isVertical is false */
+    horizontalArrangement: Arrangement.Horizontal? = null,
+    /** The list of indexes of the sticky header items */
+    headerIndexes: List<Int> = emptyList(),
+    /** The factory defining the content for an item on the given position in the list */
+    itemContent: LazyItemScope.(Int) -> @Composable () -> Unit
 ) {
-    val reverseDirection = AmbientLayoutDirection.current == LayoutDirection.Rtl && !isVertical
+    val isRtl = AmbientLayoutDirection.current == LayoutDirection.Rtl
+    // reverse scroll by default, to have "natural" gesture that goes reversed to layout
+    // if rtl and horizontal, do not reverse to make it right-to-left
+    val reverseScrollDirection = if (!isVertical && isRtl) reverseLayout else !reverseLayout
 
-    val cachingItemContentFactory = remember { CachingItemContentFactory(itemContentFactory) }
-    cachingItemContentFactory.itemContentFactory = itemContentFactory
+    val restorableItemContent = wrapWithStateRestoration(itemContent)
+    val cachingItemContentFactory = remember { CachingItemContentFactory(restorableItemContent) }
+    cachingItemContentFactory.itemContentFactory = restorableItemContent
 
     val startContentPadding = if (isVertical) contentPadding.top else contentPadding.start
     val endContentPadding = if (isVertical) contentPadding.bottom else contentPadding.end
@@ -55,8 +77,7 @@
         modifier
             .scrollable(
                 orientation = if (isVertical) Orientation.Vertical else Orientation.Horizontal,
-                // reverse scroll by default, to have "natural" gesture that goes reversed to layout
-                reverseDirection = !reverseDirection,
+                reverseDirection = reverseScrollDirection,
                 controller = state.scrollableController
             )
             .clipToBounds()
@@ -71,21 +92,32 @@
         val startContentPaddingPx = startContentPadding.toIntPx()
         val endContentPaddingPx = endContentPadding.toIntPx()
         val mainAxisMaxSize = (if (isVertical) constraints.maxHeight else constraints.maxWidth)
+        val spaceBetweenItemsDp = if (isVertical) {
+            requireNotNull(verticalArrangement).spacing
+        } else {
+            requireNotNull(horizontalArrangement).spacing
+        }
+        val spaceBetweenItems = spaceBetweenItemsDp.toIntPx()
 
         val itemProvider = LazyMeasuredItemProvider(
             constraints,
             isVertical,
             this,
             cachingItemContentFactory
-        ) { placeables ->
+        ) { index, placeables ->
+            // we add spaceBetweenItems as an extra spacing for all items apart from the last one so
+            // the lazy list measuring logic will take it into account.
+            val spacing = if (index.value == itemsCount - 1) 0 else spaceBetweenItems
             LazyMeasuredItem(
+                index = index.value,
                 placeables = placeables,
                 isVertical = isVertical,
                 horizontalAlignment = horizontalAlignment,
                 verticalAlignment = verticalAlignment,
                 layoutDirection = layoutDirection,
                 startContentPadding = startContentPaddingPx,
-                endContentPadding = endContentPaddingPx
+                endContentPadding = endContentPaddingPx,
+                spacing = spacing
             )
         }
 
@@ -102,18 +134,40 @@
 
         state.applyMeasureResult(measureResult)
 
-        val layoutWidth = constraints.constrainWidth(
-            if (isVertical) measureResult.crossAxisSize else measureResult.mainAxisSize
+        val headers = if (headerIndexes.isNotEmpty()) {
+            LazyListHeaders(itemProvider, headerIndexes, measureResult, startContentPaddingPx)
+        } else {
+            null
+        }
+
+        layoutLazyList(
+            constraints,
+            isVertical,
+            verticalArrangement,
+            horizontalArrangement,
+            measureResult,
+            reverseLayout,
+            headers
         )
-        val layoutHeight = constraints.constrainHeight(
-            if (isVertical) measureResult.mainAxisSize else measureResult.crossAxisSize
-        )
-        layout(layoutWidth, layoutHeight) {
-            var currentMainAxis = measureResult.itemsScrollOffset
-            measureResult.items.fastForEach {
-                it.place(this, layoutWidth, layoutHeight, currentMainAxis)
-                currentMainAxis += it.mainAxisSize
-            }
+    }
+}
+
+/**
+ * Converts item content factory to another one which adds auto state restoration functionality.
+ */
+@OptIn(ExperimentalRestorableStateHolder::class)
+@Composable
+internal fun wrapWithStateRestoration(
+    itemContentFactory: LazyItemScope.(Int) -> @Composable () -> Unit
+): LazyItemScope.(Int) -> @Composable () -> Unit {
+    val restorableStateHolder = rememberRestorableStateHolder<Any>()
+    return remember(itemContentFactory) {
+        { index ->
+            val content = itemContentFactory(index)
+            // we just wrap our original lambda with the one which auto restores the state
+            // currently we use index in the list as a key for the restoration, but in the future
+            // we will use the user provided key
+            (@Composable { restorableStateHolder.RestorableStateProvider(index, content) })
         }
     }
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt
new file mode 100644
index 0000000..2c4b295
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+import androidx.compose.ui.layout.Placeable
+
+/**
+ * @param itemProvider the provider so we can compose a header if it wasn't composed already
+ * @param headerIndexes list of indexes of headers. Must be sorted.
+ * @param measureResult the result of the measuring.
+ */
+internal class LazyListHeaders(
+    private val itemProvider: LazyMeasuredItemProvider,
+    headerIndexes: List<Int>,
+    measureResult: LazyListMeasureResult,
+    private val startContentPadding: Int
+) {
+    private val currentHeaderListPosition: Int
+    private val nextHeaderListPosition: Int
+
+    private val notUsedButComposedItems: MutableList<LazyMeasuredItem>?
+
+    private var currentHeaderItem: LazyMeasuredItem? = null
+    private var currentHeaderOffset: Int = Int.MIN_VALUE
+    private var nextHeaderOffset: Int = Int.MIN_VALUE
+    private var nextHeaderSize: Int = Int.MIN_VALUE
+
+    init {
+        var currentHeaderListPosition = -1
+        var nextHeaderListPosition = -1
+        // we use visibleItemsInfo and not firstVisibleItemIndex as visibleItemsInfo list also
+        // contains all the items which are visible in the start content padding area
+        val firstVisible = measureResult.visibleItemsInfo.first().index
+        // find the header which can be displayed
+        for (index in headerIndexes.indices) {
+            if (headerIndexes[index] <= firstVisible) {
+                currentHeaderListPosition = headerIndexes[index]
+                nextHeaderListPosition = headerIndexes.getOrElse(index + 1) { -1 }
+            } else {
+                break
+            }
+        }
+        this.currentHeaderListPosition = currentHeaderListPosition
+        this.nextHeaderListPosition = nextHeaderListPosition
+
+        notUsedButComposedItems = measureResult.notUsedButComposedItems
+    }
+
+    fun onBeforeItemsPlacing() {
+        currentHeaderItem = null
+        currentHeaderOffset = Int.MIN_VALUE
+        nextHeaderOffset = Int.MIN_VALUE
+    }
+
+    fun place(
+        item: LazyMeasuredItem,
+        scope: Placeable.PlacementScope,
+        layoutWidth: Int,
+        layoutHeight: Int,
+        offset: Int,
+        reverseOrder: Boolean
+    ) {
+        if (item.index == currentHeaderListPosition) {
+            currentHeaderItem = item
+            currentHeaderOffset = offset
+        } else {
+            item.place(scope, layoutWidth, layoutHeight, offset, reverseOrder)
+            if (item.index == nextHeaderListPosition) {
+                nextHeaderOffset = offset
+                nextHeaderSize = item.size
+            }
+        }
+    }
+
+    fun onAfterItemsPlacing(
+        scope: Placeable.PlacementScope,
+        mainAxisLayoutSize: Int,
+        layoutWidth: Int,
+        layoutHeight: Int,
+        reverseOrder: Boolean
+    ) {
+        if (currentHeaderListPosition == -1) {
+            // we have no headers needing special handling
+            return
+        }
+
+        val headerItem = currentHeaderItem
+            ?: notUsedButComposedItems?.find { it.index == currentHeaderListPosition }
+            ?: itemProvider.getAndMeasure(DataIndex(currentHeaderListPosition))
+
+        var headerOffset = if (!reverseOrder) {
+            if (currentHeaderOffset != Int.MIN_VALUE) {
+                maxOf(-startContentPadding, currentHeaderOffset)
+            } else {
+                -startContentPadding
+            }
+        } else {
+            if (currentHeaderOffset != Int.MIN_VALUE) {
+                minOf(
+                    mainAxisLayoutSize + startContentPadding - headerItem.size,
+                    currentHeaderOffset
+                )
+            } else {
+                mainAxisLayoutSize + startContentPadding - headerItem.size
+            }
+        }
+        // if we have a next header overlapping with the current header, the next one will be
+        // pushing the current one away from the viewport.
+        if (nextHeaderOffset != Int.MIN_VALUE) {
+            if (!reverseOrder) {
+                headerOffset = minOf(headerOffset, nextHeaderOffset - headerItem.size)
+            } else {
+                headerOffset = maxOf(headerOffset, nextHeaderOffset + headerItem.size)
+            }
+        }
+
+        headerItem.place(scope, layoutWidth, layoutHeight, headerOffset, reverseOrder)
+    }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemInfo.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemInfo.kt
new file mode 100644
index 0000000..568f577
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemInfo.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.compose.foundation.lazy
+
+/**
+ * Contains useful information about an individual item in lazy lists like [LazyColumn] or [LazyRow].
+ *
+ * @see LazyListLayoutInfo
+ */
+interface LazyListItemInfo {
+    /**
+     * The index of the item in the list.
+     */
+    val index: Int
+
+    /**
+     * The main axis offset of the item. It is relative to the start of the lazy list container.
+     */
+    val offset: Int
+
+    /**
+     * The main axis size of the item. Note that if you emit multiple layouts in the composable
+     * slot for the item then this size will be calculated as the sum of their sizes.
+     */
+    val size: Int
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfo.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfo.kt
new file mode 100644
index 0000000..40acb47
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfo.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+/**
+ * Contains useful information about the currently displayed layout state of lazy lists like
+ * [LazyColumn] or [LazyRow]. For example you can get the list of currently displayed item.
+ *
+ * Use [LazyListState.layoutInfo] to retrieve this
+ */
+interface LazyListLayoutInfo {
+    /**
+     * The list of [LazyListItemInfo] representing all the currently visible items.
+     */
+    val visibleItemsInfo: List<LazyListItemInfo>
+
+    /**
+     * The start offset of the layout's viewport. You can think of it as a minimum offset which
+     * would be visible. Usually it is 0, but it can be negative if a content padding was applied
+     * as the content displayed in the content padding area is still visible.
+     *
+     * You can use it to understand what items from [visibleItemsInfo] are fully visible.
+     */
+    val viewportStartOffset: Int
+
+    /**
+     * The end offset of the layout's viewport. You can think of it as a maximum offset which
+     * would be visible. Usually it is a size of the lazy list container plus a content padding.
+     *
+     * You can use it to understand what items from [visibleItemsInfo] are fully visible.
+     */
+    val viewportEndOffset: Int
+
+    /**
+     * The total count of items passed to [LazyColumn] or [LazyRow].
+     */
+    val totalItemsCount: Int
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
index 41c2fcf..984380a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
@@ -16,12 +16,21 @@
 
 package androidx.compose.foundation.lazy
 
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.InternalLayoutApi
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
+import androidx.compose.ui.util.fastForEach
 import kotlin.math.abs
 import kotlin.math.roundToInt
 import kotlin.math.sign
 
 /**
- * Measures and positions currently visible items using [LazyMeasuredItemProvider].
+ * Measures and calculates the positions for the currently visible items. The result is produced
+ * as a [LazyListMeasureResult] which contains all the calculations.
  */
 internal fun measureLazyList(
     itemsCount: Int,
@@ -45,7 +54,11 @@
             firstVisibleItemIndex = DataIndex(0),
             firstVisibleItemScrollOffset = 0,
             canScrollForward = false,
-            consumedScroll = 0f
+            consumedScroll = 0f,
+            notUsedButComposedItems = null,
+            viewportStartOffset = -startContentPadding,
+            viewportEndOffset = endContentPadding,
+            totalItemsCount = 0
         )
     } else {
         var currentFirstItemIndex = firstVisibleItemIndex
@@ -97,7 +110,7 @@
             val measuredItem = itemProvider.getAndMeasure(previous)
             visibleItems.add(0, measuredItem)
             maxCrossAxis = maxOf(maxCrossAxis, measuredItem.crossAxisSize)
-            currentFirstItemScrollOffset += measuredItem.mainAxisSize
+            currentFirstItemScrollOffset += measuredItem.sizeWithSpacings
             currentFirstItemIndex = previous
         }
         // if we were scrolled backward, but there were not enough items before. this means
@@ -120,12 +133,12 @@
         var mainAxisUsed = -goingForwardInitialScrollOffset
         while (mainAxisUsed <= maxMainAxis && index.value < itemsCount) {
             val measuredItem = itemProvider.getAndMeasure(index)
-            mainAxisUsed += measuredItem.mainAxisSize
+            mainAxisUsed += measuredItem.sizeWithSpacings
 
             if (mainAxisUsed < minOffset) {
                 // this item is offscreen and will not be placed. advance firstVisibleItemIndex
                 currentFirstItemIndex = index + 1
-                currentFirstItemScrollOffset -= measuredItem.mainAxisSize
+                currentFirstItemScrollOffset -= measuredItem.sizeWithSpacings
                 // but remember the corresponding placeables in case we will be forced to
                 // scroll back as there were not enough items to fill the viewport
                 if (notUsedButComposedItems == null) {
@@ -156,7 +169,7 @@
                 }
                 visibleItems.add(0, measuredItem)
                 maxCrossAxis = maxOf(maxCrossAxis, measuredItem.crossAxisSize)
-                currentFirstItemScrollOffset += measuredItem.mainAxisSize
+                currentFirstItemScrollOffset += measuredItem.sizeWithSpacings
                 currentFirstItemIndex = previous
             }
             scrollDelta += toScrollBack
@@ -189,7 +202,7 @@
             currentFirstItemScrollOffset += startContentPadding
             var startPaddingItems = 0
             while (startPaddingItems < visibleItems.lastIndex) {
-                val size = visibleItems[startPaddingItems].mainAxisSize
+                val size = visibleItems[startPaddingItems].sizeWithSpacings
                 if (size <= currentFirstItemScrollOffset) {
                     startPaddingItems++
                     currentFirstItemScrollOffset -= size
@@ -200,15 +213,87 @@
             }
         }
 
+        val mainAxisSize = mainAxisUsed + startContentPadding
+        val maximumVisibleOffset = minOf(mainAxisSize, mainAxisMaxSize) + endContentPadding
         return LazyListMeasureResult(
-            mainAxisSize = mainAxisUsed + startContentPadding,
+            mainAxisSize = mainAxisSize,
             crossAxisSize = maxCrossAxis,
             items = visibleItems,
             itemsScrollOffset = firstItemOffset,
             firstVisibleItemIndex = currentFirstItemIndex,
             firstVisibleItemScrollOffset = currentFirstItemScrollOffset,
             canScrollForward = mainAxisUsed > maxOffset,
-            consumedScroll = consumedScroll
+            consumedScroll = consumedScroll,
+            notUsedButComposedItems = notUsedButComposedItems,
+            viewportStartOffset = -startContentPadding,
+            viewportEndOffset = maximumVisibleOffset,
+            totalItemsCount = itemsCount
         )
     }
 }
+
+/**
+ * Lays out [LazyMeasuredItem]s based on the [LazyListMeasureResult] and the passed arrangement.
+ */
+@OptIn(InternalLayoutApi::class)
+internal fun MeasureScope.layoutLazyList(
+    constraints: Constraints,
+    isVertical: Boolean,
+    verticalArrangement: Arrangement.Vertical?,
+    horizontalArrangement: Arrangement.Horizontal?,
+    measureResult: LazyListMeasureResult,
+    reverseLayout: Boolean,
+    headers: LazyListHeaders?
+): MeasureResult {
+    val layoutWidth = constraints.constrainWidth(
+        if (isVertical) measureResult.crossAxisSize else measureResult.mainAxisSize
+    )
+    val layoutHeight = constraints.constrainHeight(
+        if (isVertical) measureResult.mainAxisSize else measureResult.crossAxisSize
+    )
+    val mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
+    val hasSpareSpace = measureResult.mainAxisSize < mainAxisLayoutSize
+    if (hasSpareSpace) {
+        check(measureResult.itemsScrollOffset == 0)
+    }
+    val density = this
+
+    return layout(layoutWidth, layoutHeight) {
+        var currentMainAxis = measureResult.itemsScrollOffset
+        if (hasSpareSpace) {
+            val items = if (reverseLayout) measureResult.items.reversed() else measureResult.items
+            val sizes = IntArray(items.size) { index ->
+                items[index].size
+            }
+            val positions = IntArray(items.size) { 0 }
+            if (isVertical) {
+                requireNotNull(verticalArrangement)
+                    .arrange(mainAxisLayoutSize, sizes, density, positions)
+            } else {
+                requireNotNull(horizontalArrangement)
+                    .arrange(mainAxisLayoutSize, sizes, layoutDirection, density, positions)
+            }
+            positions.forEachIndexed { index, position ->
+                items[index].place(this, layoutWidth, layoutHeight, position, reverseLayout)
+            }
+        } else {
+            headers?.onBeforeItemsPlacing()
+            measureResult.items.fastForEach {
+                val offset = if (reverseLayout) {
+                    mainAxisLayoutSize - currentMainAxis - (it.size)
+                } else {
+                    currentMainAxis
+                }
+                if (headers != null) {
+                    headers.place(it, this, layoutWidth, layoutHeight, offset, reverseLayout)
+                } else {
+                    it.place(this, layoutWidth, layoutHeight, offset, reverseLayout)
+                }
+                currentMainAxis += it.sizeWithSpacings
+            }
+            headers?.onAfterItemsPlacing(
+                this, mainAxisLayoutSize, layoutWidth, layoutHeight, reverseLayout
+            )
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasureResult.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasureResult.kt
index f3a3fcd..fb89915 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasureResult.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasureResult.kt
@@ -35,5 +35,12 @@
     /** True if there is some space available to continue scrolling in the forward direction.*/
     val canScrollForward: Boolean,
     /** The amount of scroll consumed during the measure pass.*/
-    val consumedScroll: Float
-)
+    val consumedScroll: Float,
+    /** The composed MeasuredItem which we are not going to place as they are out of screen.*/
+    val notUsedButComposedItems: MutableList<LazyMeasuredItem>?,
+    override val viewportStartOffset: Int,
+    override val viewportEndOffset: Int,
+    override val totalItemsCount: Int
+) : LazyListLayoutInfo {
+    override val visibleItemsInfo: List<LazyListItemInfo> get() = items
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
index 52250e1..a6d983b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
@@ -18,14 +18,13 @@
 
 import androidx.compose.animation.asDisposableClock
 import androidx.compose.animation.core.AnimationClockObservable
-import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.spring
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Interaction
 import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.animation.FlingConfig
 import androidx.compose.foundation.animation.defaultFlingConfig
 import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.foundation.gestures.Scrollable
 import androidx.compose.foundation.gestures.ScrollableController
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
@@ -99,7 +98,7 @@
     interactionState: InteractionState? = null,
     flingConfig: FlingConfig,
     animationClock: AnimationClockObservable
-) {
+) : Scrollable {
     /**
      * The holder class for the current scroll position.
      */
@@ -118,7 +117,7 @@
     val firstVisibleItemScrollOffset: Int get() = scrollPosition.observableScrollOffset
 
     /**
-     * whether this [LazyListState] is currently scrolling via [scroll] or via an
+     * Whether this [LazyListState] is currently scrolling via [scroll] or via an
      * animation/fling.
      *
      * Note: **all** scrolls initiated via [scroll] are considered to be animations, regardless of
@@ -127,6 +126,15 @@
     val isAnimationRunning
         get() = scrollableController.isAnimationRunning
 
+    /** Backing state for [layoutInfo] */
+    private val layoutInfoState = mutableStateOf<LazyListLayoutInfo>(EmptyLazyListLayoutInfo)
+
+    /**
+     * The object of [LazyListLayoutInfo] calculated during the last layout pass. For example,
+     * you can use it to calculate what items are currently visible.
+     */
+    val layoutInfo: LazyListLayoutInfo get() = layoutInfoState.value
+
     /**
      * The amount of scroll to be consumed in the next layout pass.  Scrolling forward is negative
      * - that is, it is the amount that the items are offset in y
@@ -216,26 +224,10 @@
      * If [scroll] is called from elsewhere, this will be canceled.
      */
     @OptIn(ExperimentalFoundationApi::class)
-    suspend fun scroll(
+    override suspend fun scroll(
         block: suspend ScrollScope.() -> Unit
     ): Unit = scrollableController.scroll(block)
 
-    /**
-     * Smooth scroll by [value] pixels.
-     *
-     * Cancels the currently running scroll, if any, and suspends until the cancellation is
-     * complete.
-     *
-     * @param value delta to scroll by
-     * @param spec [AnimationSpec] to be used for this smooth scrolling
-     *
-     * @return the amount of scroll consumed
-     */
-    suspend fun smoothScrollBy(
-        value: Float,
-        spec: AnimationSpec<Float> = spring()
-    ): Float = scrollableController.smoothScrollBy(value, spec)
-
     // TODO: Coroutine scrolling APIs will allow this to be private again once we have more
     //  fine-grained control over scrolling
     @VisibleForTesting
@@ -282,6 +274,7 @@
             canScrollForward = measureResult.canScrollForward
         )
         scrollToBeConsumed -= measureResult.consumedScroll
+        layoutInfoState.value = measureResult
         numMeasurePasses++
     }
 
@@ -353,3 +346,10 @@
         this.canScrollForward = canScrollForward
     }
 }
+
+private object EmptyLazyListLayoutInfo : LazyListLayoutInfo {
+    override val visibleItemsInfo = emptyList<LazyListItemInfo>()
+    override val viewportStartOffset = 0
+    override val viewportEndOffset = 0
+    override val totalItemsCount = 0
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItem.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItem.kt
index 21dbb69..16a5ac9 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItem.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItem.kt
@@ -26,24 +26,38 @@
  * if the user emit multiple layout nodes in the item callback.
  */
 internal class LazyMeasuredItem(
+    override val index: Int,
     private val placeables: List<Placeable>,
     private val isVertical: Boolean,
-    private val horizontalAlignment: Alignment.Horizontal,
-    private val verticalAlignment: Alignment.Vertical,
+    private val horizontalAlignment: Alignment.Horizontal?,
+    private val verticalAlignment: Alignment.Vertical?,
     private val layoutDirection: LayoutDirection,
     private val startContentPadding: Int,
-    private val endContentPadding: Int
-) {
+    private val endContentPadding: Int,
+    /**
+     * Extra spacing to be added to [size] aside from the sum of the [placeables] size. It
+     * is usually representing the spacing after the item.
+     */
+    private val spacing: Int
+) : LazyListItemInfo {
     /**
      * Sum of the main axis sizes of all the inner placeables.
      */
-    val mainAxisSize: Int
+    override val size: Int
+
+    /**
+     * Sum of the main axis sizes of all the inner placeables and [spacing].
+     */
+    val sizeWithSpacings: Int
 
     /**
      * Max of the cross axis sizes of all the inner placeables.
      */
     val crossAxisSize: Int
 
+    override var offset: Int = 0
+        private set
+
     init {
         var mainAxisSize = 0
         var maxCrossAxis = 0
@@ -51,8 +65,9 @@
             mainAxisSize += if (isVertical) it.height else it.width
             maxCrossAxis = maxOf(maxCrossAxis, if (!isVertical) it.height else it.width)
         }
-        this.mainAxisSize = mainAxisSize
-        this.crossAxisSize = maxCrossAxis
+        size = mainAxisSize
+        sizeWithSpacings = size + spacing
+        crossAxisSize = maxCrossAxis
     }
 
     /**
@@ -60,18 +75,23 @@
      * and [layoutHeight] should be provided to not place placeables which are ended up outside of
      * the viewport (for example one item consist of 2 placeables, and the first one is not going
      * to be visible, so we don't place it as an optimization, but place the second one).
+     * If [reverseOrder] is true the inner placeables would be placed in the inverted order.
      */
     fun place(
         scope: Placeable.PlacementScope,
         layoutWidth: Int,
         layoutHeight: Int,
-        offset: Int
+        offset: Int,
+        reverseOrder: Boolean
     ) = with(scope) {
+        this@LazyMeasuredItem.offset = offset
         var mainAxisOffset = offset
-        placeables.fastForEach {
+        val indices = if (reverseOrder) placeables.lastIndex downTo 0 else placeables.indices
+        for (index in indices) {
+            val it = placeables[index]
             if (isVertical) {
-                val x =
-                    horizontalAlignment.align(it.width, layoutWidth, layoutDirection)
+                val x = requireNotNull(horizontalAlignment)
+                    .align(it.width, layoutWidth, layoutDirection)
                 if (mainAxisOffset + it.height > -startContentPadding &&
                     mainAxisOffset < layoutHeight + endContentPadding
                 ) {
@@ -79,7 +99,7 @@
                 }
                 mainAxisOffset += it.height
             } else {
-                val y = verticalAlignment.align(it.height, layoutHeight)
+                val y = requireNotNull(verticalAlignment).align(it.height, layoutHeight)
                 if (mainAxisOffset + it.width > -startContentPadding &&
                     mainAxisOffset < layoutWidth + endContentPadding
                 ) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItemProvider.kt
index ff038ff..89ae7d7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItemProvider.kt
@@ -30,7 +30,7 @@
     isVertical: Boolean,
     private val scope: SubcomposeMeasureScope,
     private val itemContentFactory: (Int) -> @Composable () -> Unit,
-    private val measuredItemFactory: (List<Placeable>) -> LazyMeasuredItem
+    private val measuredItemFactory: (DataIndex, List<Placeable>) -> LazyMeasuredItem
 ) {
     // the constraints we will measure child with. the main axis is not restricted
     private val childConstraints = Constraints(
@@ -47,6 +47,6 @@
         val placeables = scope.subcompose(index, itemContentFactory(index.value)).fastMap {
             it.measure(childConstraints)
         }
-        return measuredItemFactory(placeables)
+        return measuredItemFactory(index, placeables)
     }
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Selectable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Selectable.kt
index 22fd657d..a6df531 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Selectable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Selectable.kt
@@ -27,7 +27,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.semantics.accessibilityValue
+import androidx.compose.ui.semantics.stateDescription
 import androidx.compose.ui.semantics.selected
 import androidx.compose.ui.semantics.semantics
 
@@ -71,7 +71,7 @@
             onClick = onClick
         ).semantics {
             this.selected = selected
-            this.accessibilityValue = if (selected) Strings.Selected else Strings.NotSelected
+            this.stateDescription = if (selected) Strings.Selected else Strings.NotSelected
         }
     },
     inspectorInfo = debugInspectorInfo {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt
index acde3bd..002276e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt
@@ -30,7 +30,7 @@
 import androidx.compose.ui.gesture.pressIndicatorGestureFilter
 import androidx.compose.ui.gesture.tapGestureFilter
 import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.semantics.accessibilityValue
+import androidx.compose.ui.semantics.stateDescription
 import androidx.compose.ui.semantics.disabled
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.semantics
@@ -137,7 +137,7 @@
 ): Modifier = composed {
     // TODO(pavlis): Handle multiple states for Semantics
     val semantics = Modifier.semantics(mergeDescendants = true) {
-        this.accessibilityValue = when (state) {
+        this.stateDescription = when (state) {
             // TODO(ryanmentley): These should be set by Checkbox, Switch, etc.
             On -> Strings.Checked
             Off -> Strings.Unchecked
@@ -145,9 +145,8 @@
         }
         this.toggleableState = state
 
-        if (enabled) {
-            onClick(action = { onClick(); return@onClick true }, label = "Toggle")
-        } else {
+        onClick(action = { onClick(); return@onClick true }, label = Strings.Toggle)
+        if (!enabled) {
             disabled()
         }
     }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt
index 4e86191..60e6843 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt
@@ -20,7 +20,6 @@
 import androidx.compose.ui.graphics.Outline
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.unit.Density
-import androidx.compose.ui.util.nativeClass
 import kotlin.math.min
 
 /**
@@ -90,28 +89,4 @@
      * @param all a size to apply for all four corners
      */
     fun copy(all: CornerSize): CornerBasedShape = copy(all, all, all, all)
-
-    // Implementations can't be data classes as we defined the abstract copy() method and the data
-    // class code generation is not compatible with it, so we provide our hashCode() and equals()
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (this.nativeClass() != other?.nativeClass()) return false
-
-        other as CornerBasedShape
-
-        if (topLeft != other.topLeft) return false
-        if (topRight != other.topRight) return false
-        if (bottomRight != other.bottomRight) return false
-        if (bottomLeft != other.bottomLeft) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = topLeft.hashCode()
-        result = 31 * result + topRight.hashCode()
-        result = 31 * result + bottomRight.hashCode()
-        result = 31 * result + bottomLeft.hashCode()
-        return result
-    }
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CutCornerShape.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CutCornerShape.kt
index 1080de2..43f1244 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CutCornerShape.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CutCornerShape.kt
@@ -82,6 +82,26 @@
         return "CutCornerShape(topLeft = $topLeft, topRight = $topRight, bottomRight = " +
             "$bottomRight, bottomLeft = $bottomLeft)"
     }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is CutCornerShape) return false
+
+        if (topLeft != other.topLeft) return false
+        if (topRight != other.topRight) return false
+        if (bottomRight != other.bottomRight) return false
+        if (bottomLeft != other.bottomLeft) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = topLeft.hashCode()
+        result = 31 * result + topRight.hashCode()
+        result = 31 * result + bottomRight.hashCode()
+        result = 31 * result + bottomLeft.hashCode()
+        return result
+    }
 }
 
 /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/RoundedCornerShape.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/RoundedCornerShape.kt
index 495a14a..2124441 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/RoundedCornerShape.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/RoundedCornerShape.kt
@@ -78,6 +78,26 @@
             "$bottomRight, bottomLeft = $bottomLeft)"
     }
 
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is RoundedCornerShape) return false
+
+        if (topLeft != other.topLeft) return false
+        if (topRight != other.topRight) return false
+        if (bottomRight != other.bottomRight) return false
+        if (bottomLeft != other.bottomLeft) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = topLeft.hashCode()
+        result = 31 * result + topRight.hashCode()
+        result = 31 * result + bottomRight.hashCode()
+        result = 31 * result + bottomLeft.hashCode()
+        return result
+    }
+
     private /*inline*/ fun Float.toRadius() = CornerRadius(this)
 }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
index a39fc33..ed09be3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
@@ -52,6 +52,7 @@
 import androidx.compose.ui.semantics.getTextLayoutResult
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.Placeholder
 import androidx.compose.ui.text.TextDelegate
@@ -96,6 +97,7 @@
  */
 @Composable
 @InternalTextApi
+@OptIn(ExperimentalTextApi::class)
 fun CoreText(
     text: AnnotatedString,
     modifier: Modifier = Modifier,
@@ -194,7 +196,10 @@
     }
 }
 
-@OptIn(InternalTextApi::class)
+@OptIn(
+    InternalTextApi::class,
+    ExperimentalTextApi::class
+)
 private class TextController(val state: TextState) {
     var selectionRegistrar: SelectionRegistrar? = null
 
@@ -225,7 +230,7 @@
             if (state.selectionRange != null) {
                 val newGlobalPosition = it.globalPosition
                 if (newGlobalPosition != state.previousGlobalPosition) {
-                    selectionRegistrar.onPositionChange()
+                    selectionRegistrar.notifyPositionChange()
                 }
                 state.previousGlobalPosition = newGlobalPosition
             }
@@ -272,6 +277,13 @@
         )
         if (state.layoutResult != layoutResult) {
             state.onTextLayout(layoutResult)
+            if (state.layoutResult != null) {
+                // Notify the SelectionContainer that this CoreText has changed and previous
+                // selection is invalid.
+                state.selectable?.let {
+                    selectionRegistrar?.notifySelectableChange(it)
+                }
+            }
         }
         state.layoutResult = layoutResult
 
@@ -318,31 +330,35 @@
 
     val commit: CommitScope.() -> Unit = {
         // if no SelectionContainer is added as parent selectionRegistrar will be null
-        val id: Selectable? =
-            selectionRegistrar?.let { selectionRegistrar ->
-                selectionRegistrar.subscribe(
-                    MultiWidgetSelectionDelegate(
-                        selectionRangeUpdate = { state.selectionRange = it },
-                        coordinatesCallback = { state.layoutCoordinates },
-                        layoutResultCallback = { state.layoutResult }
-                    )
+        state.selectable = selectionRegistrar?.let { selectionRegistrar ->
+            selectionRegistrar.subscribe(
+                MultiWidgetSelectionDelegate(
+                    selectionRangeUpdate = { state.selectionRange = it },
+                    coordinatesCallback = { state.layoutCoordinates },
+                    layoutResultCallback = { state.layoutResult }
                 )
-            }
+            )
+        }
 
         onDispose {
             // unregister only if any id was provided by SelectionRegistrar
-            id?.let { selectionRegistrar?.unsubscribe(id) }
+            state.selectable?.let { selectionRegistrar?.unsubscribe(it) }
         }
     }
 }
 
-@OptIn(InternalTextApi::class)
+@OptIn(
+    InternalTextApi::class,
+    ExperimentalTextApi::class
+)
 @VisibleForTesting
 internal class TextState(
     var textDelegate: TextDelegate
 ) {
     var onTextLayout: (TextLayoutResult) -> Unit = {}
 
+    /** The [Selectable] associated with this [CoreText]. */
+    var selectable: Selectable? = null
     /**
      * The current selection range, used by selection.
      * This should be a state as every time we update the value during the selection we
@@ -353,7 +369,7 @@
     var layoutCoordinates: LayoutCoordinates? = null
     /** The latest TextLayoutResult calculated in the measure block */
     var layoutResult: TextLayoutResult? = null
-    /** The global position calculated during the last onPositioned callback */
+    /** The global position calculated during the last notifyPosition callback */
     var previousGlobalPosition: Offset = Offset.Zero
     /** The paint used to draw highlight background for selected text. */
     val selectionPaint: Paint = Paint()
@@ -433,7 +449,10 @@
     return Pair(placeholders, inlineComposables)
 }
 
-@OptIn(InternalTextApi::class)
+@OptIn(
+    InternalTextApi::class,
+    ExperimentalTextApi::class
+)
 @VisibleForTesting
 internal fun longPressDragObserver(
     state: TextState,
@@ -455,10 +474,9 @@
             state.layoutCoordinates?.let {
                 if (!it.isAttached) return
 
-                selectionRegistrar?.onUpdateSelection(
+                selectionRegistrar?.notifySelectionUpdateStart(
                     layoutCoordinates = it,
-                    startPosition = pxPosition,
-                    endPosition = pxPosition
+                    startPosition = pxPosition
                 )
 
                 dragBeginPosition = pxPosition
@@ -466,7 +484,6 @@
         }
 
         override fun onDragStart() {
-            super.onDragStart()
             // selection never started
             if (state.selectionRange == null) return
             // Zero out the total distance that being dragged.
@@ -481,7 +498,7 @@
 
                 dragTotalDistance += dragDistance
 
-                selectionRegistrar?.onUpdateSelection(
+                selectionRegistrar?.notifySelectionUpdate(
                     layoutCoordinates = it,
                     startPosition = dragBeginPosition,
                     endPosition = dragBeginPosition + dragTotalDistance
@@ -489,5 +506,13 @@
             }
             return dragDistance
         }
+
+        override fun onStop(velocity: Offset) {
+            selectionRegistrar?.notifySelectionUpdateEnd()
+        }
+
+        override fun onCancel() {
+            selectionRegistrar?.notifySelectionUpdateEnd()
+        }
     }
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
index 5ab2ff5..7e3efd3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
@@ -32,11 +32,10 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.gesture.dragGestureFilter
 import androidx.compose.ui.gesture.longPressDragGestureFilter
 import androidx.compose.ui.gesture.tapGestureFilter
@@ -134,7 +133,6 @@
  */
 @Composable
 @OptIn(
-    ExperimentalFocus::class,
     ExperimentalTextApi::class,
     MouseTemporaryApi::class
 )
@@ -156,7 +154,7 @@
     // If developer doesn't pass new value to TextField, recompose won't happen but internal state
     // and IME may think it is updated. To fix this inconsistent state, enforce recompose.
     val recompose = invalidate
-    val focusRequester = FocusRequester()
+    val focusReference = FocusReference()
 
     // Ambients
     val textInputService = AmbientTextInputService.current
@@ -211,11 +209,11 @@
     manager.clipboardManager = AmbientClipboardManager.current
     manager.textToolbar = AmbientTextToolbar.current
     manager.hapticFeedBack = AmbientHapticFeedback.current
-    manager.focusRequester = focusRequester
+    manager.focusReference = focusReference
 
-    val focusObserver = Modifier.focusObserver {
+    val onFocusChanged = Modifier.onFocusChanged {
         if (state.hasFocus == it.isFocused) {
-            return@focusObserver
+            return@onFocusChanged
         }
 
         state.hasFocus = it.isFocused
@@ -267,7 +265,7 @@
 
     val focusRequestTapModifier = Modifier.tapGestureFilter {
         if (!state.hasFocus) {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         } else {
             // if already focused make sure tap request keyboard.
             textInputService?.showSoftwareKeyboard(state.inputSession)
@@ -299,7 +297,7 @@
 
     val pointerModifier = if (isMouseInput) {
         Modifier.dragGestureFilter(
-            manager.mouseSelectionObserver(onStart = { focusRequester.requestFocus() }),
+            manager.mouseSelectionObserver(onStart = { focusReference.requestFocus() }),
             startDragImmediately = true
         )
     } else {
@@ -386,7 +384,7 @@
         }
         onClick {
             if (!state.hasFocus) {
-                focusRequester.requestFocus()
+                focusReference.requestFocus()
             } else {
                 textInputService?.showSoftwareKeyboard(state.inputSession)
             }
@@ -417,8 +415,8 @@
     onDispose { manager.hideSelectionToolbar() }
 
     val modifiers = modifier
-        .focusRequester(focusRequester)
-        .then(focusObserver)
+        .focusReference(focusReference)
+        .then(onFocusChanged)
         .then(cursorModifier)
         .then(pointerModifier)
         .then(drawModifier)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldCursor.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldCursor.kt
index 626a730..e22c3ab 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldCursor.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldCursor.kt
@@ -18,11 +18,10 @@
 
 import androidx.compose.animation.core.AnimatedFloat
 import androidx.compose.animation.core.AnimationClockObservable
-import androidx.compose.animation.core.AnimationConstants
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.infiniteRepeatable
 import androidx.compose.animation.core.keyframes
-import androidx.compose.animation.core.repeatable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -103,8 +102,7 @@
 }
 
 private val cursorAnimationSpec: AnimationSpec<Float>
-    get() = repeatable(
-        iterations = AnimationConstants.Infinite,
+    get() = infiniteRepeatable(
         animation = keyframes {
             durationMillis = 1000
             1f at 0
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
index eaa5cc2..f908eb0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
@@ -22,10 +22,12 @@
 import androidx.compose.ui.selection.Selectable
 import androidx.compose.ui.selection.Selection
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextRange
 import kotlin.math.max
 
+@OptIn(ExperimentalTextApi::class)
 internal class MultiWidgetSelectionDelegate(
     private val selectionRangeUpdate: (TextRange?) -> Unit,
     private val coordinatesCallback: () -> LayoutCoordinates?,
@@ -123,6 +125,7 @@
  *
  * @return [Selection] of the current composable, or null if the composable is not selected.
  */
+@OptIn(ExperimentalTextApi::class)
 internal fun getTextSelectionInfo(
     textLayoutResult: TextLayoutResult,
     selectionCoordinates: Pair<Offset, Offset>,
@@ -206,6 +209,7 @@
  *
  * @return [Selection] of the current composable, or null if the composable is not selected.
  */
+@OptIn(ExperimentalTextApi::class)
 private fun getRefinedSelectionInfo(
     rawStartOffset: Int,
     rawEndOffset: Int,
@@ -282,6 +286,7 @@
  *
  * @return an assembled object of [Selection] using the offered selection info.
  */
+@OptIn(ExperimentalTextApi::class)
 private fun getAssembledSelectionInfo(
     startOffset: Int,
     endOffset: Int,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
index 89b5fd5..dd4b112 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
@@ -20,8 +20,7 @@
 import androidx.compose.foundation.text.TextFieldState
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.gesture.DragObserver
@@ -52,10 +51,7 @@
 /**
  * A bridge class between user interaction to the text field selection.
  */
-@OptIn(
-    InternalTextApi::class,
-    ExperimentalFocus::class
-)
+@OptIn(InternalTextApi::class)
 internal class TextFieldSelectionManager() {
 
     /**
@@ -94,9 +90,9 @@
     var hapticFeedBack: HapticFeedback? = null
 
     /**
-     * [FocusRequester] used to request focus for the TextField.
+     * [FocusReference] used to request focus for the TextField.
      */
-    var focusRequester: FocusRequester? = null
+    var focusReference: FocusReference? = null
 
     /**
      * The beginning position of the drag gesture. Every time a new drag gesture starts, it wil be
@@ -287,7 +283,7 @@
      */
     internal fun enterSelectionMode() {
         if (state?.hasFocus == false) {
-            focusRequester?.requestFocus()
+            focusReference?.requestFocus()
         }
         oldValue = value
         state?.showFloatingToolbar = true
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
index 96e0375..0384857 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.kt
@@ -88,7 +88,7 @@
 
 /**
  * Vertical scrollbar that can be attached to some scrollable
- * component (ScrollableColumn, LazyColumnFor) and share common state with it.
+ * component (ScrollableColumn, LazyColumn) and share common state with it.
  *
  * Can be placed independently.
  *
@@ -128,7 +128,7 @@
 
 /**
  * Horizontal scrollbar that can be attached to some scrollable
- * component (ScrollableRow, LazyRowFor) and share common state with it.
+ * component (ScrollableRow, LazyRow) and share common state with it.
  *
  * Can be placed independently.
  *
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/DesktopCoreTextField.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/DesktopCoreTextField.kt
index 6b6caac..d5e4a44 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/DesktopCoreTextField.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/DesktopCoreTextField.kt
@@ -19,7 +19,6 @@
 import androidx.compose.foundation.text.selection.TextFieldSelectionManager
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.input.key.plus
 import androidx.compose.ui.input.key.shortcuts
@@ -40,7 +39,6 @@
 
 private val selectAllKeySet by lazy { modifier + Key.A }
 
-@OptIn(ExperimentalKeyInput::class)
 internal actual fun Modifier.textFieldKeyboardModifier(
     manager: TextFieldSelectionManager
 ): Modifier = composed {
diff --git a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
index d2ed8a5..c2f7983 100644
--- a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
+++ b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
@@ -22,7 +22,7 @@
 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.LazyColumn
 import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.runtime.Composable
@@ -436,12 +436,13 @@
         scrollbarWidth: Dp,
     ) = withTestEnvironment {
         Box(Modifier.size(size)) {
-            LazyColumnFor(
-                (0 until childCount).toList(),
+            LazyColumn(
                 Modifier.fillMaxSize().testTag("column"),
                 state
             ) {
-                Box(Modifier.size(childSize).testTag("box$it"))
+                items((0 until childCount).toList()) {
+                    Box(Modifier.size(childSize).testTag("box$it"))
+                }
             }
 
             VerticalScrollbar(
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/DragGestureDetectorTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/DragGestureDetectorTest.kt
index a146012f..a56b6f41 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/DragGestureDetectorTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/DragGestureDetectorTest.kt
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.foundation.gestures
 
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.input.pointer.anyPositionChangeConsumed
 import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
 import androidx.compose.ui.input.pointer.consumeAllChanges
@@ -34,7 +31,6 @@
 import org.junit.runners.Parameterized
 
 @RunWith(Parameterized::class)
-@OptIn(ExperimentalPointerInput::class)
 class DragGestureDetectorTest(dragType: GestureType) {
     enum class GestureType {
         VerticalDrag,
@@ -101,7 +97,7 @@
 
     private val AwaitVerticalDragUtil = SuspendingGestureTestUtil(width = 100, height = 100) {
         forEachGesture {
-            handlePointerInput {
+            awaitPointerEventScope {
                 val down = awaitFirstDown()
                 val slopChange = awaitVerticalTouchSlopOrCancellation(down.id) { change, overSlop ->
                     if (change.positionChange().y > 0f || !consumePositiveOnly) {
@@ -132,7 +128,7 @@
 
     private val AwaitHorizontalDragUtil = SuspendingGestureTestUtil(width = 100, height = 100) {
         forEachGesture {
-            handlePointerInput {
+            awaitPointerEventScope {
                 val down = awaitFirstDown()
                 val slopChange =
                     awaitHorizontalTouchSlopOrCancellation(down.id) { change, overSlop ->
@@ -164,7 +160,7 @@
 
     private val AwaitDragUtil = SuspendingGestureTestUtil(width = 100, height = 100) {
         forEachGesture {
-            handlePointerInput {
+            awaitPointerEventScope {
                 val down = awaitFirstDown()
                 val slopChange = awaitTouchSlopOrCancellation(down.id) { change, overSlop ->
                     val positionChange = change.positionChange()
@@ -361,75 +357,6 @@
     }
 
     /**
-     * When this drag direction is less than the other drag direction, it should wait
-     * before locking the orientation.
-     */
-    @Test
-    fun dragLockedWithLowPriority() = util.executeInComposition {
-        if (!twoAxisDrag) {
-            down().moveBy(
-                dragMotion + (crossDragMotion * 2f),
-                final = {
-                    // The other direction should have priority, but it should consume the
-                    // in-direction position change
-                    assertEquals(dragMotion, consumed.positionChange)
-
-                    // but it shouldn't have called the callback, yet
-                    assertFalse(dragged)
-                }
-            )
-                .up()
-            assertTrue(dragged)
-            assertTrue(gestureEnded)
-            assertFalse(gestureCanceled)
-            assertEquals(0f, dragDistance)
-        }
-    }
-
-    /**
-     * When this drag direction is less than the other drag direction, it should wait
-     * before locking the orientation. When the other direction locks, it should not drag.
-     */
-    @Test
-    fun dragLockFailWithLowPriority() = util.executeInComposition {
-        if (!twoAxisDrag) {
-            down().moveBy(
-                dragMotion + (crossDragMotion * 2f),
-                final = {
-                    consumeAllChanges()
-                }
-            )
-                .up()
-            assertFalse(dragged)
-            assertFalse(gestureEnded)
-            assertFalse(gestureCanceled)
-        }
-    }
-
-    /**
-     * When this drag direction is less than the other drag direction, it should wait
-     * before locking the orientation. When the other direction locks, it should not drag.
-     */
-    @Test
-    fun dragLockFailNested() = util.executeInComposition {
-        if (!twoAxisDrag) {
-            down().moveBy(
-                dragMotion + (crossDragMotion * 2f),
-                final = {
-                    assertEquals(crossDragMotion * 2f, positionChange())
-                    consumeAllChanges()
-                }
-            ).also {
-                assertEquals(dragMotion, it.positionChange())
-            }
-                .up()
-            assertFalse(dragged)
-            assertFalse(gestureEnded)
-            assertFalse(gestureCanceled)
-        }
-    }
-
-    /**
      * When a drag is not consumed, it should lead to the touch slop being reset. This is
      * important when you drag your finger to
      */
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/MultitouchGestureDetectorTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/MultitouchGestureDetectorTest.kt
index e2a1373..aa3caaa 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/MultitouchGestureDetectorTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/MultitouchGestureDetectorTest.kt
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.foundation.gestures
 
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.input.pointer.anyPositionChangeConsumed
 import androidx.compose.ui.input.pointer.consumeAllChanges
 import org.junit.Assert.assertEquals
@@ -31,7 +28,6 @@
 import org.junit.runners.Parameterized
 
 @RunWith(Parameterized::class)
-@OptIn(ExperimentalPointerInput::class)
 class MultitouchGestureDetectorTest(val panZoomLock: Boolean) {
     companion object {
         @JvmStatic
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt
index 5ce5068..b544ab1 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt
@@ -23,13 +23,11 @@
 import androidx.compose.runtime.InternalComposeApi
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.SlotTable
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.dispatch.MonotonicFrameClock
 import androidx.compose.runtime.withRunningRecomposer
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.input.pointer.ConsumedData
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
@@ -59,7 +57,6 @@
  * [gestureDetector]. The [width] and [height] of the LayoutNode may
  * be provided.
  */
-@OptIn(ExperimentalPointerInput::class)
 internal class SuspendingGestureTestUtil(
     val width: Int = 10,
     val height: Int = 10,
@@ -304,7 +301,6 @@
         block: @Composable () -> Unit
     ): Composer<Unit> {
         return Composer(
-            SlotTable(),
             EmptyApplier(),
             recomposer
         ).apply {
@@ -314,7 +310,7 @@
                 fn(this, 0)
             }
             applyChanges()
-            slotTable.verifyWellFormed()
+            verifyConsistent()
         }
     }
 
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt
index a5740b8..5484742 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.foundation.gestures
 
 import androidx.compose.ui.gesture.DoubleTapTimeout
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.gesture.LongPressTimeout
 import androidx.compose.ui.input.pointer.consumeDownChange
 import androidx.compose.ui.input.pointer.consumePositionChange
@@ -34,7 +31,6 @@
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-@OptIn(ExperimentalPointerInput::class)
 class TapGestureDetectorTest {
     private var pressed = false
     private var released = false
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/lazy/IntervalListTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/lazy/IntervalListTest.kt
new file mode 100644
index 0000000..e3128b1
--- /dev/null
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/lazy/IntervalListTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class IntervalListTest {
+
+    private val intervalList = IntervalList<Int>()
+
+    @Test
+    fun addOneItem_searchInterval() {
+        intervalList.add(1, 10)
+
+        val foundInterval = intervalList.intervalForIndex(0)
+
+        assertThat(foundInterval.content).isEqualTo(10)
+        assertThat(foundInterval.size).isEqualTo(1)
+        assertThat(foundInterval.startIndex).isEqualTo(0)
+    }
+
+    @Test
+    fun addOneItem_searchIndexOutOfBounds() {
+        intervalList.add(1, 10)
+
+        val wasException: Boolean = try {
+            intervalList.intervalForIndex(2)
+            false
+        } catch (e: IndexOutOfBoundsException) {
+            true
+        }
+
+        assertThat(wasException).isTrue()
+    }
+
+    @Test
+    fun addSingleItems_searchFirstInterval() {
+        addFiveSingleIntervals()
+
+        val foundInterval = intervalList.intervalForIndex(0)
+
+        assertThat(foundInterval.content).isEqualTo(10)
+        assertThat(foundInterval.size).isEqualTo(1)
+        assertThat(foundInterval.startIndex).isEqualTo(0)
+    }
+
+    @Test
+    fun addSingleItems_searchLastInterval() {
+        addFiveSingleIntervals()
+
+        val foundInterval = intervalList.intervalForIndex(4)
+
+        assertThat(foundInterval.content).isEqualTo(50)
+        assertThat(foundInterval.size).isEqualTo(1)
+        assertThat(foundInterval.startIndex).isEqualTo(4)
+    }
+
+    @Test
+    fun addSingleItems_searchMidInterval() {
+        addFiveSingleIntervals()
+
+        val foundInterval = intervalList.intervalForIndex(2)
+
+        assertThat(foundInterval.content).isEqualTo(30)
+        assertThat(foundInterval.size).isEqualTo(1)
+        assertThat(foundInterval.startIndex).isEqualTo(2)
+    }
+
+    @Test
+    fun addVariableItems_searchFirstInterval() {
+        addFiveVariableIntervals()
+
+        val foundInterval = intervalList.intervalForIndex(0)
+
+        assertThat(foundInterval.content).isEqualTo(10)
+        assertThat(foundInterval.size).isEqualTo(3)
+        assertThat(foundInterval.startIndex).isEqualTo(0)
+    }
+
+    @Test
+    fun addVariableItems_searchLastInterval() {
+        addFiveVariableIntervals()
+
+        val foundInterval = intervalList.intervalForIndex(22)
+
+        assertThat(foundInterval.content).isEqualTo(50)
+        assertThat(foundInterval.size).isEqualTo(11)
+        assertThat(foundInterval.startIndex).isEqualTo(12)
+    }
+
+    @Test
+    fun addVariableItems_searchMidInterval() {
+        addFiveVariableIntervals()
+
+        val foundInterval = intervalList.intervalForIndex(6)
+
+        assertThat(foundInterval.content).isEqualTo(30)
+        assertThat(foundInterval.size).isEqualTo(7)
+        assertThat(foundInterval.startIndex).isEqualTo(4)
+    }
+
+    @Test
+    fun addVariableItems_searchSecondInterval() {
+        addFiveVariableIntervals()
+
+        val foundInterval = intervalList.intervalForIndex(3)
+
+        assertThat(foundInterval.content).isEqualTo(20)
+        assertThat(foundInterval.size).isEqualTo(1)
+        assertThat(foundInterval.startIndex).isEqualTo(3)
+    }
+
+    @Test
+    fun addVariableItems_searchIndexOutOfBounds() {
+        addFiveVariableIntervals()
+
+        val wasException: Boolean = try {
+            intervalList.intervalForIndex(23)
+            false
+        } catch (e: IndexOutOfBoundsException) {
+            true
+        }
+
+        assertThat(wasException).isTrue()
+    }
+
+    private fun addFiveSingleIntervals() {
+        intervalList.add(1, 10)
+        intervalList.add(1, 20)
+        intervalList.add(1, 30)
+        intervalList.add(1, 40)
+        intervalList.add(1, 50)
+    }
+
+    private fun addFiveVariableIntervals() {
+        intervalList.add(3, 10)
+        intervalList.add(1, 20)
+        intervalList.add(7, 30)
+        intervalList.add(1, 40)
+        intervalList.add(11, 50)
+    }
+}
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
index 9ea31ce..cd6e838 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
@@ -22,6 +22,7 @@
 import androidx.compose.ui.selection.Selectable
 import androidx.compose.ui.selection.Selection
 import androidx.compose.ui.selection.SelectionRegistrar
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.TextDelegate
 import androidx.compose.ui.text.style.ResolvedTextDirection
@@ -36,7 +37,10 @@
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-@OptIn(InternalTextApi::class)
+@OptIn(
+    InternalTextApi::class,
+    ExperimentalTextApi::class
+)
 class TextSelectionLongPressDragTest {
     private val selectionRegistrar = mock<SelectionRegistrar>()
     private val selectable = mock<Selectable>()
@@ -93,15 +97,14 @@
     }
 
     @Test
-    fun longPressDragObserver_onLongPress_calls_getSelection_change_selection() {
+    fun longPressDragObserver_onLongPress_calls_notifySelectionInitiated() {
         val position = Offset(100f, 100f)
 
         gesture.onLongPress(position)
 
-        verify(selectionRegistrar, times(1)).onUpdateSelection(
+        verify(selectionRegistrar, times(1)).notifySelectionUpdateStart(
             layoutCoordinates = layoutCoordinates,
-            startPosition = position,
-            endPosition = position
+            startPosition = position
         )
     }
 
@@ -127,7 +130,7 @@
 
         // Verify.
         verify(selectionRegistrar, times(1))
-            .onUpdateSelection(
+            .notifySelectionUpdate(
                 layoutCoordinates = layoutCoordinates,
                 startPosition = beginPosition2,
                 endPosition = beginPosition2 + dragDistance2
@@ -135,7 +138,7 @@
     }
 
     @Test
-    fun longPressDragObserver_onDrag_calls_getSelection_change_selection() {
+    fun longPressDragObserver_onDrag_calls_notifySelectionDrag() {
         val dragDistance = Offset(15f, 10f)
         val beginPosition = Offset(30f, 20f)
         gesture.onLongPress(beginPosition)
@@ -146,10 +149,35 @@
 
         assertThat(result).isEqualTo(dragDistance)
         verify(selectionRegistrar, times(1))
-            .onUpdateSelection(
+            .notifySelectionUpdate(
                 layoutCoordinates = layoutCoordinates,
                 startPosition = beginPosition,
                 endPosition = beginPosition + dragDistance
             )
     }
+
+    @Test
+    fun longPressDragObserver_onStop_calls_notifySelectionEnd() {
+        val dragDistance = Offset(15f, 10f)
+        val beginPosition = Offset(30f, 20f)
+        gesture.onLongPress(beginPosition)
+        state.selectionRange = fakeInitialSelection.toTextRange()
+        gesture.onDragStart()
+        gesture.onStop(dragDistance)
+
+        verify(selectionRegistrar, times(1))
+            .notifySelectionUpdateEnd()
+    }
+
+    @Test
+    fun longPressDragObserver_onCancel_calls_notifySelectionEnd() {
+        val beginPosition = Offset(30f, 20f)
+        gesture.onLongPress(beginPosition)
+        state.selectionRange = fakeInitialSelection.toTextRange()
+        gesture.onDragStart()
+        gesture.onCancel()
+
+        verify(selectionRegistrar, times(1))
+            .notifySelectionUpdateEnd()
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
index 78d7fff..5c75510 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
@@ -17,8 +17,7 @@
 package androidx.compose.foundation.text.selection
 
 import androidx.compose.foundation.text.TextFieldState
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.hapticfeedback.HapticFeedback
@@ -54,10 +53,7 @@
 import org.mockito.stubbing.Answer
 
 @RunWith(JUnit4::class)
-@OptIn(
-    InternalTextApi::class,
-    ExperimentalFocus::class
-)
+@OptIn(InternalTextApi::class)
 class TextFieldSelectionManagerTest {
     private val text = "Hello World"
     private val density = Density(density = 1f)
@@ -79,7 +75,7 @@
     private val clipboardManager = mock<ClipboardManager>()
     private val textToolbar = mock<TextToolbar>()
     private val hapticFeedback = mock<HapticFeedback>()
-    private val focusRequester = mock<FocusRequester>()
+    private val focusReference = mock<FocusReference>()
 
     @Before
     fun setup() {
@@ -90,7 +86,7 @@
         manager.clipboardManager = clipboardManager
         manager.textToolbar = textToolbar
         manager.hapticFeedBack = hapticFeedback
-        manager.focusRequester = focusRequester
+        manager.focusReference = focusReference
 
         state.layoutResult = mock()
         state.textDelegate = mock()
@@ -144,7 +140,7 @@
         ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
 
         verify(
-            focusRequester,
+            focusReference,
             times(1)
         ).requestFocus()
     }
@@ -173,7 +169,7 @@
         ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
 
         verify(
-            focusRequester,
+            focusReference,
             times(1)
         ).requestFocus()
     }
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt
index 4c14493..8e652ac 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt
@@ -24,11 +24,7 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.LazyColumnFor
-import androidx.compose.foundation.lazy.LazyColumnForIndexed
 import androidx.compose.foundation.lazy.LazyRow
-import androidx.compose.foundation.lazy.LazyRowFor
-import androidx.compose.foundation.lazy.LazyRowForIndexed
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.emptyContent
@@ -92,13 +88,9 @@
                 LazyColumnWithItemAndItems,
                 LazyColumnWithItems,
                 LazyColumnWithItemsIndexed,
-                LazyColumnFor,
-                LazyColumnForIndexed,
                 LazyRowWithItemAndItems,
                 LazyRowWithItems,
-                LazyRowWithItemsIndexed,
-                LazyRowFor,
-                LazyRowForIndexed
+                LazyRowWithItemsIndexed
             )
     }
 }
@@ -147,26 +139,6 @@
     }
 }
 
-private val LazyColumnFor = LazyListScrollingTestCase("LazyColumnFor") {
-    LazyColumnFor(items, modifier = Modifier.height(400.dp).fillMaxWidth()) {
-        if (it.index == 0) {
-            RemeasurableItem()
-        } else {
-            RegularItem()
-        }
-    }
-}
-
-private val LazyColumnForIndexed = LazyListScrollingTestCase("LazyColumnForIndexed") {
-    LazyColumnForIndexed(items, modifier = Modifier.height(400.dp).fillMaxWidth()) { index, _ ->
-        if (index == 0) {
-            RemeasurableItem()
-        } else {
-            RegularItem()
-        }
-    }
-}
-
 private val LazyRowWithItemAndItems = LazyListScrollingTestCase("LazyRowWithItemAndItems") {
     LazyRow(modifier = Modifier.width(400.dp).fillMaxHeight()) {
         item {
@@ -202,26 +174,6 @@
     }
 }
 
-private val LazyRowFor = LazyListScrollingTestCase("LazyRowFor") {
-    LazyRowFor(items, modifier = Modifier.width(400.dp).fillMaxHeight()) {
-        if (it.index == 0) {
-            RemeasurableItem()
-        } else {
-            RegularItem()
-        }
-    }
-}
-
-private val LazyRowForIndexed = LazyListScrollingTestCase("LazyRowForIndexed") {
-    LazyRowForIndexed(items, modifier = Modifier.width(400.dp).fillMaxHeight()) { index, _ ->
-        if (index == 0) {
-            RemeasurableItem()
-        } else {
-            RegularItem()
-        }
-    }
-}
-
 // TODO(b/169852102 use existing public constructs instead)
 private fun ComposeBenchmarkRule.toggleStateBenchmarkMeasure(
     caseFactory: () -> ListRemeasureTestCase
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/LayoutNodeModifierBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/LayoutNodeModifierBenchmark.kt
index c440dc1..7d08b8b 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/LayoutNodeModifierBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/LayoutNodeModifierBenchmark.kt
@@ -14,28 +14,27 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATION_ERROR")
+
 package androidx.ui.benchmark.test
 
-import android.view.ViewGroup
 import androidx.activity.ComponentActivity
 import androidx.benchmark.junit4.BenchmarkRule
 import androidx.benchmark.junit4.measureRepeated
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.padding
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.gesture.pressIndicatorGestureFilter
 import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.input.key.ExperimentalKeyInput
-import androidx.compose.ui.input.key.keyInputFilter
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.layout.TestModifierUpdater
+import androidx.compose.ui.layout.TestModifierUpdaterLayout
 import androidx.compose.ui.layout.layoutId
-import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.platform.AndroidOwner
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.test.junit4.DisableTransitionsTestRule
 import androidx.compose.ui.test.InternalTestingApi
+import androidx.compose.ui.test.junit4.DisableTransitionsTestRule
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
 import androidx.test.filters.LargeTest
@@ -50,7 +49,7 @@
 import org.junit.runners.model.Statement
 
 /**
- * Benchmark that sets the [LayoutNode.modifier].
+ * Benchmark that sets the LayoutNode.modifier.
  */
 @LargeTest
 @RunWith(Parameterized::class)
@@ -68,16 +67,15 @@
 
     var modifiers = emptyList<Modifier>()
     var combinedModifier: Modifier = Modifier
-    lateinit var layoutNode: LayoutNode
+    lateinit var testModifierUpdater: TestModifierUpdater
 
     @Before
-    @OptIn(ExperimentalKeyInput::class)
     fun setup() {
         modifiers = listOf(
             Modifier.padding(10.dp),
             Modifier.drawBehind { },
             Modifier.graphicsLayer(),
-            Modifier.keyInputFilter { _ -> true },
+            Modifier.onKeyEvent { true },
             Modifier.semantics { },
             Modifier.pressIndicatorGestureFilter(),
             Modifier.layoutId("Hello"),
@@ -91,14 +89,11 @@
         }
 
         rule.activityTestRule.runOnUiThread {
-            rule.activityTestRule.activity.setContent { Box(Modifier) }
-        }
-        rule.activityTestRule.runOnUiThread {
-            val composeView = rule.findAndroidOwner()
-            val root = composeView.root
-            check(root.children.size == 1) { "Expecting only a Box" }
-            layoutNode = root.children[0]
-            check(layoutNode.children.isEmpty()) { "Box should be empty" }
+            rule.activityTestRule.activity.setContent {
+                TestModifierUpdaterLayout {
+                    testModifierUpdater = it
+                }
+            }
         }
     }
 
@@ -106,8 +101,8 @@
     fun setAndClearModifiers() {
         rule.activityTestRule.runOnUiThread {
             rule.benchmarkRule.measureRepeated {
-                layoutNode.modifier = combinedModifier
-                layoutNode.modifier = Modifier
+                testModifierUpdater.updateModifier(combinedModifier)
+                testModifierUpdater.updateModifier(Modifier)
             }
         }
     }
@@ -116,11 +111,11 @@
     fun smallModifierChange() {
         rule.activityTestRule.runOnUiThread {
             val altModifier = Modifier.padding(10.dp).then(combinedModifier)
-            layoutNode.modifier = altModifier
+            testModifierUpdater.updateModifier(altModifier)
 
             rule.benchmarkRule.measureRepeated {
-                layoutNode.modifier = combinedModifier
-                layoutNode.modifier = altModifier
+                testModifierUpdater.updateModifier(combinedModifier)
+                testModifierUpdater.updateModifier(altModifier)
             }
         }
     }
@@ -140,10 +135,5 @@
                 .around(activityTestRule)
                 .apply(base, description)
         }
-
-        fun findAndroidOwner(): AndroidOwner {
-            return activityTestRule.activity.findViewById<ViewGroup>(android.R.id.content)
-                .getChildAt(0) as AndroidOwner
-        }
     }
 }
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/autofill/AndroidAutofillBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/autofill/AndroidAutofillBenchmark.kt
index 77859d6..4aee2ae 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/autofill/AndroidAutofillBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/autofill/AndroidAutofillBenchmark.kt
@@ -21,6 +21,7 @@
 import android.view.autofill.AutofillValue
 import androidx.benchmark.junit4.BenchmarkRule
 import androidx.benchmark.junit4.measureRepeated
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.autofill.AutofillNode
 import androidx.compose.ui.autofill.AutofillTree
 import androidx.compose.ui.autofill.AutofillType
@@ -38,6 +39,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 
 @LargeTest
+@OptIn(ExperimentalComposeUiApi::class)
 @RunWith(AndroidJUnit4::class)
 class AndroidAutofillBenchmark {
 
@@ -58,6 +60,7 @@
         }
     }
 
+    @OptIn(ExperimentalComposeUiApi::class)
     @Test
     @UiThreadTest
     @SdkSuppress(minSdkVersion = 26)
diff --git a/compose/integration-tests/demos/build.gradle b/compose/integration-tests/demos/build.gradle
index 2f7c2c0..e422582 100644
--- a/compose/integration-tests/demos/build.gradle
+++ b/compose/integration-tests/demos/build.gradle
@@ -32,7 +32,7 @@
     implementation project(":compose:runtime:runtime")
     implementation project(":compose:ui:ui")
 
-    implementation "androidx.preference:preference-ktx:1.1.0"
+    implementation "androidx.preference:preference-ktx:1.1.1"
 
     androidTestImplementation project(":compose:ui:ui-test-junit4")
 
diff --git a/compose/integration-tests/demos/lint-baseline.xml b/compose/integration-tests/demos/lint-baseline.xml
deleted file mode 100644
index 08f1cf1..0000000
--- a/compose/integration-tests/demos/lint-baseline.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.2.0-alpha15" client="gradle" variant="debug" version="4.2.0-alpha15">
-
-    <issue
-        id="ObsoleteLintCustomCheck"
-        message="Lint found an issue registry (`androidx.lifecycle.lint.LifecycleRuntimeIssueRegistry`) which is older than the current API level; these checks may not work correctly.&#xA;&#xA;Recompile the checks against the latest version. Custom check API version is 6 (3.6), current lint API level is 8 (4.1)">
-        <location
-            file="../../../../../../../home/jeffrygaston/.gradle/caches/transforms-2/files-2.1/02dcda78691438fc76cdfd012889a28d/lifecycle-runtime-ktx-2.2.0/jars/lint.jar"/>
-    </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 54eda68..d88636e 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
@@ -39,9 +39,8 @@
 import androidx.compose.runtime.onCommit
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.withStyle
@@ -101,25 +100,22 @@
  * [BasicTextField] that edits the current [filterText], providing [onFilter] when edited.
  */
 @Composable
-@OptIn(
-    ExperimentalFocus::class,
-    ExperimentalFoundationApi::class
-)
+@OptIn(ExperimentalFoundationApi::class)
 private fun FilterField(
     filterText: String,
     onFilter: (String) -> Unit,
     modifier: Modifier = Modifier
 ) {
-    val focusRequester = FocusRequester()
+    val focusReference = FocusReference()
     // TODO: replace with Material text field when available
     BasicTextField(
-        modifier = modifier.focusRequester(focusRequester),
+        modifier = modifier.focusReference(focusReference),
         value = filterText,
         onValueChange = onFilter,
         textStyle = AmbientTextStyle.current,
         cursorColor = AmbientContentColor.current
     )
-    onCommit { focusRequester.requestFocus() }
+    onCommit { focusReference.requestFocus() }
 }
 
 /**
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/layout/Layout.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/layout/Layout.kt
index dcab8ad..07e9583 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/layout/Layout.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/layout/Layout.kt
@@ -29,7 +29,7 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumnFor
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.Button
 import androidx.compose.material.Scaffold
 import androidx.compose.material.Surface
@@ -137,8 +137,10 @@
         onSelected: (Artist) -> Unit
     ) {
         Surface(Modifier.fillMaxSize()) {
-            LazyColumnFor(feedItems) { item ->
-                ArtistCard(item, onSelected)
+            LazyColumn {
+                items(feedItems) { item ->
+                    ArtistCard(item, onSelected)
+                }
             }
         }
     }
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/mentalmodel/MentalModel.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/mentalmodel/MentalModel.kt
index e110e4d..635fbdf 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/mentalmodel/MentalModel.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/mentalmodel/MentalModel.kt
@@ -23,7 +23,7 @@
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.lazy.LazyColumnFor
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.Button
 import androidx.compose.material.Checkbox
 import androidx.compose.material.Divider
@@ -118,12 +118,14 @@
             Text(header, style = MaterialTheme.typography.h5)
             Divider()
 
-            // LazyColumnFor is the Compose version of a RecyclerView.
-            // The lambda passed is similar to a RecyclerView.ViewHolder.
-            LazyColumnFor(names) { name ->
-                // When an item's [name] updates, the adapter for that item
-                // will recompose. This will not recompose when [header] changes
-                NamePickerItem(name, onNameClicked)
+            // LazyColumn is the Compose version of a RecyclerView.
+            // The lambda passed to items() is similar to a RecyclerView.ViewHolder.
+            LazyColumn {
+                items(names) { name ->
+                    // When an item's [name] updates, the adapter for that item
+                    // will recompose. This will not recompose when [header] changes
+                    NamePickerItem(name, onNameClicked)
+                }
             }
         }
     }
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/preview/LayoutPreview.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/preview/LayoutPreview.kt
index 3661374..25c3edd 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/preview/LayoutPreview.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/preview/LayoutPreview.kt
@@ -21,7 +21,7 @@
 package androidx.compose.integration.docs.preview
 
 import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants.defaultButtonColors
+import androidx.compose.material.ButtonDefaults.buttonColors
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
@@ -78,7 +78,7 @@
     fun Counter(count: Int, updateCount: (Int) -> Unit) {
         Button(
             onClick = { updateCount(count + 1) },
-            colors = defaultButtonColors(
+            colors = buttonColors(
                 backgroundColor = if (count > 5) Color.Green else Color.White
             )
         ) {
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
index 2c92a77..2dd4e4e 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
@@ -327,7 +327,7 @@
 
 private const val it = 1
 private lateinit var helloViewModel: StateSnippet2.HelloViewModel
-private fun computeTextFormatting(st: String) {}
+private fun computeTextFormatting(st: String): String = ""
 
 private fun ExpandingCard(
     title: String,
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt
index a14e06bc..c5f7f64 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt
@@ -25,11 +25,10 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.semantics.AccessibilityAction
 import androidx.compose.ui.semantics.SemanticsPropertyKey
-import androidx.compose.ui.semantics.accessibilityLabel
+import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.SemanticsNodeInteraction
@@ -47,10 +46,10 @@
 import androidx.compose.ui.test.hasTestTag
 import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onAllNodesWithLabel
+import androidx.compose.ui.test.onAllNodesWithContentDescription
 import androidx.compose.ui.test.onChildren
 import androidx.compose.ui.test.onFirst
-import androidx.compose.ui.test.onNodeWithLabel
+import androidx.compose.ui.test.onNodeWithContentDescription
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performClick
@@ -73,7 +72,7 @@
  */
 
 @Composable private fun TestingSnippet1() {
-    MyButton(modifier = Modifier.semantics { accessibilityLabel = "Like button" })
+    MyButton(modifier = Modifier.semantics { contentDescription = "Like button" })
 }
 
 private object TestingSnippet3 {
@@ -142,11 +141,11 @@
 
 @Composable private fun TestingSnippets8() {
     // Check number of matched nodes
-    composeTestRule.onAllNodesWithLabel("Beatle").assertCountEquals(4)
+    composeTestRule.onAllNodesWithContentDescription("Beatle").assertCountEquals(4)
     // At least one matches
-    composeTestRule.onAllNodesWithLabel("Beatle").assertAny(hasTestTag("Drummer"))
+    composeTestRule.onAllNodesWithContentDescription("Beatle").assertAny(hasTestTag("Drummer"))
     // All of them match
-    composeTestRule.onAllNodesWithLabel("Beatle").assertAll(hasClickAction())
+    composeTestRule.onAllNodesWithContentDescription("Beatle").assertAll(hasClickAction())
 }
 
 @Composable private fun SemanticsNodeInteraction.TestingSnippets9() {
@@ -202,13 +201,13 @@
 
         @Test
         fun changeTheme_scrollIsPersisted() {
-            composeTestRule.onNodeWithLabel("Continue").performClick()
+            composeTestRule.onNodeWithContentDescription("Continue").performClick()
 
             // Set theme to dark
             themeIsDark.value = true
 
             // Check that we're still on the same page
-            composeTestRule.onNodeWithLabel("Welcome").assertIsDisplayed()
+            composeTestRule.onNodeWithContentDescription("Welcome").assertIsDisplayed()
         }
     }
 }
@@ -228,5 +227,4 @@
 private class MyActivity : ComponentActivity()
 @Composable private fun MyButton(content: @Composable RowScope.() -> Unit) { }
 private lateinit var key: SemanticsPropertyKey<AccessibilityAction<() -> Boolean>>
-@OptIn(ExperimentalKeyInput::class)
 private lateinit var keyEvent: KeyEvent
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/theming/Theming.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/theming/Theming.kt
index 2020679..95a8359 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/theming/Theming.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/theming/Theming.kt
@@ -26,7 +26,7 @@
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.AmbientContentAlpha
 import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants
+import androidx.compose.material.ButtonDefaults
 import androidx.compose.material.Colors
 import androidx.compose.material.ContentAlpha
 import androidx.compose.material.MaterialTheme
@@ -250,7 +250,7 @@
         content: @Composable RowScope.() -> Unit
     ) {
         Button(
-            colors = ButtonConstants.defaultButtonColors(
+            colors = ButtonDefaults.buttonColors(
                 backgroundColor = MaterialTheme.colors.secondary
             ),
             onClick = onClick,
diff --git a/compose/integration-tests/macrobenchmark-target/build.gradle b/compose/integration-tests/macrobenchmark-target/build.gradle
index 7666c45..666cb1c 100644
--- a/compose/integration-tests/macrobenchmark-target/build.gradle
+++ b/compose/integration-tests/macrobenchmark-target/build.gradle
@@ -9,11 +9,20 @@
     id("org.jetbrains.kotlin.android")
 }
 
+android {
+    buildTypes {
+        release {
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
+        }
+    }
+}
+
 dependencies {
     kotlinPlugin project(":compose:compiler:compiler")
 
     implementation(KOTLIN_STDLIB)
-
     implementation project(":compose:foundation:foundation-layout")
     implementation project(":compose:material:material")
     implementation project(":compose:runtime:runtime")
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyColumnActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyColumnActivity.kt
index a0461f4..d981bab 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyColumnActivity.kt
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyColumnActivity.kt
@@ -21,11 +21,15 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.lazy.LazyColumnFor
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.Card
 import androidx.compose.material.Checkbox
 import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.setContent
+import androidx.compose.ui.unit.dp
 
 class LazyColumnActivity : ComponentActivity() {
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -34,22 +38,12 @@
         val itemCount = intent.getIntExtra(EXTRA_ITEM_COUNT, 1000)
 
         setContent {
-            LazyColumnFor(
-                items = List(itemCount) {
-                    Entry("Item $it")
-                },
-                modifier = Modifier.fillMaxWidth(),
-                itemContent = { item ->
-                    Row {
-                        Text(text = item.contents)
-                        Spacer(modifier = Modifier.weight(1f, fill = true))
-                        Checkbox(checked = false, onCheckedChange = {})
-                    }
+            LazyColumn(modifier = Modifier.fillMaxWidth()) {
+                items(List(itemCount) { Entry("Item $it") }) {
+                    ListRow(it)
                 }
-            )
+            }
         }
-
-        reportFullyDrawn()
     }
 
     companion object {
@@ -57,4 +51,22 @@
     }
 }
 
+@Composable
+private fun ListRow(entry: Entry) {
+    Card(modifier = Modifier.padding(8.dp)) {
+        Row {
+            Text(
+                text = entry.contents,
+                modifier = Modifier.padding(16.dp)
+            )
+            Spacer(modifier = Modifier.weight(1f, fill = true))
+            Checkbox(
+                checked = false,
+                onCheckedChange = {},
+                modifier = Modifier.padding(16.dp)
+            )
+        }
+    }
+}
+
 data class Entry(val contents: String)
\ No newline at end of file
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/TrivialStartupActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/TrivialStartupActivity.kt
index ca32dc3..49dd58e 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/TrivialStartupActivity.kt
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/TrivialStartupActivity.kt
@@ -28,7 +28,5 @@
         setContent {
             Text("Compose Macrobenchmark Target")
         }
-
-        reportFullyDrawn()
     }
 }
\ No newline at end of file
diff --git a/compose/integration-tests/macrobenchmark/build.gradle b/compose/integration-tests/macrobenchmark/build.gradle
index 07444b7..1cf4c30 100644
--- a/compose/integration-tests/macrobenchmark/build.gradle
+++ b/compose/integration-tests/macrobenchmark/build.gradle
@@ -27,7 +27,10 @@
     id("kotlin-android")
 }
 
-android.defaultConfig.minSdkVersion 28
+android.defaultConfig {
+    minSdkVersion 28
+    testInstrumentationRunnerArgument 'androidx.benchmark.output.enable', 'true'
+}
 
 dependencies {
     androidTestImplementation(project(":benchmark:benchmark-junit4"))
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml b/compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
index 82ef367..b86f8ba 100644
--- a/compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
+++ b/compose/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
@@ -28,4 +28,5 @@
     <queries>
         <package android:name="androidx.compose.integration.macrobenchmark.target" />
     </queries>
+    <application android:requestLegacyExternalStorage="true"/>
 </manifest>
diff --git a/compose/material/material-ripple/src/commonMain/kotlin/androidx/compose/material/ripple/Ripple.kt b/compose/material/material-ripple/src/commonMain/kotlin/androidx/compose/material/ripple/Ripple.kt
index 59d79ee..5b0c252 100644
--- a/compose/material/material-ripple/src/commonMain/kotlin/androidx/compose/material/ripple/Ripple.kt
+++ b/compose/material/material-ripple/src/commonMain/kotlin/androidx/compose/material/ripple/Ripple.kt
@@ -39,11 +39,10 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.drawscope.clipRect
-import androidx.compose.ui.graphics.useOrElse
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.nativeClass
 
 /**
  * Creates and [remember]s a Ripple using values provided by [RippleTheme].
@@ -90,7 +89,7 @@
 ): Indication {
     val theme = AmbientRippleTheme.current
     val clock = AmbientAnimationClock.current.asDisposableClock()
-    val resolvedColor = color.useOrElse { theme.defaultColor() }
+    val resolvedColor = color.takeOrElse { theme.defaultColor() }
     val colorState = remember { mutableStateOf(resolvedColor, structuralEqualityPolicy()) }
     colorState.value = resolvedColor
     val rippleAlpha = theme.rippleAlpha()
@@ -137,7 +136,7 @@
 ): Indication {
     val theme = AmbientRippleTheme.current
     val clock = AmbientAnimationClock.current.asDisposableClock()
-    val resolvedColor = color.useOrElse { theme.defaultColor() }
+    val resolvedColor = color.takeOrElse { theme.defaultColor() }
     val colorState = remember { mutableStateOf(resolvedColor, structuralEqualityPolicy()) }
     colorState.value = resolvedColor
     val rippleAlpha = theme.rippleAlpha()
@@ -180,9 +179,7 @@
     // making this class to be "data class"
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (this.nativeClass() != other?.nativeClass()) return false
-
-        other as Ripple
+        if (other !is Ripple) return false
 
         if (bounded != other.bounded) return false
         if (radius != other.radius) return false
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index e7608b9..866aca3 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -12,18 +12,32 @@
     method @androidx.compose.runtime.Composable public static void TopAppBar-ye6PvEY(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional float elevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
-  public final class BackdropScaffoldConstants {
-    method public float getDefaultFrontLayerElevation-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public long getDefaultFrontLayerScrimColor-0d7_KjU();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getDefaultFrontLayerShape();
-    method public float getDefaultHeaderHeight-D9Ej5fM();
-    method public float getDefaultPeekHeight-D9Ej5fM();
+  @Deprecated public final class BackdropScaffoldConstants {
+    method @Deprecated public float getDefaultFrontLayerElevation-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultFrontLayerScrimColor-0d7_KjU();
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getDefaultFrontLayerShape();
+    method @Deprecated public float getDefaultHeaderHeight-D9Ej5fM();
+    method @Deprecated public float getDefaultPeekHeight-D9Ej5fM();
     property public final float DefaultFrontLayerElevation;
     property @androidx.compose.runtime.Composable public final long DefaultFrontLayerScrimColor;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape DefaultFrontLayerShape;
     property public final float DefaultHeaderHeight;
     property public final float DefaultPeekHeight;
-    field public static final androidx.compose.material.BackdropScaffoldConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.BackdropScaffoldConstants INSTANCE;
+  }
+
+  public final class BackdropScaffoldDefaults {
+    method public float getFrontLayerElevation-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public long getFrontLayerScrimColor-0d7_KjU();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFrontLayerShape();
+    method public float getHeaderHeight-D9Ej5fM();
+    method public float getPeekHeight-D9Ej5fM();
+    property public final float FrontLayerElevation;
+    property public final float HeaderHeight;
+    property public final float PeekHeight;
+    property @androidx.compose.runtime.Composable public final long frontLayerScrimColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape frontLayerShape;
+    field public static final androidx.compose.material.BackdropScaffoldDefaults INSTANCE;
   }
 
   public final class BackdropScaffoldKt {
@@ -82,12 +96,20 @@
     method @androidx.compose.runtime.Composable public static void BottomNavigationItem-S1l6qvI(kotlin.jvm.functions.Function0<kotlin.Unit> icon, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> label, optional boolean alwaysShowLabels, optional androidx.compose.foundation.InteractionState interactionState, optional long selectedContentColor, optional long unselectedContentColor);
   }
 
-  public final class BottomSheetScaffoldConstants {
-    method public float getDefaultSheetElevation-D9Ej5fM();
-    method public float getDefaultSheetPeekHeight-D9Ej5fM();
+  @Deprecated public final class BottomSheetScaffoldConstants {
+    method @Deprecated public float getDefaultSheetElevation-D9Ej5fM();
+    method @Deprecated public float getDefaultSheetPeekHeight-D9Ej5fM();
     property public final float DefaultSheetElevation;
     property public final float DefaultSheetPeekHeight;
-    field public static final androidx.compose.material.BottomSheetScaffoldConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.BottomSheetScaffoldConstants INSTANCE;
+  }
+
+  public final class BottomSheetScaffoldDefaults {
+    method public float getSheetElevation-D9Ej5fM();
+    method public float getSheetPeekHeight-D9Ej5fM();
+    property public final float SheetElevation;
+    property public final float SheetPeekHeight;
+    field public static final androidx.compose.material.BottomSheetScaffoldDefaults INSTANCE;
   }
 
   public final class BottomSheetScaffoldKt {
@@ -131,19 +153,19 @@
     method public long contentColor-0d7_KjU(boolean enabled);
   }
 
-  public final class ButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultButtonColors-nlx5xbs(optional long backgroundColor, optional long disabledBackgroundColor, optional long contentColor, optional long disabledContentColor);
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation defaultElevation-qYQSm_w(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation);
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultOutlinedButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultTextButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
-    method public androidx.compose.foundation.layout.PaddingValues getDefaultContentPadding();
-    method public float getDefaultIconSize-D9Ej5fM();
-    method public float getDefaultIconSpacing-D9Ej5fM();
-    method public float getDefaultMinHeight-D9Ej5fM();
-    method public float getDefaultMinWidth-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getDefaultOutlinedBorder();
-    method public androidx.compose.foundation.layout.PaddingValues getDefaultTextContentPadding();
-    method public float getOutlinedBorderSize-D9Ej5fM();
+  @Deprecated public final class ButtonConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultButtonColors-nlx5xbs(optional long backgroundColor, optional long disabledBackgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation defaultElevation-qYQSm_w(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultOutlinedButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultTextButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @Deprecated public androidx.compose.foundation.layout.PaddingValues getDefaultContentPadding();
+    method @Deprecated public float getDefaultIconSize-D9Ej5fM();
+    method @Deprecated public float getDefaultIconSpacing-D9Ej5fM();
+    method @Deprecated public float getDefaultMinHeight-D9Ej5fM();
+    method @Deprecated public float getDefaultMinWidth-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getDefaultOutlinedBorder();
+    method @Deprecated public androidx.compose.foundation.layout.PaddingValues getDefaultTextContentPadding();
+    method @Deprecated public float getOutlinedBorderSize-D9Ej5fM();
     property public final androidx.compose.foundation.layout.PaddingValues DefaultContentPadding;
     property public final float DefaultIconSize;
     property public final float DefaultIconSpacing;
@@ -152,7 +174,32 @@
     property public final androidx.compose.foundation.layout.PaddingValues DefaultTextContentPadding;
     property public final float OutlinedBorderSize;
     property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke defaultOutlinedBorder;
-    field public static final androidx.compose.material.ButtonConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.ButtonConstants INSTANCE;
+    field @Deprecated public static final float OutlinedBorderOpacity = 0.12f;
+  }
+
+  public final class ButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors buttonColors-nlx5xbs(optional long backgroundColor, optional long disabledBackgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation elevation-qYQSm_w(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation);
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method public float getIconSize-D9Ej5fM();
+    method public float getIconSpacing-D9Ej5fM();
+    method public float getMinHeight-D9Ej5fM();
+    method public float getMinWidth-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getOutlinedBorder();
+    method public float getOutlinedBorderSize-D9Ej5fM();
+    method public androidx.compose.foundation.layout.PaddingValues getTextButtonContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors outlinedButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors textButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final float IconSize;
+    property public final float IconSpacing;
+    property public final float MinHeight;
+    property public final float MinWidth;
+    property public final float OutlinedBorderSize;
+    property public final androidx.compose.foundation.layout.PaddingValues TextButtonContentPadding;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke outlinedBorder;
+    field public static final androidx.compose.material.ButtonDefaults INSTANCE;
     field public static final float OutlinedBorderOpacity = 0.12f;
   }
 
@@ -176,9 +223,14 @@
     method public long checkmarkColor-0d7_KjU(androidx.compose.ui.state.ToggleableState state);
   }
 
-  public final class CheckboxConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.CheckboxColors defaultColors-QGkLkJU(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledColor, optional long disabledIndeterminateColor);
-    field public static final androidx.compose.material.CheckboxConstants INSTANCE;
+  @Deprecated public final class CheckboxConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.CheckboxColors defaultColors-QGkLkJU(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledColor, optional long disabledIndeterminateColor);
+    field @Deprecated public static final androidx.compose.material.CheckboxConstants INSTANCE;
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.CheckboxColors colors-QGkLkJU(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledColor, optional long disabledIndeterminateColor);
+    field public static final androidx.compose.material.CheckboxDefaults INSTANCE;
   }
 
   public final class CheckboxKt {
@@ -270,13 +322,22 @@
     method @androidx.compose.runtime.Composable public static void Divider-JRSVyrs(optional androidx.compose.ui.Modifier modifier, optional long color, optional float thickness, optional float startIndent);
   }
 
-  public final class DrawerConstants {
-    method public float getDefaultElevation-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
+  @Deprecated public final class DrawerConstants {
+    method @Deprecated public float getDefaultElevation-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
     property public final float DefaultElevation;
     property @androidx.compose.runtime.Composable public final long defaultScrimColor;
-    field public static final androidx.compose.material.DrawerConstants INSTANCE;
-    field public static final float ScrimDefaultOpacity = 0.32f;
+    field @Deprecated public static final androidx.compose.material.DrawerConstants INSTANCE;
+    field @Deprecated public static final float ScrimDefaultOpacity = 0.32f;
+  }
+
+  public final class DrawerDefaults {
+    method public float getElevation-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public long getScrimColor-0d7_KjU();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable public final long scrimColor;
+    field public static final androidx.compose.material.DrawerDefaults INSTANCE;
+    field public static final float ScrimOpacity = 0.32f;
   }
 
   public final class DrawerKt {
@@ -306,10 +367,16 @@
     enum_constant public static final androidx.compose.material.DrawerValue Open;
   }
 
-  public final class ElevationConstants {
+  @Deprecated public final class ElevationConstants {
+    method @Deprecated public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? incomingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
+    method @Deprecated public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? outgoingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
+    field @Deprecated public static final androidx.compose.material.ElevationConstants INSTANCE;
+  }
+
+  public final class ElevationDefaults {
     method public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? incomingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
     method public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? outgoingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
-    field public static final androidx.compose.material.ElevationConstants INSTANCE;
+    field public static final androidx.compose.material.ElevationDefaults INSTANCE;
   }
 
   public final class ElevationKt {
@@ -356,9 +423,14 @@
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public androidx.compose.material.FixedThreshold copy-0680j_4(float offset);
   }
 
-  public final class FloatingActionButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation defaultElevation-ioHfwGI(optional float defaultElevation, optional float pressedElevation);
-    field public static final androidx.compose.material.FloatingActionButtonConstants INSTANCE;
+  @Deprecated public final class FloatingActionButtonConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation defaultElevation-ioHfwGI(optional float defaultElevation, optional float pressedElevation);
+    field @Deprecated public static final androidx.compose.material.FloatingActionButtonConstants INSTANCE;
+  }
+
+  public final class FloatingActionButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation elevation-ioHfwGI(optional float defaultElevation, optional float pressedElevation);
+    field public static final androidx.compose.material.FloatingActionButtonDefaults INSTANCE;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface FloatingActionButtonElevation {
@@ -413,12 +485,20 @@
     method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.InteractionState interactionState, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
-  public final class ModalBottomSheetConstants {
-    method public float getDefaultElevation-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
+  @Deprecated public final class ModalBottomSheetConstants {
+    method @Deprecated public float getDefaultElevation-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
     property public final float DefaultElevation;
     property @androidx.compose.runtime.Composable public final long DefaultScrimColor;
-    field public static final androidx.compose.material.ModalBottomSheetConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.ModalBottomSheetConstants INSTANCE;
+  }
+
+  public final class ModalBottomSheetDefaults {
+    method public float getElevation-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public long getScrimColor-0d7_KjU();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable public final long scrimColor;
+    field public static final androidx.compose.material.ModalBottomSheetDefaults INSTANCE;
   }
 
   public final class ModalBottomSheetKt {
@@ -450,13 +530,22 @@
     method @androidx.compose.runtime.Composable public static void OutlinedTextField-IIju55g(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isErrorValue, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onImeActionPerformed, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long activeColor, optional long inactiveColor, optional long errorColor);
   }
 
-  public final class ProgressIndicatorConstants {
-    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultProgressAnimationSpec();
-    method public float getDefaultStrokeWidth-D9Ej5fM();
+  @Deprecated public final class ProgressIndicatorConstants {
+    method @Deprecated public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultProgressAnimationSpec();
+    method @Deprecated public float getDefaultStrokeWidth-D9Ej5fM();
     property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> DefaultProgressAnimationSpec;
     property public final float DefaultStrokeWidth;
-    field public static final float DefaultIndicatorBackgroundOpacity = 0.24f;
-    field public static final androidx.compose.material.ProgressIndicatorConstants INSTANCE;
+    field @Deprecated public static final float DefaultIndicatorBackgroundOpacity = 0.24f;
+    field @Deprecated public static final androidx.compose.material.ProgressIndicatorConstants INSTANCE;
+  }
+
+  public final class ProgressIndicatorDefaults {
+    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getProgressAnimationSpec();
+    method public float getStrokeWidth-D9Ej5fM();
+    property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> ProgressAnimationSpec;
+    property public final float StrokeWidth;
+    field public static final androidx.compose.material.ProgressIndicatorDefaults INSTANCE;
+    field public static final float IndicatorBackgroundOpacity = 0.24f;
   }
 
   public final class ProgressIndicatorKt {
@@ -470,9 +559,14 @@
     method public long radioColor-0d7_KjU(boolean enabled, boolean selected);
   }
 
-  public final class RadioButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.RadioButtonColors defaultColors-xS_xkl8(optional long selectedColor, optional long unselectedColor, optional long disabledColor);
-    field public static final androidx.compose.material.RadioButtonConstants INSTANCE;
+  @Deprecated public final class RadioButtonConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.RadioButtonColors defaultColors-xS_xkl8(optional long selectedColor, optional long unselectedColor, optional long disabledColor);
+    field @Deprecated public static final androidx.compose.material.RadioButtonConstants INSTANCE;
+  }
+
+  public final class RadioButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.RadioButtonColors colors-xS_xkl8(optional long selectedColor, optional long unselectedColor, optional long disabledColor);
+    field public static final androidx.compose.material.RadioButtonDefaults INSTANCE;
   }
 
   public final class RadioButtonKt {
@@ -525,8 +619,14 @@
   public final class ShapesKt {
   }
 
-  public final class SliderConstants {
-    field public static final androidx.compose.material.SliderConstants INSTANCE;
+  @Deprecated public final class SliderConstants {
+    field @Deprecated public static final androidx.compose.material.SliderConstants INSTANCE;
+    field @Deprecated public static final float InactiveTrackColorAlpha = 0.24f;
+    field @Deprecated public static final float TickColorAlpha = 0.54f;
+  }
+
+  public final class SliderDefaults {
+    field public static final androidx.compose.material.SliderDefaults INSTANCE;
     field public static final float InactiveTrackColorAlpha = 0.24f;
     field public static final float TickColorAlpha = 0.54f;
   }
@@ -535,12 +635,12 @@
     method @androidx.compose.runtime.Composable public static void Slider-B7rb6FQ(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit> onValueChangeEnd, optional androidx.compose.foundation.InteractionState interactionState, optional long thumbColor, optional long activeTrackColor, optional long inactiveTrackColor, optional long activeTickColor, optional long inactiveTickColor);
   }
 
-  public final class SnackbarConstants {
-    method @androidx.compose.runtime.Composable public long getDefaultActionPrimaryColor-0d7_KjU();
-    method @androidx.compose.runtime.Composable public long getDefaultBackgroundColor-0d7_KjU();
+  @Deprecated public final class SnackbarConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultActionPrimaryColor-0d7_KjU();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultBackgroundColor-0d7_KjU();
     property @androidx.compose.runtime.Composable public final long defaultActionPrimaryColor;
     property @androidx.compose.runtime.Composable public final long defaultBackgroundColor;
-    field public static final androidx.compose.material.SnackbarConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.SnackbarConstants INSTANCE;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi public interface SnackbarData {
@@ -554,6 +654,14 @@
     property public abstract String message;
   }
 
+  public final class SnackbarDefaults {
+    method @androidx.compose.runtime.Composable public long getBackgroundColor-0d7_KjU();
+    method @androidx.compose.runtime.Composable public long getPrimaryActionColor-0d7_KjU();
+    property @androidx.compose.runtime.Composable public final long backgroundColor;
+    property @androidx.compose.runtime.Composable public final long primaryActionColor;
+    field public static final androidx.compose.material.SnackbarDefaults INSTANCE;
+  }
+
   public enum SnackbarDuration {
     enum_constant public static final androidx.compose.material.SnackbarDuration Indefinite;
     enum_constant public static final androidx.compose.material.SnackbarDuration Long;
@@ -605,13 +713,24 @@
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.DismissState rememberDismissState(optional androidx.compose.material.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
   }
 
-  public final class SwipeableConstants {
-    method public androidx.compose.material.ResistanceConfig? defaultResistanceConfig(java.util.Set<java.lang.Float> anchors, optional float factorAtMin, optional float factorAtMax);
-    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultAnimationSpec();
-    method public float getDefaultVelocityThreshold-D9Ej5fM();
+  @Deprecated public final class SwipeableConstants {
+    method @Deprecated public androidx.compose.material.ResistanceConfig? defaultResistanceConfig(java.util.Set<java.lang.Float> anchors, optional float factorAtMin, optional float factorAtMax);
+    method @Deprecated public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultAnimationSpec();
+    method @Deprecated public float getDefaultVelocityThreshold-D9Ej5fM();
     property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> DefaultAnimationSpec;
     property public final float DefaultVelocityThreshold;
-    field public static final androidx.compose.material.SwipeableConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.SwipeableConstants INSTANCE;
+    field @Deprecated public static final float StandardResistanceFactor = 10.0f;
+    field @Deprecated public static final float StiffResistanceFactor = 20.0f;
+  }
+
+  public final class SwipeableDefaults {
+    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getAnimationSpec();
+    method public float getVelocityThreshold-D9Ej5fM();
+    method public androidx.compose.material.ResistanceConfig? resistanceConfig(java.util.Set<java.lang.Float> anchors, optional float factorAtMin, optional float factorAtMax);
+    property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> AnimationSpec;
+    property public final float VelocityThreshold;
+    field public static final androidx.compose.material.SwipeableDefaults INSTANCE;
     field public static final float StandardResistanceFactor = 10.0f;
     field public static final float StiffResistanceFactor = 20.0f;
   }
@@ -631,6 +750,8 @@
     method public final T! getTargetValue();
     method public final T! getValue();
     method public final boolean isAnimationRunning();
+    method public final float performDrag(float delta);
+    method public final void performFling(float velocity, kotlin.jvm.functions.Function0<kotlin.Unit> onEnd);
     method @androidx.compose.material.ExperimentalMaterialApi public final void snapTo(T? targetValue);
     property public final float direction;
     property public final boolean isAnimationRunning;
@@ -651,27 +772,46 @@
     method public long trackColor-0d7_KjU(boolean enabled, boolean checked);
   }
 
-  public final class SwitchConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.SwitchColors defaultColors-R8aI8sA(optional long checkedThumbColor, optional long checkedTrackColor, optional float checkedTrackAlpha, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional float uncheckedTrackAlpha, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
-    field public static final androidx.compose.material.SwitchConstants INSTANCE;
+  @Deprecated public final class SwitchConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.SwitchColors defaultColors-R8aI8sA(optional long checkedThumbColor, optional long checkedTrackColor, optional float checkedTrackAlpha, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional float uncheckedTrackAlpha, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
+    field @Deprecated public static final androidx.compose.material.SwitchConstants INSTANCE;
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.SwitchColors colors-R8aI8sA(optional long checkedThumbColor, optional long checkedTrackColor, optional float checkedTrackAlpha, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional float uncheckedTrackAlpha, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
+    field public static final androidx.compose.material.SwitchDefaults INSTANCE;
   }
 
   public final class SwitchKt {
     method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.InteractionState interactionState, optional androidx.compose.material.SwitchColors colors);
   }
 
-  public final class TabConstants {
-    method @androidx.compose.runtime.Composable public void DefaultDivider-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
-    method @androidx.compose.runtime.Composable public void DefaultIndicator-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
-    method public androidx.compose.ui.Modifier defaultTabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material.TabPosition currentTabPosition);
-    method public float getDefaultDividerThickness-D9Ej5fM();
-    method public float getDefaultIndicatorHeight-D9Ej5fM();
-    method public float getDefaultScrollableTabRowPadding-D9Ej5fM();
+  @Deprecated public final class TabConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public void DefaultDivider-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+    method @Deprecated @androidx.compose.runtime.Composable public void DefaultIndicator-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @Deprecated public androidx.compose.ui.Modifier defaultTabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material.TabPosition currentTabPosition);
+    method @Deprecated public float getDefaultDividerThickness-D9Ej5fM();
+    method @Deprecated public float getDefaultIndicatorHeight-D9Ej5fM();
+    method @Deprecated public float getDefaultScrollableTabRowPadding-D9Ej5fM();
     property public final float DefaultDividerThickness;
     property public final float DefaultIndicatorHeight;
     property public final float DefaultScrollableTabRowPadding;
-    field public static final float DefaultDividerOpacity = 0.12f;
-    field public static final androidx.compose.material.TabConstants INSTANCE;
+    field @Deprecated public static final float DefaultDividerOpacity = 0.12f;
+    field @Deprecated public static final androidx.compose.material.TabConstants INSTANCE;
+  }
+
+  public final class TabDefaults {
+    method @androidx.compose.runtime.Composable public void Divider-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+    method @androidx.compose.runtime.Composable public void Indicator-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method public float getDividerThickness-D9Ej5fM();
+    method public float getIndicatorHeight-D9Ej5fM();
+    method public float getScrollableTabRowPadding-D9Ej5fM();
+    method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material.TabPosition currentTabPosition);
+    property public final float DividerThickness;
+    property public final float IndicatorHeight;
+    property public final float ScrollableTabRowPadding;
+    field public static final float DividerOpacity = 0.12f;
+    field public static final androidx.compose.material.TabDefaults INSTANCE;
   }
 
   public final class TabKt {
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index e7608b9..866aca3 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -12,18 +12,32 @@
     method @androidx.compose.runtime.Composable public static void TopAppBar-ye6PvEY(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional float elevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
-  public final class BackdropScaffoldConstants {
-    method public float getDefaultFrontLayerElevation-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public long getDefaultFrontLayerScrimColor-0d7_KjU();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getDefaultFrontLayerShape();
-    method public float getDefaultHeaderHeight-D9Ej5fM();
-    method public float getDefaultPeekHeight-D9Ej5fM();
+  @Deprecated public final class BackdropScaffoldConstants {
+    method @Deprecated public float getDefaultFrontLayerElevation-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultFrontLayerScrimColor-0d7_KjU();
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getDefaultFrontLayerShape();
+    method @Deprecated public float getDefaultHeaderHeight-D9Ej5fM();
+    method @Deprecated public float getDefaultPeekHeight-D9Ej5fM();
     property public final float DefaultFrontLayerElevation;
     property @androidx.compose.runtime.Composable public final long DefaultFrontLayerScrimColor;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape DefaultFrontLayerShape;
     property public final float DefaultHeaderHeight;
     property public final float DefaultPeekHeight;
-    field public static final androidx.compose.material.BackdropScaffoldConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.BackdropScaffoldConstants INSTANCE;
+  }
+
+  public final class BackdropScaffoldDefaults {
+    method public float getFrontLayerElevation-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public long getFrontLayerScrimColor-0d7_KjU();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFrontLayerShape();
+    method public float getHeaderHeight-D9Ej5fM();
+    method public float getPeekHeight-D9Ej5fM();
+    property public final float FrontLayerElevation;
+    property public final float HeaderHeight;
+    property public final float PeekHeight;
+    property @androidx.compose.runtime.Composable public final long frontLayerScrimColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape frontLayerShape;
+    field public static final androidx.compose.material.BackdropScaffoldDefaults INSTANCE;
   }
 
   public final class BackdropScaffoldKt {
@@ -82,12 +96,20 @@
     method @androidx.compose.runtime.Composable public static void BottomNavigationItem-S1l6qvI(kotlin.jvm.functions.Function0<kotlin.Unit> icon, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> label, optional boolean alwaysShowLabels, optional androidx.compose.foundation.InteractionState interactionState, optional long selectedContentColor, optional long unselectedContentColor);
   }
 
-  public final class BottomSheetScaffoldConstants {
-    method public float getDefaultSheetElevation-D9Ej5fM();
-    method public float getDefaultSheetPeekHeight-D9Ej5fM();
+  @Deprecated public final class BottomSheetScaffoldConstants {
+    method @Deprecated public float getDefaultSheetElevation-D9Ej5fM();
+    method @Deprecated public float getDefaultSheetPeekHeight-D9Ej5fM();
     property public final float DefaultSheetElevation;
     property public final float DefaultSheetPeekHeight;
-    field public static final androidx.compose.material.BottomSheetScaffoldConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.BottomSheetScaffoldConstants INSTANCE;
+  }
+
+  public final class BottomSheetScaffoldDefaults {
+    method public float getSheetElevation-D9Ej5fM();
+    method public float getSheetPeekHeight-D9Ej5fM();
+    property public final float SheetElevation;
+    property public final float SheetPeekHeight;
+    field public static final androidx.compose.material.BottomSheetScaffoldDefaults INSTANCE;
   }
 
   public final class BottomSheetScaffoldKt {
@@ -131,19 +153,19 @@
     method public long contentColor-0d7_KjU(boolean enabled);
   }
 
-  public final class ButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultButtonColors-nlx5xbs(optional long backgroundColor, optional long disabledBackgroundColor, optional long contentColor, optional long disabledContentColor);
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation defaultElevation-qYQSm_w(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation);
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultOutlinedButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultTextButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
-    method public androidx.compose.foundation.layout.PaddingValues getDefaultContentPadding();
-    method public float getDefaultIconSize-D9Ej5fM();
-    method public float getDefaultIconSpacing-D9Ej5fM();
-    method public float getDefaultMinHeight-D9Ej5fM();
-    method public float getDefaultMinWidth-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getDefaultOutlinedBorder();
-    method public androidx.compose.foundation.layout.PaddingValues getDefaultTextContentPadding();
-    method public float getOutlinedBorderSize-D9Ej5fM();
+  @Deprecated public final class ButtonConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultButtonColors-nlx5xbs(optional long backgroundColor, optional long disabledBackgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation defaultElevation-qYQSm_w(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultOutlinedButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultTextButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @Deprecated public androidx.compose.foundation.layout.PaddingValues getDefaultContentPadding();
+    method @Deprecated public float getDefaultIconSize-D9Ej5fM();
+    method @Deprecated public float getDefaultIconSpacing-D9Ej5fM();
+    method @Deprecated public float getDefaultMinHeight-D9Ej5fM();
+    method @Deprecated public float getDefaultMinWidth-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getDefaultOutlinedBorder();
+    method @Deprecated public androidx.compose.foundation.layout.PaddingValues getDefaultTextContentPadding();
+    method @Deprecated public float getOutlinedBorderSize-D9Ej5fM();
     property public final androidx.compose.foundation.layout.PaddingValues DefaultContentPadding;
     property public final float DefaultIconSize;
     property public final float DefaultIconSpacing;
@@ -152,7 +174,32 @@
     property public final androidx.compose.foundation.layout.PaddingValues DefaultTextContentPadding;
     property public final float OutlinedBorderSize;
     property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke defaultOutlinedBorder;
-    field public static final androidx.compose.material.ButtonConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.ButtonConstants INSTANCE;
+    field @Deprecated public static final float OutlinedBorderOpacity = 0.12f;
+  }
+
+  public final class ButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors buttonColors-nlx5xbs(optional long backgroundColor, optional long disabledBackgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation elevation-qYQSm_w(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation);
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method public float getIconSize-D9Ej5fM();
+    method public float getIconSpacing-D9Ej5fM();
+    method public float getMinHeight-D9Ej5fM();
+    method public float getMinWidth-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getOutlinedBorder();
+    method public float getOutlinedBorderSize-D9Ej5fM();
+    method public androidx.compose.foundation.layout.PaddingValues getTextButtonContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors outlinedButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors textButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final float IconSize;
+    property public final float IconSpacing;
+    property public final float MinHeight;
+    property public final float MinWidth;
+    property public final float OutlinedBorderSize;
+    property public final androidx.compose.foundation.layout.PaddingValues TextButtonContentPadding;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke outlinedBorder;
+    field public static final androidx.compose.material.ButtonDefaults INSTANCE;
     field public static final float OutlinedBorderOpacity = 0.12f;
   }
 
@@ -176,9 +223,14 @@
     method public long checkmarkColor-0d7_KjU(androidx.compose.ui.state.ToggleableState state);
   }
 
-  public final class CheckboxConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.CheckboxColors defaultColors-QGkLkJU(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledColor, optional long disabledIndeterminateColor);
-    field public static final androidx.compose.material.CheckboxConstants INSTANCE;
+  @Deprecated public final class CheckboxConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.CheckboxColors defaultColors-QGkLkJU(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledColor, optional long disabledIndeterminateColor);
+    field @Deprecated public static final androidx.compose.material.CheckboxConstants INSTANCE;
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.CheckboxColors colors-QGkLkJU(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledColor, optional long disabledIndeterminateColor);
+    field public static final androidx.compose.material.CheckboxDefaults INSTANCE;
   }
 
   public final class CheckboxKt {
@@ -270,13 +322,22 @@
     method @androidx.compose.runtime.Composable public static void Divider-JRSVyrs(optional androidx.compose.ui.Modifier modifier, optional long color, optional float thickness, optional float startIndent);
   }
 
-  public final class DrawerConstants {
-    method public float getDefaultElevation-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
+  @Deprecated public final class DrawerConstants {
+    method @Deprecated public float getDefaultElevation-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
     property public final float DefaultElevation;
     property @androidx.compose.runtime.Composable public final long defaultScrimColor;
-    field public static final androidx.compose.material.DrawerConstants INSTANCE;
-    field public static final float ScrimDefaultOpacity = 0.32f;
+    field @Deprecated public static final androidx.compose.material.DrawerConstants INSTANCE;
+    field @Deprecated public static final float ScrimDefaultOpacity = 0.32f;
+  }
+
+  public final class DrawerDefaults {
+    method public float getElevation-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public long getScrimColor-0d7_KjU();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable public final long scrimColor;
+    field public static final androidx.compose.material.DrawerDefaults INSTANCE;
+    field public static final float ScrimOpacity = 0.32f;
   }
 
   public final class DrawerKt {
@@ -306,10 +367,16 @@
     enum_constant public static final androidx.compose.material.DrawerValue Open;
   }
 
-  public final class ElevationConstants {
+  @Deprecated public final class ElevationConstants {
+    method @Deprecated public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? incomingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
+    method @Deprecated public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? outgoingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
+    field @Deprecated public static final androidx.compose.material.ElevationConstants INSTANCE;
+  }
+
+  public final class ElevationDefaults {
     method public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? incomingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
     method public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? outgoingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
-    field public static final androidx.compose.material.ElevationConstants INSTANCE;
+    field public static final androidx.compose.material.ElevationDefaults INSTANCE;
   }
 
   public final class ElevationKt {
@@ -356,9 +423,14 @@
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public androidx.compose.material.FixedThreshold copy-0680j_4(float offset);
   }
 
-  public final class FloatingActionButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation defaultElevation-ioHfwGI(optional float defaultElevation, optional float pressedElevation);
-    field public static final androidx.compose.material.FloatingActionButtonConstants INSTANCE;
+  @Deprecated public final class FloatingActionButtonConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation defaultElevation-ioHfwGI(optional float defaultElevation, optional float pressedElevation);
+    field @Deprecated public static final androidx.compose.material.FloatingActionButtonConstants INSTANCE;
+  }
+
+  public final class FloatingActionButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation elevation-ioHfwGI(optional float defaultElevation, optional float pressedElevation);
+    field public static final androidx.compose.material.FloatingActionButtonDefaults INSTANCE;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface FloatingActionButtonElevation {
@@ -413,12 +485,20 @@
     method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.InteractionState interactionState, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
-  public final class ModalBottomSheetConstants {
-    method public float getDefaultElevation-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
+  @Deprecated public final class ModalBottomSheetConstants {
+    method @Deprecated public float getDefaultElevation-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
     property public final float DefaultElevation;
     property @androidx.compose.runtime.Composable public final long DefaultScrimColor;
-    field public static final androidx.compose.material.ModalBottomSheetConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.ModalBottomSheetConstants INSTANCE;
+  }
+
+  public final class ModalBottomSheetDefaults {
+    method public float getElevation-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public long getScrimColor-0d7_KjU();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable public final long scrimColor;
+    field public static final androidx.compose.material.ModalBottomSheetDefaults INSTANCE;
   }
 
   public final class ModalBottomSheetKt {
@@ -450,13 +530,22 @@
     method @androidx.compose.runtime.Composable public static void OutlinedTextField-IIju55g(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isErrorValue, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onImeActionPerformed, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long activeColor, optional long inactiveColor, optional long errorColor);
   }
 
-  public final class ProgressIndicatorConstants {
-    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultProgressAnimationSpec();
-    method public float getDefaultStrokeWidth-D9Ej5fM();
+  @Deprecated public final class ProgressIndicatorConstants {
+    method @Deprecated public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultProgressAnimationSpec();
+    method @Deprecated public float getDefaultStrokeWidth-D9Ej5fM();
     property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> DefaultProgressAnimationSpec;
     property public final float DefaultStrokeWidth;
-    field public static final float DefaultIndicatorBackgroundOpacity = 0.24f;
-    field public static final androidx.compose.material.ProgressIndicatorConstants INSTANCE;
+    field @Deprecated public static final float DefaultIndicatorBackgroundOpacity = 0.24f;
+    field @Deprecated public static final androidx.compose.material.ProgressIndicatorConstants INSTANCE;
+  }
+
+  public final class ProgressIndicatorDefaults {
+    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getProgressAnimationSpec();
+    method public float getStrokeWidth-D9Ej5fM();
+    property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> ProgressAnimationSpec;
+    property public final float StrokeWidth;
+    field public static final androidx.compose.material.ProgressIndicatorDefaults INSTANCE;
+    field public static final float IndicatorBackgroundOpacity = 0.24f;
   }
 
   public final class ProgressIndicatorKt {
@@ -470,9 +559,14 @@
     method public long radioColor-0d7_KjU(boolean enabled, boolean selected);
   }
 
-  public final class RadioButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.RadioButtonColors defaultColors-xS_xkl8(optional long selectedColor, optional long unselectedColor, optional long disabledColor);
-    field public static final androidx.compose.material.RadioButtonConstants INSTANCE;
+  @Deprecated public final class RadioButtonConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.RadioButtonColors defaultColors-xS_xkl8(optional long selectedColor, optional long unselectedColor, optional long disabledColor);
+    field @Deprecated public static final androidx.compose.material.RadioButtonConstants INSTANCE;
+  }
+
+  public final class RadioButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.RadioButtonColors colors-xS_xkl8(optional long selectedColor, optional long unselectedColor, optional long disabledColor);
+    field public static final androidx.compose.material.RadioButtonDefaults INSTANCE;
   }
 
   public final class RadioButtonKt {
@@ -525,8 +619,14 @@
   public final class ShapesKt {
   }
 
-  public final class SliderConstants {
-    field public static final androidx.compose.material.SliderConstants INSTANCE;
+  @Deprecated public final class SliderConstants {
+    field @Deprecated public static final androidx.compose.material.SliderConstants INSTANCE;
+    field @Deprecated public static final float InactiveTrackColorAlpha = 0.24f;
+    field @Deprecated public static final float TickColorAlpha = 0.54f;
+  }
+
+  public final class SliderDefaults {
+    field public static final androidx.compose.material.SliderDefaults INSTANCE;
     field public static final float InactiveTrackColorAlpha = 0.24f;
     field public static final float TickColorAlpha = 0.54f;
   }
@@ -535,12 +635,12 @@
     method @androidx.compose.runtime.Composable public static void Slider-B7rb6FQ(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit> onValueChangeEnd, optional androidx.compose.foundation.InteractionState interactionState, optional long thumbColor, optional long activeTrackColor, optional long inactiveTrackColor, optional long activeTickColor, optional long inactiveTickColor);
   }
 
-  public final class SnackbarConstants {
-    method @androidx.compose.runtime.Composable public long getDefaultActionPrimaryColor-0d7_KjU();
-    method @androidx.compose.runtime.Composable public long getDefaultBackgroundColor-0d7_KjU();
+  @Deprecated public final class SnackbarConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultActionPrimaryColor-0d7_KjU();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultBackgroundColor-0d7_KjU();
     property @androidx.compose.runtime.Composable public final long defaultActionPrimaryColor;
     property @androidx.compose.runtime.Composable public final long defaultBackgroundColor;
-    field public static final androidx.compose.material.SnackbarConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.SnackbarConstants INSTANCE;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi public interface SnackbarData {
@@ -554,6 +654,14 @@
     property public abstract String message;
   }
 
+  public final class SnackbarDefaults {
+    method @androidx.compose.runtime.Composable public long getBackgroundColor-0d7_KjU();
+    method @androidx.compose.runtime.Composable public long getPrimaryActionColor-0d7_KjU();
+    property @androidx.compose.runtime.Composable public final long backgroundColor;
+    property @androidx.compose.runtime.Composable public final long primaryActionColor;
+    field public static final androidx.compose.material.SnackbarDefaults INSTANCE;
+  }
+
   public enum SnackbarDuration {
     enum_constant public static final androidx.compose.material.SnackbarDuration Indefinite;
     enum_constant public static final androidx.compose.material.SnackbarDuration Long;
@@ -605,13 +713,24 @@
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.DismissState rememberDismissState(optional androidx.compose.material.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
   }
 
-  public final class SwipeableConstants {
-    method public androidx.compose.material.ResistanceConfig? defaultResistanceConfig(java.util.Set<java.lang.Float> anchors, optional float factorAtMin, optional float factorAtMax);
-    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultAnimationSpec();
-    method public float getDefaultVelocityThreshold-D9Ej5fM();
+  @Deprecated public final class SwipeableConstants {
+    method @Deprecated public androidx.compose.material.ResistanceConfig? defaultResistanceConfig(java.util.Set<java.lang.Float> anchors, optional float factorAtMin, optional float factorAtMax);
+    method @Deprecated public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultAnimationSpec();
+    method @Deprecated public float getDefaultVelocityThreshold-D9Ej5fM();
     property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> DefaultAnimationSpec;
     property public final float DefaultVelocityThreshold;
-    field public static final androidx.compose.material.SwipeableConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.SwipeableConstants INSTANCE;
+    field @Deprecated public static final float StandardResistanceFactor = 10.0f;
+    field @Deprecated public static final float StiffResistanceFactor = 20.0f;
+  }
+
+  public final class SwipeableDefaults {
+    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getAnimationSpec();
+    method public float getVelocityThreshold-D9Ej5fM();
+    method public androidx.compose.material.ResistanceConfig? resistanceConfig(java.util.Set<java.lang.Float> anchors, optional float factorAtMin, optional float factorAtMax);
+    property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> AnimationSpec;
+    property public final float VelocityThreshold;
+    field public static final androidx.compose.material.SwipeableDefaults INSTANCE;
     field public static final float StandardResistanceFactor = 10.0f;
     field public static final float StiffResistanceFactor = 20.0f;
   }
@@ -631,6 +750,8 @@
     method public final T! getTargetValue();
     method public final T! getValue();
     method public final boolean isAnimationRunning();
+    method public final float performDrag(float delta);
+    method public final void performFling(float velocity, kotlin.jvm.functions.Function0<kotlin.Unit> onEnd);
     method @androidx.compose.material.ExperimentalMaterialApi public final void snapTo(T? targetValue);
     property public final float direction;
     property public final boolean isAnimationRunning;
@@ -651,27 +772,46 @@
     method public long trackColor-0d7_KjU(boolean enabled, boolean checked);
   }
 
-  public final class SwitchConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.SwitchColors defaultColors-R8aI8sA(optional long checkedThumbColor, optional long checkedTrackColor, optional float checkedTrackAlpha, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional float uncheckedTrackAlpha, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
-    field public static final androidx.compose.material.SwitchConstants INSTANCE;
+  @Deprecated public final class SwitchConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.SwitchColors defaultColors-R8aI8sA(optional long checkedThumbColor, optional long checkedTrackColor, optional float checkedTrackAlpha, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional float uncheckedTrackAlpha, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
+    field @Deprecated public static final androidx.compose.material.SwitchConstants INSTANCE;
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.SwitchColors colors-R8aI8sA(optional long checkedThumbColor, optional long checkedTrackColor, optional float checkedTrackAlpha, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional float uncheckedTrackAlpha, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
+    field public static final androidx.compose.material.SwitchDefaults INSTANCE;
   }
 
   public final class SwitchKt {
     method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.InteractionState interactionState, optional androidx.compose.material.SwitchColors colors);
   }
 
-  public final class TabConstants {
-    method @androidx.compose.runtime.Composable public void DefaultDivider-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
-    method @androidx.compose.runtime.Composable public void DefaultIndicator-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
-    method public androidx.compose.ui.Modifier defaultTabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material.TabPosition currentTabPosition);
-    method public float getDefaultDividerThickness-D9Ej5fM();
-    method public float getDefaultIndicatorHeight-D9Ej5fM();
-    method public float getDefaultScrollableTabRowPadding-D9Ej5fM();
+  @Deprecated public final class TabConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public void DefaultDivider-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+    method @Deprecated @androidx.compose.runtime.Composable public void DefaultIndicator-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @Deprecated public androidx.compose.ui.Modifier defaultTabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material.TabPosition currentTabPosition);
+    method @Deprecated public float getDefaultDividerThickness-D9Ej5fM();
+    method @Deprecated public float getDefaultIndicatorHeight-D9Ej5fM();
+    method @Deprecated public float getDefaultScrollableTabRowPadding-D9Ej5fM();
     property public final float DefaultDividerThickness;
     property public final float DefaultIndicatorHeight;
     property public final float DefaultScrollableTabRowPadding;
-    field public static final float DefaultDividerOpacity = 0.12f;
-    field public static final androidx.compose.material.TabConstants INSTANCE;
+    field @Deprecated public static final float DefaultDividerOpacity = 0.12f;
+    field @Deprecated public static final androidx.compose.material.TabConstants INSTANCE;
+  }
+
+  public final class TabDefaults {
+    method @androidx.compose.runtime.Composable public void Divider-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+    method @androidx.compose.runtime.Composable public void Indicator-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method public float getDividerThickness-D9Ej5fM();
+    method public float getIndicatorHeight-D9Ej5fM();
+    method public float getScrollableTabRowPadding-D9Ej5fM();
+    method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material.TabPosition currentTabPosition);
+    property public final float DividerThickness;
+    property public final float IndicatorHeight;
+    property public final float ScrollableTabRowPadding;
+    field public static final float DividerOpacity = 0.12f;
+    field public static final androidx.compose.material.TabDefaults INSTANCE;
   }
 
   public final class TabKt {
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index e7608b9..866aca3 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -12,18 +12,32 @@
     method @androidx.compose.runtime.Composable public static void TopAppBar-ye6PvEY(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional float elevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
-  public final class BackdropScaffoldConstants {
-    method public float getDefaultFrontLayerElevation-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public long getDefaultFrontLayerScrimColor-0d7_KjU();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getDefaultFrontLayerShape();
-    method public float getDefaultHeaderHeight-D9Ej5fM();
-    method public float getDefaultPeekHeight-D9Ej5fM();
+  @Deprecated public final class BackdropScaffoldConstants {
+    method @Deprecated public float getDefaultFrontLayerElevation-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultFrontLayerScrimColor-0d7_KjU();
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getDefaultFrontLayerShape();
+    method @Deprecated public float getDefaultHeaderHeight-D9Ej5fM();
+    method @Deprecated public float getDefaultPeekHeight-D9Ej5fM();
     property public final float DefaultFrontLayerElevation;
     property @androidx.compose.runtime.Composable public final long DefaultFrontLayerScrimColor;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape DefaultFrontLayerShape;
     property public final float DefaultHeaderHeight;
     property public final float DefaultPeekHeight;
-    field public static final androidx.compose.material.BackdropScaffoldConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.BackdropScaffoldConstants INSTANCE;
+  }
+
+  public final class BackdropScaffoldDefaults {
+    method public float getFrontLayerElevation-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public long getFrontLayerScrimColor-0d7_KjU();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFrontLayerShape();
+    method public float getHeaderHeight-D9Ej5fM();
+    method public float getPeekHeight-D9Ej5fM();
+    property public final float FrontLayerElevation;
+    property public final float HeaderHeight;
+    property public final float PeekHeight;
+    property @androidx.compose.runtime.Composable public final long frontLayerScrimColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape frontLayerShape;
+    field public static final androidx.compose.material.BackdropScaffoldDefaults INSTANCE;
   }
 
   public final class BackdropScaffoldKt {
@@ -82,12 +96,20 @@
     method @androidx.compose.runtime.Composable public static void BottomNavigationItem-S1l6qvI(kotlin.jvm.functions.Function0<kotlin.Unit> icon, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> label, optional boolean alwaysShowLabels, optional androidx.compose.foundation.InteractionState interactionState, optional long selectedContentColor, optional long unselectedContentColor);
   }
 
-  public final class BottomSheetScaffoldConstants {
-    method public float getDefaultSheetElevation-D9Ej5fM();
-    method public float getDefaultSheetPeekHeight-D9Ej5fM();
+  @Deprecated public final class BottomSheetScaffoldConstants {
+    method @Deprecated public float getDefaultSheetElevation-D9Ej5fM();
+    method @Deprecated public float getDefaultSheetPeekHeight-D9Ej5fM();
     property public final float DefaultSheetElevation;
     property public final float DefaultSheetPeekHeight;
-    field public static final androidx.compose.material.BottomSheetScaffoldConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.BottomSheetScaffoldConstants INSTANCE;
+  }
+
+  public final class BottomSheetScaffoldDefaults {
+    method public float getSheetElevation-D9Ej5fM();
+    method public float getSheetPeekHeight-D9Ej5fM();
+    property public final float SheetElevation;
+    property public final float SheetPeekHeight;
+    field public static final androidx.compose.material.BottomSheetScaffoldDefaults INSTANCE;
   }
 
   public final class BottomSheetScaffoldKt {
@@ -131,19 +153,19 @@
     method public long contentColor-0d7_KjU(boolean enabled);
   }
 
-  public final class ButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultButtonColors-nlx5xbs(optional long backgroundColor, optional long disabledBackgroundColor, optional long contentColor, optional long disabledContentColor);
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation defaultElevation-qYQSm_w(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation);
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultOutlinedButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
-    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultTextButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
-    method public androidx.compose.foundation.layout.PaddingValues getDefaultContentPadding();
-    method public float getDefaultIconSize-D9Ej5fM();
-    method public float getDefaultIconSpacing-D9Ej5fM();
-    method public float getDefaultMinHeight-D9Ej5fM();
-    method public float getDefaultMinWidth-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getDefaultOutlinedBorder();
-    method public androidx.compose.foundation.layout.PaddingValues getDefaultTextContentPadding();
-    method public float getOutlinedBorderSize-D9Ej5fM();
+  @Deprecated public final class ButtonConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultButtonColors-nlx5xbs(optional long backgroundColor, optional long disabledBackgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation defaultElevation-qYQSm_w(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultOutlinedButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors defaultTextButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @Deprecated public androidx.compose.foundation.layout.PaddingValues getDefaultContentPadding();
+    method @Deprecated public float getDefaultIconSize-D9Ej5fM();
+    method @Deprecated public float getDefaultIconSpacing-D9Ej5fM();
+    method @Deprecated public float getDefaultMinHeight-D9Ej5fM();
+    method @Deprecated public float getDefaultMinWidth-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getDefaultOutlinedBorder();
+    method @Deprecated public androidx.compose.foundation.layout.PaddingValues getDefaultTextContentPadding();
+    method @Deprecated public float getOutlinedBorderSize-D9Ej5fM();
     property public final androidx.compose.foundation.layout.PaddingValues DefaultContentPadding;
     property public final float DefaultIconSize;
     property public final float DefaultIconSpacing;
@@ -152,7 +174,32 @@
     property public final androidx.compose.foundation.layout.PaddingValues DefaultTextContentPadding;
     property public final float OutlinedBorderSize;
     property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke defaultOutlinedBorder;
-    field public static final androidx.compose.material.ButtonConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.ButtonConstants INSTANCE;
+    field @Deprecated public static final float OutlinedBorderOpacity = 0.12f;
+  }
+
+  public final class ButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors buttonColors-nlx5xbs(optional long backgroundColor, optional long disabledBackgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation elevation-qYQSm_w(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation);
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method public float getIconSize-D9Ej5fM();
+    method public float getIconSpacing-D9Ej5fM();
+    method public float getMinHeight-D9Ej5fM();
+    method public float getMinWidth-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getOutlinedBorder();
+    method public float getOutlinedBorderSize-D9Ej5fM();
+    method public androidx.compose.foundation.layout.PaddingValues getTextButtonContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors outlinedButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors textButtonColors-xS_xkl8(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final float IconSize;
+    property public final float IconSpacing;
+    property public final float MinHeight;
+    property public final float MinWidth;
+    property public final float OutlinedBorderSize;
+    property public final androidx.compose.foundation.layout.PaddingValues TextButtonContentPadding;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke outlinedBorder;
+    field public static final androidx.compose.material.ButtonDefaults INSTANCE;
     field public static final float OutlinedBorderOpacity = 0.12f;
   }
 
@@ -176,9 +223,14 @@
     method public long checkmarkColor-0d7_KjU(androidx.compose.ui.state.ToggleableState state);
   }
 
-  public final class CheckboxConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.CheckboxColors defaultColors-QGkLkJU(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledColor, optional long disabledIndeterminateColor);
-    field public static final androidx.compose.material.CheckboxConstants INSTANCE;
+  @Deprecated public final class CheckboxConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.CheckboxColors defaultColors-QGkLkJU(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledColor, optional long disabledIndeterminateColor);
+    field @Deprecated public static final androidx.compose.material.CheckboxConstants INSTANCE;
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.CheckboxColors colors-QGkLkJU(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledColor, optional long disabledIndeterminateColor);
+    field public static final androidx.compose.material.CheckboxDefaults INSTANCE;
   }
 
   public final class CheckboxKt {
@@ -270,13 +322,22 @@
     method @androidx.compose.runtime.Composable public static void Divider-JRSVyrs(optional androidx.compose.ui.Modifier modifier, optional long color, optional float thickness, optional float startIndent);
   }
 
-  public final class DrawerConstants {
-    method public float getDefaultElevation-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
+  @Deprecated public final class DrawerConstants {
+    method @Deprecated public float getDefaultElevation-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
     property public final float DefaultElevation;
     property @androidx.compose.runtime.Composable public final long defaultScrimColor;
-    field public static final androidx.compose.material.DrawerConstants INSTANCE;
-    field public static final float ScrimDefaultOpacity = 0.32f;
+    field @Deprecated public static final androidx.compose.material.DrawerConstants INSTANCE;
+    field @Deprecated public static final float ScrimDefaultOpacity = 0.32f;
+  }
+
+  public final class DrawerDefaults {
+    method public float getElevation-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public long getScrimColor-0d7_KjU();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable public final long scrimColor;
+    field public static final androidx.compose.material.DrawerDefaults INSTANCE;
+    field public static final float ScrimOpacity = 0.32f;
   }
 
   public final class DrawerKt {
@@ -306,10 +367,16 @@
     enum_constant public static final androidx.compose.material.DrawerValue Open;
   }
 
-  public final class ElevationConstants {
+  @Deprecated public final class ElevationConstants {
+    method @Deprecated public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? incomingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
+    method @Deprecated public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? outgoingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
+    field @Deprecated public static final androidx.compose.material.ElevationConstants INSTANCE;
+  }
+
+  public final class ElevationDefaults {
     method public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? incomingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
     method public androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp>? outgoingAnimationSpecForInteraction(androidx.compose.foundation.Interaction interaction);
-    field public static final androidx.compose.material.ElevationConstants INSTANCE;
+    field public static final androidx.compose.material.ElevationDefaults INSTANCE;
   }
 
   public final class ElevationKt {
@@ -356,9 +423,14 @@
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public androidx.compose.material.FixedThreshold copy-0680j_4(float offset);
   }
 
-  public final class FloatingActionButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation defaultElevation-ioHfwGI(optional float defaultElevation, optional float pressedElevation);
-    field public static final androidx.compose.material.FloatingActionButtonConstants INSTANCE;
+  @Deprecated public final class FloatingActionButtonConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation defaultElevation-ioHfwGI(optional float defaultElevation, optional float pressedElevation);
+    field @Deprecated public static final androidx.compose.material.FloatingActionButtonConstants INSTANCE;
+  }
+
+  public final class FloatingActionButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation elevation-ioHfwGI(optional float defaultElevation, optional float pressedElevation);
+    field public static final androidx.compose.material.FloatingActionButtonDefaults INSTANCE;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface FloatingActionButtonElevation {
@@ -413,12 +485,20 @@
     method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.InteractionState interactionState, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
-  public final class ModalBottomSheetConstants {
-    method public float getDefaultElevation-D9Ej5fM();
-    method @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
+  @Deprecated public final class ModalBottomSheetConstants {
+    method @Deprecated public float getDefaultElevation-D9Ej5fM();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultScrimColor-0d7_KjU();
     property public final float DefaultElevation;
     property @androidx.compose.runtime.Composable public final long DefaultScrimColor;
-    field public static final androidx.compose.material.ModalBottomSheetConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.ModalBottomSheetConstants INSTANCE;
+  }
+
+  public final class ModalBottomSheetDefaults {
+    method public float getElevation-D9Ej5fM();
+    method @androidx.compose.runtime.Composable public long getScrimColor-0d7_KjU();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable public final long scrimColor;
+    field public static final androidx.compose.material.ModalBottomSheetDefaults INSTANCE;
   }
 
   public final class ModalBottomSheetKt {
@@ -450,13 +530,22 @@
     method @androidx.compose.runtime.Composable public static void OutlinedTextField-IIju55g(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isErrorValue, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onImeActionPerformed, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long activeColor, optional long inactiveColor, optional long errorColor);
   }
 
-  public final class ProgressIndicatorConstants {
-    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultProgressAnimationSpec();
-    method public float getDefaultStrokeWidth-D9Ej5fM();
+  @Deprecated public final class ProgressIndicatorConstants {
+    method @Deprecated public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultProgressAnimationSpec();
+    method @Deprecated public float getDefaultStrokeWidth-D9Ej5fM();
     property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> DefaultProgressAnimationSpec;
     property public final float DefaultStrokeWidth;
-    field public static final float DefaultIndicatorBackgroundOpacity = 0.24f;
-    field public static final androidx.compose.material.ProgressIndicatorConstants INSTANCE;
+    field @Deprecated public static final float DefaultIndicatorBackgroundOpacity = 0.24f;
+    field @Deprecated public static final androidx.compose.material.ProgressIndicatorConstants INSTANCE;
+  }
+
+  public final class ProgressIndicatorDefaults {
+    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getProgressAnimationSpec();
+    method public float getStrokeWidth-D9Ej5fM();
+    property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> ProgressAnimationSpec;
+    property public final float StrokeWidth;
+    field public static final androidx.compose.material.ProgressIndicatorDefaults INSTANCE;
+    field public static final float IndicatorBackgroundOpacity = 0.24f;
   }
 
   public final class ProgressIndicatorKt {
@@ -470,9 +559,14 @@
     method public long radioColor-0d7_KjU(boolean enabled, boolean selected);
   }
 
-  public final class RadioButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.RadioButtonColors defaultColors-xS_xkl8(optional long selectedColor, optional long unselectedColor, optional long disabledColor);
-    field public static final androidx.compose.material.RadioButtonConstants INSTANCE;
+  @Deprecated public final class RadioButtonConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.RadioButtonColors defaultColors-xS_xkl8(optional long selectedColor, optional long unselectedColor, optional long disabledColor);
+    field @Deprecated public static final androidx.compose.material.RadioButtonConstants INSTANCE;
+  }
+
+  public final class RadioButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.RadioButtonColors colors-xS_xkl8(optional long selectedColor, optional long unselectedColor, optional long disabledColor);
+    field public static final androidx.compose.material.RadioButtonDefaults INSTANCE;
   }
 
   public final class RadioButtonKt {
@@ -525,8 +619,14 @@
   public final class ShapesKt {
   }
 
-  public final class SliderConstants {
-    field public static final androidx.compose.material.SliderConstants INSTANCE;
+  @Deprecated public final class SliderConstants {
+    field @Deprecated public static final androidx.compose.material.SliderConstants INSTANCE;
+    field @Deprecated public static final float InactiveTrackColorAlpha = 0.24f;
+    field @Deprecated public static final float TickColorAlpha = 0.54f;
+  }
+
+  public final class SliderDefaults {
+    field public static final androidx.compose.material.SliderDefaults INSTANCE;
     field public static final float InactiveTrackColorAlpha = 0.24f;
     field public static final float TickColorAlpha = 0.54f;
   }
@@ -535,12 +635,12 @@
     method @androidx.compose.runtime.Composable public static void Slider-B7rb6FQ(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit> onValueChangeEnd, optional androidx.compose.foundation.InteractionState interactionState, optional long thumbColor, optional long activeTrackColor, optional long inactiveTrackColor, optional long activeTickColor, optional long inactiveTickColor);
   }
 
-  public final class SnackbarConstants {
-    method @androidx.compose.runtime.Composable public long getDefaultActionPrimaryColor-0d7_KjU();
-    method @androidx.compose.runtime.Composable public long getDefaultBackgroundColor-0d7_KjU();
+  @Deprecated public final class SnackbarConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultActionPrimaryColor-0d7_KjU();
+    method @Deprecated @androidx.compose.runtime.Composable public long getDefaultBackgroundColor-0d7_KjU();
     property @androidx.compose.runtime.Composable public final long defaultActionPrimaryColor;
     property @androidx.compose.runtime.Composable public final long defaultBackgroundColor;
-    field public static final androidx.compose.material.SnackbarConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.SnackbarConstants INSTANCE;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi public interface SnackbarData {
@@ -554,6 +654,14 @@
     property public abstract String message;
   }
 
+  public final class SnackbarDefaults {
+    method @androidx.compose.runtime.Composable public long getBackgroundColor-0d7_KjU();
+    method @androidx.compose.runtime.Composable public long getPrimaryActionColor-0d7_KjU();
+    property @androidx.compose.runtime.Composable public final long backgroundColor;
+    property @androidx.compose.runtime.Composable public final long primaryActionColor;
+    field public static final androidx.compose.material.SnackbarDefaults INSTANCE;
+  }
+
   public enum SnackbarDuration {
     enum_constant public static final androidx.compose.material.SnackbarDuration Indefinite;
     enum_constant public static final androidx.compose.material.SnackbarDuration Long;
@@ -605,13 +713,24 @@
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.DismissState rememberDismissState(optional androidx.compose.material.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
   }
 
-  public final class SwipeableConstants {
-    method public androidx.compose.material.ResistanceConfig? defaultResistanceConfig(java.util.Set<java.lang.Float> anchors, optional float factorAtMin, optional float factorAtMax);
-    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultAnimationSpec();
-    method public float getDefaultVelocityThreshold-D9Ej5fM();
+  @Deprecated public final class SwipeableConstants {
+    method @Deprecated public androidx.compose.material.ResistanceConfig? defaultResistanceConfig(java.util.Set<java.lang.Float> anchors, optional float factorAtMin, optional float factorAtMax);
+    method @Deprecated public androidx.compose.animation.core.SpringSpec<java.lang.Float> getDefaultAnimationSpec();
+    method @Deprecated public float getDefaultVelocityThreshold-D9Ej5fM();
     property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> DefaultAnimationSpec;
     property public final float DefaultVelocityThreshold;
-    field public static final androidx.compose.material.SwipeableConstants INSTANCE;
+    field @Deprecated public static final androidx.compose.material.SwipeableConstants INSTANCE;
+    field @Deprecated public static final float StandardResistanceFactor = 10.0f;
+    field @Deprecated public static final float StiffResistanceFactor = 20.0f;
+  }
+
+  public final class SwipeableDefaults {
+    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getAnimationSpec();
+    method public float getVelocityThreshold-D9Ej5fM();
+    method public androidx.compose.material.ResistanceConfig? resistanceConfig(java.util.Set<java.lang.Float> anchors, optional float factorAtMin, optional float factorAtMax);
+    property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> AnimationSpec;
+    property public final float VelocityThreshold;
+    field public static final androidx.compose.material.SwipeableDefaults INSTANCE;
     field public static final float StandardResistanceFactor = 10.0f;
     field public static final float StiffResistanceFactor = 20.0f;
   }
@@ -631,6 +750,8 @@
     method public final T! getTargetValue();
     method public final T! getValue();
     method public final boolean isAnimationRunning();
+    method public final float performDrag(float delta);
+    method public final void performFling(float velocity, kotlin.jvm.functions.Function0<kotlin.Unit> onEnd);
     method @androidx.compose.material.ExperimentalMaterialApi public final void snapTo(T? targetValue);
     property public final float direction;
     property public final boolean isAnimationRunning;
@@ -651,27 +772,46 @@
     method public long trackColor-0d7_KjU(boolean enabled, boolean checked);
   }
 
-  public final class SwitchConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.material.SwitchColors defaultColors-R8aI8sA(optional long checkedThumbColor, optional long checkedTrackColor, optional float checkedTrackAlpha, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional float uncheckedTrackAlpha, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
-    field public static final androidx.compose.material.SwitchConstants INSTANCE;
+  @Deprecated public final class SwitchConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.SwitchColors defaultColors-R8aI8sA(optional long checkedThumbColor, optional long checkedTrackColor, optional float checkedTrackAlpha, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional float uncheckedTrackAlpha, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
+    field @Deprecated public static final androidx.compose.material.SwitchConstants INSTANCE;
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material.SwitchColors colors-R8aI8sA(optional long checkedThumbColor, optional long checkedTrackColor, optional float checkedTrackAlpha, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional float uncheckedTrackAlpha, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
+    field public static final androidx.compose.material.SwitchDefaults INSTANCE;
   }
 
   public final class SwitchKt {
     method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.InteractionState interactionState, optional androidx.compose.material.SwitchColors colors);
   }
 
-  public final class TabConstants {
-    method @androidx.compose.runtime.Composable public void DefaultDivider-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
-    method @androidx.compose.runtime.Composable public void DefaultIndicator-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
-    method public androidx.compose.ui.Modifier defaultTabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material.TabPosition currentTabPosition);
-    method public float getDefaultDividerThickness-D9Ej5fM();
-    method public float getDefaultIndicatorHeight-D9Ej5fM();
-    method public float getDefaultScrollableTabRowPadding-D9Ej5fM();
+  @Deprecated public final class TabConstants {
+    method @Deprecated @androidx.compose.runtime.Composable public void DefaultDivider-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+    method @Deprecated @androidx.compose.runtime.Composable public void DefaultIndicator-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @Deprecated public androidx.compose.ui.Modifier defaultTabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material.TabPosition currentTabPosition);
+    method @Deprecated public float getDefaultDividerThickness-D9Ej5fM();
+    method @Deprecated public float getDefaultIndicatorHeight-D9Ej5fM();
+    method @Deprecated public float getDefaultScrollableTabRowPadding-D9Ej5fM();
     property public final float DefaultDividerThickness;
     property public final float DefaultIndicatorHeight;
     property public final float DefaultScrollableTabRowPadding;
-    field public static final float DefaultDividerOpacity = 0.12f;
-    field public static final androidx.compose.material.TabConstants INSTANCE;
+    field @Deprecated public static final float DefaultDividerOpacity = 0.12f;
+    field @Deprecated public static final androidx.compose.material.TabConstants INSTANCE;
+  }
+
+  public final class TabDefaults {
+    method @androidx.compose.runtime.Composable public void Divider-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+    method @androidx.compose.runtime.Composable public void Indicator-Z-uBYeE(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method public float getDividerThickness-D9Ej5fM();
+    method public float getIndicatorHeight-D9Ej5fM();
+    method public float getScrollableTabRowPadding-D9Ej5fM();
+    method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material.TabPosition currentTabPosition);
+    property public final float DividerThickness;
+    property public final float IndicatorHeight;
+    property public final float ScrollableTabRowPadding;
+    field public static final float DividerOpacity = 0.12f;
+    field public static final androidx.compose.material.TabDefaults INSTANCE;
   }
 
   public final class TabKt {
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ButtonDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ButtonDemo.kt
index db79704..d765481 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ButtonDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ButtonDemo.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material.demos
 
+import androidx.compose.animation.animate
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.layout.Arrangement
@@ -28,11 +29,15 @@
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.shape.GenericShape
 import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants
+import androidx.compose.material.ButtonDefaults
+import androidx.compose.material.Icon
+import androidx.compose.material.IconToggleButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.OutlinedButton
 import androidx.compose.material.Text
 import androidx.compose.material.TextButton
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.material.samples.ButtonSample
 import androidx.compose.material.samples.ButtonWithIconSample
 import androidx.compose.material.samples.FluidExtendedFab
@@ -44,6 +49,10 @@
 import androidx.compose.material.samples.SimpleFab
 import androidx.compose.material.samples.TextButtonSample
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
@@ -78,7 +87,7 @@
     Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
         Button(
             onClick = {},
-            colors = ButtonConstants.defaultButtonColors(
+            colors = ButtonDefaults.buttonColors(
                 backgroundColor = MaterialTheme.colors.secondary
             )
         ) {
@@ -124,6 +133,7 @@
     Row {
         IconButtonSample()
         IconToggleButtonSample()
+        IconToggleButtonDisabled()
     }
 }
 
@@ -135,7 +145,7 @@
         onClick = {},
         modifier = Modifier.preferredSize(110.dp),
         shape = TriangleShape,
-        colors = ButtonConstants.defaultOutlinedButtonColors(
+        colors = ButtonDefaults.outlinedButtonColors(
             backgroundColor = Color.Yellow
         ),
         border = BorderStroke(width = 2.dp, color = Color.Black)
@@ -147,6 +157,16 @@
     }
 }
 
+@Composable
+private fun IconToggleButtonDisabled() {
+    var checked by remember { mutableStateOf(false) }
+
+    IconToggleButton(checked = checked, enabled = false, onCheckedChange = { checked = it }) {
+        val tint = animate(if (checked) Color(0xFFEC407A) else Color(0xFFB0BEC5))
+        Icon(Icons.Filled.Favorite, tint = tint)
+    }
+}
+
 private val TriangleShape = GenericShape { size ->
     moveTo(size.width / 2f, 0f)
     lineTo(size.width, size.height)
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
index cd681e6..ae05869 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
@@ -61,7 +61,7 @@
 import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.SweepGradient
+import androidx.compose.ui.graphics.SweepGradientShader
 import androidx.compose.ui.graphics.isSpecified
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.graphics.toPixelMap
@@ -309,9 +309,8 @@
 private class ColorWheel(diameter: Int) {
     private val radius = diameter / 2f
 
-    // TODO: b/152063545 - replace with Compose SweepGradient when it is available
-    private val sweepGradient = SweepGradient(
-        listOf(
+    private val sweepGradient = SweepGradientShader(
+        colors = listOf(
             Color.Red,
             Color.Magenta,
             Color.Blue,
@@ -320,13 +319,14 @@
             Color.Yellow,
             Color.Red
         ),
-        Offset(radius, radius)
+        colorStops = null,
+        center = Offset(radius, radius)
     )
 
     val image = ImageBitmap(diameter, diameter).also { imageBitmap ->
         val canvas = Canvas(imageBitmap)
         val center = Offset(radius, radius)
-        val paint = Paint().apply { sweepGradient.applyTo(this, 1.0f) }
+        val paint = Paint().apply { shader = sweepGradient }
         canvas.drawCircle(center, radius, paint)
     }
 }
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt
index 3b8a880..834ffbc5 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt
@@ -41,7 +41,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -88,9 +87,7 @@
 private fun DynamicThemeApp(scrollFraction: ScrollFraction, palette: Colors) {
     MaterialTheme(palette) {
         val scrollState = rememberScrollState()
-        val fraction =
-            round((scrollState.value / scrollState.maxValue) * 100) / 100
-        remember(fraction) { scrollFraction.value = fraction }
+        scrollFraction.value = round((scrollState.value / scrollState.maxValue) * 100) / 100
         Scaffold(
             topBar = { TopAppBar({ Text("Scroll down!") }) },
             bottomBar = { BottomAppBar(cutoutShape = CircleShape) {} },
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 038d79e..d3c01a0 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
@@ -91,7 +91,8 @@
             "TextFields",
             listOf(
                 ComposableDemo("FilledTextField/OutlinedTextField") { MaterialTextFieldDemo() },
-                ComposableDemo("Multiple text fields") { TextFieldsDemo() }
+                ComposableDemo("Multiple text fields") { TextFieldsDemo() },
+                ComposableDemo("Alignment inside text fields") { VerticalAlignmentsInTextField() }
             )
         )
     )
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 8370f2e..92121a8 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
@@ -25,9 +25,12 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredWidth
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.widthIn
 import androidx.compose.foundation.selection.selectable
 import androidx.compose.material.Checkbox
@@ -51,6 +54,8 @@
 import androidx.compose.material.samples.TextFieldWithPlaceholder
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.savedinstancestate.savedInstanceState
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -84,6 +89,49 @@
 }
 
 @Composable
+fun VerticalAlignmentsInTextField() {
+    Column {
+        val singleLine = remember { mutableStateOf(false) }
+        val label = remember { mutableStateOf(false) }
+        val text = remember { mutableStateOf("") }
+
+        Spacer(Modifier.height(10.dp))
+        OptionRow(
+            title = "Single line",
+            checked = singleLine.value,
+            onCheckedChange = { singleLine.value = it }
+        )
+        OptionRow(
+            title = "Label",
+            checked = label.value,
+            onCheckedChange = { label.value = it }
+        )
+
+        Spacer(Modifier.height(10.dp))
+        val textFieldModifier = Modifier
+            .align(Alignment.CenterHorizontally)
+            .width(300.dp)
+            .heightIn(max = 200.dp)
+            .then(if (singleLine.value) Modifier else Modifier.heightIn(min = 100.dp))
+        TextField(
+            value = text.value,
+            onValueChange = { text.value = it },
+            label = { if (label.value) Text("Label") },
+            singleLine = singleLine.value,
+            modifier = textFieldModifier
+        )
+        Spacer(Modifier.height(10.dp))
+        OutlinedTextField(
+            value = text.value,
+            onValueChange = { text.value = it },
+            label = { if (label.value) Text("Label") },
+            singleLine = singleLine.value,
+            modifier = textFieldModifier
+        )
+    }
+}
+
+@Composable
 fun MaterialTextFieldDemo() {
     ScrollableColumn(contentPadding = PaddingValues(10.dp)) {
         var text by savedInstanceState { "" }
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MenuDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MenuDemo.kt
index ddbfc47..0dcc59c 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MenuDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MenuDemo.kt
@@ -85,7 +85,7 @@
         expanded = expanded,
         onDismissRequest = { expanded = false },
         toggle = iconButton,
-        dropdownOffset = Position(-12.dp, -12.dp),
+        dropdownOffset = Position(24.dp, 0.dp),
         toggleModifier = modifier
     ) {
         options.forEach {
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TabDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TabDemo.kt
index 1d503d0..cb37e1c 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TabDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TabDemo.kt
@@ -21,7 +21,7 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants
+import androidx.compose.material.ButtonDefaults
 import androidx.compose.material.Text
 import androidx.compose.material.samples.FancyIndicatorContainerTabs
 import androidx.compose.material.samples.FancyIndicatorTabs
@@ -69,7 +69,7 @@
             onClick = {
                 showingSimple.value = !showingSimple.value
             },
-            colors = ButtonConstants.defaultButtonColors(backgroundColor = Color.Cyan)
+            colors = ButtonDefaults.buttonColors(backgroundColor = Color.Cyan)
         ) {
             Text(buttonText)
         }
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt
index ecbfef5..0294cad 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt
@@ -18,9 +18,7 @@
 
 import androidx.annotation.Sampled
 import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.lazy.LazyColumnFor
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.BackdropScaffold
 import androidx.compose.material.BackdropValue
 import androidx.compose.material.ExperimentalMaterialApi
@@ -40,7 +38,6 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
@@ -88,22 +85,27 @@
             )
         },
         backLayerContent = {
-            LazyColumnFor((1..5).toList()) {
-                ListItem(
-                    Modifier.clickable {
-                        selection.value = it
-                        scaffoldState.conceal()
-                    },
-                    text = { Text("Select $it") }
-                )
+            LazyColumn {
+                for (i in 1..5) item {
+                    ListItem(
+                        Modifier.clickable {
+                            selection.value = i
+                            scaffoldState.conceal()
+                        },
+                        text = { Text("Select $i") }
+                    )
+                }
             }
         },
         frontLayerContent = {
-            Box(
-                Modifier.fillMaxSize(),
-                contentAlignment = Alignment.Center
-            ) {
-                Text("Selection: ${selection.value}")
+            Text("Selection: ${selection.value}")
+            LazyColumn {
+                for (i in 1..50) item {
+                    ListItem(
+                        text = { Text("Item $i") },
+                        icon = { Icon(Icons.Default.Favorite) }
+                    )
+                }
             }
         }
     )
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ButtonSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ButtonSamples.kt
index e2ac96e..12cfe41 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ButtonSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ButtonSamples.kt
@@ -20,7 +20,7 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.size
 import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants
+import androidx.compose.material.ButtonDefaults
 import androidx.compose.material.Icon
 import androidx.compose.material.OutlinedButton
 import androidx.compose.material.Text
@@ -58,8 +58,8 @@
 @Composable
 fun ButtonWithIconSample() {
     Button(onClick = { /* Do something! */ }) {
-        Icon(Icons.Filled.Favorite, Modifier.size(ButtonConstants.DefaultIconSize))
-        Spacer(Modifier.size(ButtonConstants.DefaultIconSpacing))
+        Icon(Icons.Filled.Favorite, Modifier.size(ButtonDefaults.IconSize))
+        Spacer(Modifier.size(ButtonDefaults.IconSpacing))
         Text("Like")
     }
 }
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt
index 6f72f97..7261692 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.Button
 import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.Icon
@@ -45,11 +46,13 @@
     ModalBottomSheetLayout(
         sheetState = state,
         sheetContent = {
-            for (i in 1..5) {
-                ListItem(
-                    text = { Text("Item $i") },
-                    icon = { Icon(Icons.Default.Favorite) }
-                )
+            LazyColumn {
+                for (i in 1..50) item {
+                    ListItem(
+                        text = { Text("Item $i") },
+                        icon = { Icon(Icons.Default.Favorite) }
+                    )
+                }
             }
         }
     ) {
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ProgressIndicatorSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ProgressIndicatorSamples.kt
index 8c81625..ab3277e 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ProgressIndicatorSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ProgressIndicatorSamples.kt
@@ -24,7 +24,7 @@
 import androidx.compose.material.CircularProgressIndicator
 import androidx.compose.material.LinearProgressIndicator
 import androidx.compose.material.OutlinedButton
-import androidx.compose.material.ProgressIndicatorConstants
+import androidx.compose.material.ProgressIndicatorDefaults
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
@@ -41,7 +41,7 @@
     var progress by remember { mutableStateOf(0.1f) }
     val animatedProgress = animate(
         target = progress,
-        animSpec = ProgressIndicatorConstants.DefaultProgressAnimationSpec
+        animSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
     )
 
     Column(horizontalAlignment = Alignment.CenterHorizontally) {
@@ -63,7 +63,7 @@
     var progress by remember { mutableStateOf(0.1f) }
     val animatedProgress = animate(
         target = progress,
-        animSpec = ProgressIndicatorConstants.DefaultProgressAnimationSpec
+        animSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
     )
 
     Column(horizontalAlignment = Alignment.CenterHorizontally) {
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SelectionControlsSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SelectionControlsSamples.kt
index 84b16d4..67c5f45 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SelectionControlsSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SelectionControlsSamples.kt
@@ -25,7 +25,7 @@
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.selection.selectable
 import androidx.compose.material.Checkbox
-import androidx.compose.material.CheckboxConstants
+import androidx.compose.material.CheckboxDefaults
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.RadioButton
 import androidx.compose.material.Switch
@@ -66,7 +66,7 @@
         TriStateCheckbox(
             state = parentState,
             onClick = onParentClick,
-            colors = CheckboxConstants.defaultColors(
+            colors = CheckboxDefaults.colors(
                 checkedColor = MaterialTheme.colors.primary
             )
         )
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeToDismissSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeToDismissSamples.kt
index cfa1849..ca1b39d 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeToDismissSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeToDismissSamples.kt
@@ -22,7 +22,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumnFor
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.Card
 import androidx.compose.material.DismissDirection.EndToStart
 import androidx.compose.material.DismissDirection.StartToEnd
@@ -78,57 +78,63 @@
     // will animate to red if you're swiping left or green if you're swiping right. When you let
     // go, the item will animate out of the way if you're swiping left (like deleting an email) or
     // back to its default position if you're swiping right (like marking an email as read/unread).
-    LazyColumnFor(items) { item ->
-        var unread by remember { mutableStateOf(false) }
-        val dismissState = rememberDismissState(
-            confirmStateChange = {
-                if (it == DismissedToEnd) unread = !unread
-                it != DismissedToEnd
-            }
-        )
-        SwipeToDismiss(
-            state = dismissState,
-            modifier = Modifier.padding(vertical = 4.dp),
-            directions = setOf(StartToEnd, EndToStart),
-            dismissThresholds = { direction ->
-                FractionalThreshold(if (direction == StartToEnd) 0.25f else 0.5f)
-            },
-            background = {
-                val direction = dismissState.dismissDirection ?: return@SwipeToDismiss
-                val color = animate(
-                    when (dismissState.targetValue) {
-                        Default -> Color.LightGray
-                        DismissedToEnd -> Color.Green
-                        DismissedToStart -> Color.Red
-                    }
-                )
-                val alignment = when (direction) {
-                    StartToEnd -> Alignment.CenterStart
-                    EndToStart -> Alignment.CenterEnd
+    LazyColumn {
+        items(items) { item ->
+            var unread by remember { mutableStateOf(false) }
+            val dismissState = rememberDismissState(
+                confirmStateChange = {
+                    if (it == DismissedToEnd) unread = !unread
+                    it != DismissedToEnd
                 }
-                val icon = when (direction) {
-                    StartToEnd -> Icons.Default.Done
-                    EndToStart -> Icons.Default.Delete
-                }
-                val scale = animate(if (dismissState.targetValue == Default) 0.75f else 1f)
-
-                Box(
-                    modifier = Modifier.fillMaxSize().background(color).padding(horizontal = 20.dp),
-                    contentAlignment = alignment
-                ) {
-                    Icon(icon, Modifier.scale(scale))
-                }
-            },
-            dismissContent = {
-                Card(
-                    elevation = animate(if (dismissState.dismissDirection != null) 4.dp else 0.dp)
-                ) {
-                    ListItem(
-                        text = { Text(item, fontWeight = if (unread) FontWeight.Bold else null) },
-                        secondaryText = { Text("Swipe me left or right!") }
+            )
+            SwipeToDismiss(
+                state = dismissState,
+                modifier = Modifier.padding(vertical = 4.dp),
+                directions = setOf(StartToEnd, EndToStart),
+                dismissThresholds = { direction ->
+                    FractionalThreshold(if (direction == StartToEnd) 0.25f else 0.5f)
+                },
+                background = {
+                    val direction = dismissState.dismissDirection ?: return@SwipeToDismiss
+                    val color = animate(
+                        when (dismissState.targetValue) {
+                            Default -> Color.LightGray
+                            DismissedToEnd -> Color.Green
+                            DismissedToStart -> Color.Red
+                        }
                     )
+                    val alignment = when (direction) {
+                        StartToEnd -> Alignment.CenterStart
+                        EndToStart -> Alignment.CenterEnd
+                    }
+                    val icon = when (direction) {
+                        StartToEnd -> Icons.Default.Done
+                        EndToStart -> Icons.Default.Delete
+                    }
+                    val scale = animate(if (dismissState.targetValue == Default) 0.75f else 1f)
+
+                    Box(
+                        Modifier.fillMaxSize().background(color).padding(horizontal = 20.dp),
+                        contentAlignment = alignment
+                    ) {
+                        Icon(icon, Modifier.scale(scale))
+                    }
+                },
+                dismissContent = {
+                    Card(
+                        elevation = animate(
+                            if (dismissState.dismissDirection != null) 4.dp else 0.dp
+                        )
+                    ) {
+                        ListItem(
+                            text = {
+                                Text(item, fontWeight = if (unread) FontWeight.Bold else null)
+                            },
+                            secondaryText = { Text("Swipe me left or right!") }
+                        )
+                    }
                 }
-            }
-        )
+            )
+        }
     }
 }
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeableSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeableSamples.kt
index 933fd45..1ca2897 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeableSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeableSamples.kt
@@ -33,8 +33,10 @@
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.AmbientDensity
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import kotlin.math.roundToInt
 
 @Sampled
 @Composable
@@ -62,7 +64,7 @@
     ) {
         Box(
             Modifier
-                .offset(x = { swipeableState.offset.value })
+                .offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
                 .preferredSize(squareSize)
                 .background(Color.Red),
             contentAlignment = Alignment.Center
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TabSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TabSamples.kt
index b76d5a8..a6b9745 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TabSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TabSamples.kt
@@ -41,7 +41,7 @@
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ScrollableTabRow
 import androidx.compose.material.Tab
-import androidx.compose.material.TabConstants.defaultTabIndicatorOffset
+import androidx.compose.material.TabDefaults.tabIndicatorOffset
 import androidx.compose.material.TabPosition
 import androidx.compose.material.TabRow
 import androidx.compose.material.Text
@@ -189,7 +189,7 @@
 
     // Reuse the default offset animation modifier, but use our own indicator
     val indicator = @Composable { tabPositions: List<TabPosition> ->
-        FancyIndicator(Color.White, Modifier.defaultTabIndicatorOffset(tabPositions[state]))
+        FancyIndicator(Color.White, Modifier.tabIndicatorOffset(tabPositions[state]))
     }
 
     Column {
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 03c84ef..faa4f03 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
@@ -47,7 +47,6 @@
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertHasClickAction
-import androidx.compose.ui.test.assertHasNoClickAction
 import androidx.compose.ui.test.assertHeightIsAtLeast
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsEnabled
@@ -63,7 +62,6 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
@@ -133,7 +131,6 @@
     }
 
     @Test
-    @FlakyTest // TODO: b/158341686
     fun canBeDisabled() {
         val tag = "myButton"
 
@@ -151,8 +148,8 @@
             .assertHasClickAction()
             .assertIsEnabled()
             .performClick()
-            // Then confirm it's disabled with no click action after clicking it
-            .assertHasNoClickAction()
+            // Then confirm it's disabled with click action after clicking it
+            .assertHasClickAction()
             .assertIsNotEnabled()
     }
 
@@ -395,7 +392,7 @@
                     Button(
                         onClick = {},
                         enabled = false,
-                        colors = ButtonConstants.defaultButtonColors(
+                        colors = ButtonDefaults.buttonColors(
                             backgroundColor = Color.Red
                         ),
                         shape = RectangleShape
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CheckboxUiTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CheckboxUiTest.kt
index fab7d9d..8df6dc8 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CheckboxUiTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CheckboxUiTest.kt
@@ -25,7 +25,7 @@
 import androidx.compose.ui.state.ToggleableState.Indeterminate
 import androidx.compose.ui.state.ToggleableState.Off
 import androidx.compose.ui.state.ToggleableState.On
-import androidx.compose.ui.test.assertHasNoClickAction
+import androidx.compose.ui.test.assertHasClickAction
 import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsOff
 import androidx.compose.ui.test.assertIsOn
@@ -106,7 +106,7 @@
         }
 
         rule.onNodeWithTag(defaultTag)
-            .assertHasNoClickAction()
+            .assertHasClickAction()
     }
 
     @Test
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 f81b3d9..9f831e1 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
@@ -182,7 +182,7 @@
                     FloatingActionButton(
                         modifier = Modifier.testTag("myButton"),
                         onClick = {},
-                        elevation = FloatingActionButtonConstants.defaultElevation(
+                        elevation = FloatingActionButtonDefaults.elevation(
                             defaultElevation = 0.dp
                         )
                     ) {
@@ -219,7 +219,7 @@
                     ExtendedFloatingActionButton(
                         modifier = Modifier.testTag("myButton"),
                         onClick = {},
-                        elevation = FloatingActionButtonConstants.defaultElevation(
+                        elevation = FloatingActionButtonDefaults.elevation(
                             defaultElevation = 0.dp
                         ),
                         text = { Box(Modifier.preferredSize(10.dp, 50.dp)) }
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 64e7730..aff45d5 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
@@ -144,7 +144,7 @@
         )
 
         assertThat(ltrPosition.x).isEqualTo(
-            anchorPosition.x + anchorSize.width + offsetX
+            anchorPosition.x + offsetX
         )
         assertThat(ltrPosition.y).isEqualTo(
             anchorPosition.y + anchorSize.height + offsetY
@@ -161,7 +161,7 @@
         )
 
         assertThat(rtlPosition.x).isEqualTo(
-            anchorPosition.x - popupSize.width - offsetX
+            anchorPosition.x + anchorSize.width - offsetX - popupSize.width
         )
         assertThat(rtlPosition.y).isEqualTo(
             anchorPosition.y + anchorSize.height + offsetY
@@ -192,7 +192,7 @@
         )
 
         assertThat(ltrPosition.x).isEqualTo(
-            anchorPosition.x - popupSize.width - offsetX
+            anchorPosition.x + anchorSize.width - offsetX - popupSize.width
         )
         assertThat(ltrPosition.y).isEqualTo(
             anchorPosition.y - popupSize.height - offsetY
@@ -209,7 +209,7 @@
         )
 
         assertThat(rtlPosition.x).isEqualTo(
-            anchorPositionRtl.x + anchorSize.width + offsetX
+            anchorPositionRtl.x + offsetX
         )
         assertThat(rtlPosition.y).isEqualTo(
             anchorPositionRtl.y - popupSize.height - offsetY
@@ -275,9 +275,9 @@
         assertThat(obtainedParentBounds).isEqualTo(IntBounds(anchorPosition, anchorSize))
         assertThat(obtainedMenuBounds).isEqualTo(
             IntBounds(
-                anchorPosition.x + anchorSize.width + offsetX,
+                anchorPosition.x + offsetX,
                 anchorPosition.y + anchorSize.height + offsetY,
-                anchorPosition.x + anchorSize.width + offsetX + popupSize.width,
+                anchorPosition.x + offsetX + popupSize.width,
                 anchorPosition.y + anchorSize.height + offsetY + popupSize.height
             )
         )
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
index 915e930..ca5dee3 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.compose.material
 
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithText
@@ -52,7 +52,7 @@
         rule.setContent {
             scope = rememberCoroutineScope()
             SnackbarHost(hostState) { data ->
-                remember(data) {
+                LaunchedEffect(data) {
                     resultedInvocation += data.message
                     data.dismiss()
                 }
@@ -79,9 +79,9 @@
         rule.setContent {
             scope = rememberCoroutineScope()
             SnackbarHost(hostState) { data ->
-                remember(data) {
+                LaunchedEffect(data) {
                     resultedInvocation += data.message
-                    scope.launch {
+                    launch {
                         delay(30L)
                         data.dismiss()
                     }
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 ba151db..21e4fa0 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
@@ -18,27 +18,41 @@
 
 import androidx.compose.animation.core.AnimationEndReason
 import androidx.compose.animation.core.ManualAnimationClock
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.preferredSize
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.nestedscroll.nestedScroll
 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.centerX
+import androidx.compose.ui.test.centerY
+import androidx.compose.ui.test.down
 import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.moveBy
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performGesture
 import androidx.compose.ui.test.swipe
 import androidx.compose.ui.test.swipeWithVelocity
+import androidx.compose.ui.test.up
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.milliseconds
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
@@ -1521,6 +1535,223 @@
         }
     }
 
+    @Test
+    fun swipeable_defaultVerticalNestedScrollConnection_nestedDrag() {
+        lateinit var swipeableState: SwipeableState<String>
+        lateinit var anchors: MutableState<Map<Float, String>>
+        lateinit var scrollState: ScrollState
+        rule.setContent {
+            swipeableState = rememberSwipeableState("A")
+            anchors = remember { mutableStateOf(mapOf(0f to "A", -1000f to "B")) }
+            scrollState = rememberScrollState()
+            Box(
+                Modifier
+                    .preferredSize(300.dp)
+                    .nestedScroll(swipeableState.PreUpPostDownNestedScrollConnection)
+                    .swipeable(
+                        state = swipeableState,
+                        anchors = anchors.value,
+                        thresholds = { _, _ -> FractionalThreshold(0.5f) },
+                        orientation = Orientation.Horizontal
+                    )
+            ) {
+                ScrollableColumn(
+                    scrollState = scrollState,
+                    modifier = Modifier.fillMaxWidth().testTag(swipeableTag)
+                ) {
+                    repeat(100) {
+                        Text(text = it.toString(), modifier = Modifier.height(50.dp))
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(swipeableState.value).isEqualTo("A")
+        }
+
+        rule.onNodeWithTag(swipeableTag)
+            .performGesture {
+                down(Offset(x = 10f, y = 10f))
+                moveBy(Offset(x = 0f, y = -1500f))
+                up()
+            }
+        advanceClock()
+
+        rule.runOnIdle {
+            assertThat(swipeableState.value).isEqualTo("B")
+            assertThat(scrollState.value).isGreaterThan(0f)
+        }
+
+        rule.onNodeWithTag(swipeableTag)
+            .performGesture {
+                down(Offset(x = 10f, y = 10f))
+                moveBy(Offset(x = 0f, y = 1500f))
+                up()
+            }
+
+        advanceClock()
+
+        rule.runOnIdle {
+            assertThat(swipeableState.value).isEqualTo("A")
+            assertThat(scrollState.value).isEqualTo(0f)
+        }
+    }
+
+    @Test
+    fun swipeable_nestedScroll_preFling() {
+        lateinit var swipeableState: SwipeableState<String>
+        lateinit var anchors: MutableState<Map<Float, String>>
+        lateinit var scrollState: ScrollState
+        rule.setContent {
+            swipeableState = rememberSwipeableState("A")
+            anchors = remember { mutableStateOf(mapOf(0f to "A", -1000f to "B")) }
+            scrollState = rememberScrollState()
+            Box(
+                Modifier
+                    .preferredSize(300.dp)
+                    .nestedScroll(swipeableState.PreUpPostDownNestedScrollConnection)
+                    .swipeable(
+                        state = swipeableState,
+                        anchors = anchors.value,
+                        thresholds = { _, _ -> FixedThreshold(56.dp) },
+                        orientation = Orientation.Horizontal
+                    )
+            ) {
+                ScrollableColumn(
+                    scrollState = scrollState,
+                    modifier = Modifier.fillMaxWidth().testTag(swipeableTag)
+                ) {
+                    repeat(100) {
+                        Text(text = it.toString(), modifier = Modifier.height(50.dp))
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(swipeableState.value).isEqualTo("A")
+        }
+
+        rule.onNodeWithTag(swipeableTag)
+            .performGesture {
+                swipeWithVelocity(
+                    center,
+                    center.copy(y = centerY - 500, x = centerX),
+                    duration = 50.milliseconds,
+                    endVelocity = 20000f
+                )
+            }
+
+        advanceClock()
+
+        rule.runOnIdle {
+            assertThat(swipeableState.value).isEqualTo("B")
+            // should eat all velocity, no internal scroll
+            assertThat(scrollState.value).isEqualTo(0f)
+        }
+
+        rule.onNodeWithTag(swipeableTag)
+            .performGesture {
+                swipeWithVelocity(
+                    center,
+                    center.copy(y = centerY + 500, x = centerX),
+                    duration = 50.milliseconds,
+                    endVelocity = 20000f
+                )
+            }
+
+        advanceClock()
+
+        rule.runOnIdle {
+            assertThat(swipeableState.value).isEqualTo("A")
+            assertThat(scrollState.value).isEqualTo(0f)
+        }
+    }
+
+    @Test
+    fun swipeable_nestedScroll_postFlings() {
+        lateinit var swipeableState: SwipeableState<String>
+        lateinit var anchors: MutableState<Map<Float, String>>
+        lateinit var scrollState: ScrollState
+        rule.setContent {
+            swipeableState = rememberSwipeableState("B")
+            anchors = remember { mutableStateOf(mapOf(0f to "A", -1000f to "B")) }
+            scrollState = rememberScrollState(initial = 5000f)
+            Box(
+                Modifier
+                    .preferredSize(300.dp)
+                    .nestedScroll(swipeableState.PreUpPostDownNestedScrollConnection)
+                    .swipeable(
+                        state = swipeableState,
+                        anchors = anchors.value,
+                        thresholds = { _, _ -> FixedThreshold(56.dp) },
+                        orientation = Orientation.Horizontal
+                    )
+            ) {
+                ScrollableColumn(
+                    scrollState = scrollState,
+                    modifier = Modifier.fillMaxWidth().testTag(swipeableTag)
+                ) {
+                    repeat(100) {
+                        Text(text = it.toString(), modifier = Modifier.height(50.dp))
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(swipeableState.value).isEqualTo("B")
+            assertThat(scrollState.value).isEqualTo(5000f)
+        }
+
+        rule.onNodeWithTag(swipeableTag)
+            .performGesture {
+                // swipe less than scrollState.value but with velocity to test that backdrop won't
+                // move when receives, because it's at anchor
+                swipeWithVelocity(
+                    center,
+                    center.copy(y = centerY + 1500, x = centerX),
+                    duration = 50.milliseconds,
+                    endVelocity = 20000f
+                )
+            }
+
+        advanceClock()
+
+        rule.runOnIdle {
+            assertThat(swipeableState.value).isEqualTo("B")
+            assertThat(scrollState.value).isEqualTo(0f)
+            // set value again to test overshoot
+            scrollState.scrollBy(500f)
+        }
+
+        advanceClock()
+
+        rule.runOnIdle {
+            assertThat(swipeableState.value).isEqualTo("B")
+            assertThat(scrollState.value).isEqualTo(500f)
+        }
+
+        rule.onNodeWithTag(swipeableTag)
+            .performGesture {
+                // swipe more than scrollState.value so backdrop start receiving nested scroll
+                swipeWithVelocity(
+                    center,
+                    center.copy(y = centerY + 1500, x = centerX),
+                    duration = 50.milliseconds,
+                    endVelocity = 20000f
+                )
+            }
+
+        advanceClock()
+
+        rule.runOnIdle {
+            assertThat(swipeableState.value).isEqualTo("A")
+            assertThat(scrollState.value).isEqualTo(0f)
+        }
+    }
+
     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 9cf48ed..9b05fd8 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
@@ -98,7 +98,7 @@
                 Switch(
                     checked = true,
                     onCheckedChange = { },
-                    colors = SwitchConstants.defaultColors(checkedThumbColor = Color.Red)
+                    colors = SwitchDefaults.colors(checkedThumbColor = Color.Red)
                 )
             }
         }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwitchTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwitchTest.kt
index e552475..9043a7c 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwitchTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwitchTest.kt
@@ -25,7 +25,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.AmbientLayoutDirection
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.assertHasNoClickAction
+import androidx.compose.ui.test.assertHasClickAction
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsOff
@@ -133,7 +133,7 @@
             )
         }
         rule.onNodeWithTag(defaultSwitchTag)
-            .assertHasNoClickAction()
+            .assertHasClickAction()
     }
 
     @Test
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 a10d1cb..bd3d988 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
@@ -19,7 +19,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.preferredHeight
-import androidx.compose.material.TabConstants.defaultTabIndicatorOffset
+import androidx.compose.material.TabDefaults.tabIndicatorOffset
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.material.samples.ScrollingTextTabs
@@ -124,7 +124,7 @@
             val indicator = @Composable { tabPositions: List<TabPosition> ->
                 Box(
                     Modifier
-                        .defaultTabIndicatorOffset(tabPositions[state])
+                        .tabIndicatorOffset(tabPositions[state])
                         .fillMaxWidth()
                         .preferredHeight(indicatorHeight)
                         .background(color = Color.Red)
@@ -299,7 +299,7 @@
             val indicator = @Composable { tabPositions: List<TabPosition> ->
                 Box(
                     Modifier
-                        .defaultTabIndicatorOffset(tabPositions[state])
+                        .tabIndicatorOffset(tabPositions[state])
                         .fillMaxWidth()
                         .preferredHeight(indicatorHeight)
                         .background(color = Color.Red)
@@ -330,7 +330,7 @@
         rule.onNodeWithTag("indicator")
             .assertPositionInRootIsEqualTo(
                 // Tabs in a scrollable tab row are offset 52.dp from each end
-                expectedLeft = TabConstants.DefaultScrollableTabRowPadding,
+                expectedLeft = TabDefaults.ScrollableTabRowPadding,
                 expectedTop = tabRowBounds.height - indicatorHeight
             )
 
@@ -341,7 +341,7 @@
         // should be in the middle of the TabRow
         rule.onNodeWithTag("indicator")
             .assertPositionInRootIsEqualTo(
-                expectedLeft = TabConstants.DefaultScrollableTabRowPadding + minimumTabWidth,
+                expectedLeft = TabDefaults.ScrollableTabRowPadding + minimumTabWidth,
                 expectedTop = tabRowBounds.height - indicatorHeight
             )
     }
@@ -446,8 +446,8 @@
     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")
+            val modifier = Modifier.tabIndicatorOffset(pos) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("tabIndicatorOffset")
             assertThat(modifier.valueOverride).isEqualTo(pos)
             assertThat(modifier.inspectableElements.asIterable()).isEmpty()
         }
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 9b1f9a1..e2a4cc5 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
@@ -18,6 +18,7 @@
 
 import android.os.Build
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
 import androidx.compose.material.AmbientContentColor
 import androidx.compose.material.GOLDEN_MATERIAL
 import androidx.compose.material.OutlinedTextField
@@ -39,6 +40,7 @@
 import androidx.compose.ui.test.performGesture
 import androidx.compose.ui.test.up
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -183,6 +185,172 @@
         assertAgainstGolden("outlined_textField_textColor_defaultContentColor")
     }
 
+    @Test
+    fun outlinedTextField_multiLine_withLabel_textAlignedToTop() {
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "Text",
+                onValueChange = {},
+                label = { Text("Label") },
+                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("outlined_textField_multiLine_withLabel_textAlignedToTop")
+    }
+
+    @Test
+    fun outlinedTextField_multiLine_withoutLabel_textAlignedToTop() {
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "Text",
+                onValueChange = {},
+                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("outlined_textField_multiLine_withoutLabel_textAlignedToTop")
+    }
+
+    @Test
+    fun outlinedTextField_multiLine_withLabel_placeholderAlignedToTop() {
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "",
+                onValueChange = {},
+                label = { Text("Label") },
+                placeholder = { Text("placeholder") },
+                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+            )
+        }
+
+        rule.onNodeWithTag(TextFieldTag)
+            // split click into (down) and (move, up) to enforce a composition in between
+            .performGesture { down(center) }
+            .performGesture { move(); up() }
+
+        assertAgainstGolden("outlined_textField_multiLine_withLabel_placeholderAlignedToTop")
+    }
+
+    @Test
+    fun outlinedTextField_multiLine_withoutLabel_placeholderAlignedToTop() {
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "",
+                onValueChange = {},
+                placeholder = { Text("placeholder") },
+                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+            )
+        }
+
+        rule.onNodeWithTag(TextFieldTag)
+            // split click into (down) and (move, up) to enforce a composition in between
+            .performGesture { down(center) }
+            .performGesture { move(); up() }
+
+        assertAgainstGolden("outlined_textField_multiLine_withoutLabel_placeholderAlignedToTop")
+    }
+
+    @Test
+    fun outlinedTextField_multiLine_labelAlignedToTop() {
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "",
+                onValueChange = {},
+                label = { Text("Label") },
+                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("outlined_textField_multiLine_labelAlignedToTop")
+    }
+
+    @Test
+    fun outlinedTextField_singleLine_withLabel_textAlignedToTop() {
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "Text",
+                onValueChange = {},
+                singleLine = true,
+                label = { Text("Label") },
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("outlined_textField_singleLine_withLabel_textAlignedToTop")
+    }
+
+    @Test
+    fun outlinedTextField_singleLine_withoutLabel_textCenteredVertically() {
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "Text",
+                onValueChange = {},
+                singleLine = true,
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("outlined_textField_singleLine_withoutLabel_textCenteredVertically")
+    }
+
+    @Test
+    fun outlinedTextField_singleLine_withLabel_placeholderAlignedToTop() {
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "",
+                onValueChange = {},
+                placeholder = { Text("placeholder") },
+                label = { Text("Label") },
+                singleLine = true,
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        rule.onNodeWithTag(TextFieldTag)
+            // split click into (down) and (move, up) to enforce a composition in between
+            .performGesture { down(center) }
+            .performGesture { move(); up() }
+
+        assertAgainstGolden("outlined_textField_singleLine_withLabel_placeholderAlignedToTop")
+    }
+
+    @Test
+    fun outlinedTextField_singleLine_withoutLabel_placeholderCenteredVertically() {
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "",
+                onValueChange = {},
+                placeholder = { Text("placeholder") },
+                singleLine = true,
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        rule.onNodeWithTag(TextFieldTag)
+            // split click into (down) and (move, up) to enforce a composition in between
+            .performGesture { down(center) }
+            .performGesture { move(); up() }
+
+        assertAgainstGolden(
+            "outlined_textField_singleLine_withoutLabel_placeholderCenteredVertically"
+        )
+    }
+
+    @Test
+    fun outlinedTextField_singleLine_labelCenteredVetically() {
+        rule.setMaterialContent {
+            OutlinedTextField(
+                value = "",
+                onValueChange = {},
+                label = { Text("Label") },
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("outlined_textField_singleLine_labelCenteredVetically")
+    }
+
     private fun assertAgainstGolden(goldenIdentifier: String) {
         rule.onNodeWithTag(TextFieldTag)
             .captureToImage()
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 80d88be..b35ec1e 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
@@ -29,6 +29,7 @@
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.OutlinedTextField
 import androidx.compose.material.Text
+import androidx.compose.material.TextFieldPadding
 import androidx.compose.material.runOnIdleWithDensity
 import androidx.compose.material.setMaterialContent
 import androidx.compose.runtime.Providers
@@ -36,9 +37,8 @@
 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
-import androidx.compose.ui.focusObserver
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
@@ -81,7 +81,7 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalFocus::class, ExperimentalTesting::class)
+@OptIn(ExperimentalTesting::class)
 class OutlinedTextFieldTest {
     private val ExpectedMinimumTextFieldHeight = 56.dp
     private val ExpectedPadding = 16.dp
@@ -105,7 +105,7 @@
                 OutlinedTextField(
                     modifier = Modifier
                         .testTag(textField1Tag)
-                        .focusObserver { textField1Focused = it.isFocused },
+                        .onFocusChanged { textField1Focused = it.isFocused },
                     value = "input1",
                     onValueChange = {},
                     label = {}
@@ -113,7 +113,7 @@
                 OutlinedTextField(
                     modifier = Modifier
                         .testTag(textField2Tag)
-                        .focusObserver { textField2Focused = it.isFocused },
+                        .onFocusChanged { textField2Focused = it.isFocused },
                     value = "input2",
                     onValueChange = {},
                     label = {}
@@ -144,7 +144,7 @@
                 OutlinedTextField(
                     modifier = Modifier
                         .testTag(TextfieldTag)
-                        .focusObserver { focused = it.isFocused },
+                        .onFocusChanged { focused = it.isFocused },
                     value = "input",
                     onValueChange = {},
                     label = {}
@@ -163,6 +163,45 @@
     }
 
     @Test
+    fun testOutlinedTextField_labelPosition_initial_singlineLine() {
+        val labelSize = Ref<IntSize>()
+        val labelPosition = Ref<Offset>()
+        rule.setMaterialContent {
+            Box {
+                OutlinedTextField(
+                    value = "",
+                    onValueChange = {},
+                    singleLine = true,
+                    label = {
+                        Text(
+                            text = "label",
+                            modifier = Modifier.onGloballyPositioned {
+                                labelPosition.value = it.positionInRoot
+                                labelSize.value = it.size
+                            }
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.runOnIdleWithDensity {
+            // size
+            assertThat(labelSize.value).isNotNull()
+            assertThat(labelSize.value?.height).isGreaterThan(0)
+            assertThat(labelSize.value?.width).isGreaterThan(0)
+            assertThat(labelPosition.value?.x).isEqualTo(
+                ExpectedPadding.toIntPx().toFloat()
+            )
+            // label is centered in 56.dp default container, plus additional 8.dp padding on top
+            val minimumHeight = ExpectedMinimumTextFieldHeight.toIntPx()
+            assertThat(labelPosition.value?.y).isEqualTo(
+                ((minimumHeight - labelSize.value!!.height) / 2f).roundToInt() + 8.dp.toIntPx()
+            )
+        }
+    }
+
+    @Test
     fun testOutlinedTextField_labelPosition_initial_withDefaultHeight() {
         val labelSize = Ref<IntSize>()
         val labelPosition = Ref<Offset>()
@@ -192,10 +231,9 @@
             assertThat(labelPosition.value?.x).isEqualTo(
                 ExpectedPadding.toIntPx().toFloat()
             )
-            // label is centered in 56.dp default container, plus additional 8.dp padding on top
-            val minimumHeight = ExpectedMinimumTextFieldHeight.toIntPx()
+            // label is aligned to the top with padding, plus additional 8.dp padding on top
             assertThat(labelPosition.value?.y).isEqualTo(
-                ((minimumHeight - labelSize.value!!.height) / 2f).roundToInt() + 8.dp.toIntPx()
+                TextFieldPadding.toIntPx() + 8.dp.toIntPx()
             )
         }
     }
@@ -309,11 +347,7 @@
             // placeholder is centered in 56.dp default container,
             // plus additional 8.dp padding on top
             assertThat(placeholderPosition.value?.y).isEqualTo(
-                (
-                    (ExpectedMinimumTextFieldHeight.toIntPx() - placeholderSize.value!!.height) /
-                        2f
-                    ).roundToInt() +
-                    8.dp.toIntPx()
+                TextFieldPadding.toIntPx() + 8.dp.toIntPx()
             )
         }
     }
@@ -353,14 +387,9 @@
             assertThat(placeholderPosition.value?.x).isEqualTo(
                 ExpectedPadding.toIntPx().toFloat()
             )
-            // placeholder is centered in 56.dp default container,
-            // plus additional 8.dp padding on top
+            // placeholder is placed with fixed padding plus additional 8.dp padding on top
             assertThat(placeholderPosition.value?.y).isEqualTo(
-                (
-                    (ExpectedMinimumTextFieldHeight.toIntPx() - placeholderSize.value!!.height) /
-                        2f
-                    ).roundToInt() +
-                    8.dp.toIntPx()
+                TextFieldPadding.toIntPx() + 8.dp.toIntPx()
             )
         }
     }
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 eaf3f3a..c870083 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
@@ -18,6 +18,7 @@
 
 import android.os.Build
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
 import androidx.compose.material.AmbientContentColor
 import androidx.compose.material.GOLDEN_MATERIAL
 import androidx.compose.material.Text
@@ -39,6 +40,7 @@
 import androidx.compose.ui.test.performGesture
 import androidx.compose.ui.test.up
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -181,6 +183,172 @@
         assertAgainstGolden("filled_textField_textColor_defaultContentColor")
     }
 
+    @Test
+    fun textField_multiLine_withLabel_textAlignedToTop() {
+        rule.setMaterialContent {
+            TextField(
+                value = "Text",
+                onValueChange = {},
+                label = { Text("Label") },
+                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("filled_textField_multiLine_withLabel_textAlignedToTop")
+    }
+
+    @Test
+    fun textField_multiLine_withoutLabel_textAlignedToTop() {
+        rule.setMaterialContent {
+            TextField(
+                value = "Text",
+                onValueChange = {},
+                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("filled_textField_multiLine_withoutLabel_textAlignedToTop")
+    }
+
+    @Test
+    fun textField_multiLine_withLabel_placeholderAlignedToTop() {
+        rule.setMaterialContent {
+            TextField(
+                value = "",
+                onValueChange = {},
+                label = { Text("Label") },
+                placeholder = { Text("placeholder") },
+                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+            )
+        }
+
+        rule.onNodeWithTag(TextFieldTag)
+            // split click into (down) and (move, up) to enforce a composition in between
+            .performGesture { down(center) }
+            .performGesture { move(); up() }
+
+        assertAgainstGolden("filled_textField_multiLine_withLabel_placeholderAlignedToTop")
+    }
+
+    @Test
+    fun textField_multiLine_withoutLabel_placeholderAlignedToTop() {
+        rule.setMaterialContent {
+            TextField(
+                value = "",
+                onValueChange = {},
+                placeholder = { Text("placeholder") },
+                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+            )
+        }
+
+        rule.onNodeWithTag(TextFieldTag)
+            // split click into (down) and (move, up) to enforce a composition in between
+            .performGesture { down(center) }
+            .performGesture { move(); up() }
+
+        assertAgainstGolden("filled_textField_multiLine_withoutLabel_placeholderAlignedToTop")
+    }
+
+    @Test
+    fun textField_multiLine_labelAlignedToTop() {
+        rule.setMaterialContent {
+            TextField(
+                value = "",
+                onValueChange = {},
+                label = { Text("Label") },
+                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("filled_textField_multiLine_labelAlignedToTop")
+    }
+
+    @Test
+    fun textField_singleLine_withLabel_textAlignedToTop() {
+        rule.setMaterialContent {
+            TextField(
+                value = "Text",
+                onValueChange = {},
+                singleLine = true,
+                label = { Text("Label") },
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("filled_textField_singleLine_withLabel_textAlignedToTop")
+    }
+
+    @Test
+    fun textField_singleLine_withoutLabel_textCenteredVertically() {
+        rule.setMaterialContent {
+            TextField(
+                value = "Text",
+                onValueChange = {},
+                singleLine = true,
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("filled_textField_singleLine_withoutLabel_textCenteredVertically")
+    }
+
+    @Test
+    fun textField_singleLine_withLabel_placeholderAlignedToTop() {
+        rule.setMaterialContent {
+            TextField(
+                value = "",
+                onValueChange = {},
+                placeholder = { Text("placeholder") },
+                label = { Text("Label") },
+                singleLine = true,
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        rule.onNodeWithTag(TextFieldTag)
+            // split click into (down) and (move, up) to enforce a composition in between
+            .performGesture { down(center) }
+            .performGesture { move(); up() }
+
+        assertAgainstGolden("filled_textField_singleLine_withLabel_placeholderAlignedToTop")
+    }
+
+    @Test
+    fun textField_singleLine_withoutLabel_placeholderCenteredVertically() {
+        rule.setMaterialContent {
+            TextField(
+                value = "",
+                onValueChange = {},
+                placeholder = { Text("placeholder") },
+                singleLine = true,
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        rule.onNodeWithTag(TextFieldTag)
+            // split click into (down) and (move, up) to enforce a composition in between
+            .performGesture { down(center) }
+            .performGesture { move(); up() }
+
+        assertAgainstGolden(
+            "filled_textField_singleLine_withoutLabel_placeholderCenteredVertically"
+        )
+    }
+
+    @Test
+    fun textField_singleLine_labelCenteredVetically() {
+        rule.setMaterialContent {
+            TextField(
+                value = "",
+                onValueChange = {},
+                label = { Text("Label") },
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        assertAgainstGolden("filled_textField_singleLine_labelCenteredVetically")
+    }
+
     private fun assertAgainstGolden(goldenIdentifier: String) {
         rule.onNodeWithTag(TextFieldTag)
             .captureToImage()
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 8b2d1f3..d35d2bc9 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
@@ -33,6 +33,7 @@
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Text
 import androidx.compose.material.TextField
+import androidx.compose.material.TextFieldPadding
 import androidx.compose.material.runOnIdleWithDensity
 import androidx.compose.material.setMaterialContent
 import androidx.compose.material.setMaterialContentForSizeAssertions
@@ -41,12 +42,11 @@
 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
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusModifier
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
@@ -95,7 +95,7 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalFocus::class, ExperimentalTesting::class)
+@OptIn(ExperimentalTesting::class)
 class TextFieldTest {
 
     private val ExpectedMinimumTextFieldHeight = 56.dp
@@ -134,7 +134,7 @@
             Column {
                 TextField(
                     modifier = Modifier
-                        .focusObserver { textField1Focused = it.isFocused }
+                        .onFocusChanged { textField1Focused = it.isFocused }
                         .testTag(textField1Tag),
                     value = "input1",
                     onValueChange = {},
@@ -142,7 +142,7 @@
                 )
                 TextField(
                     modifier = Modifier
-                        .focusObserver { textField2Focused = it.isFocused }
+                        .onFocusChanged { textField2Focused = it.isFocused }
                         .testTag(textField2Tag),
                     value = "input2",
                     onValueChange = {},
@@ -173,7 +173,7 @@
             Box {
                 TextField(
                     modifier = Modifier
-                        .focusObserver { focused = it.isFocused }
+                        .onFocusChanged { focused = it.isFocused }
                         .testTag(TextfieldTag),
                     value = "input",
                     onValueChange = {},
@@ -194,17 +194,16 @@
 
     @Test
     fun testTextField_showHideKeyboardBasedOnFocus() {
-        val parentFocusRequester = FocusRequester()
-        val focusRequester = FocusRequester()
+        val (focusReference, parentFocusReference) = FocusReference.createRefs()
         lateinit var hostView: View
         rule.setMaterialContent {
             hostView = AmbientView.current
             Box {
                 TextField(
                     modifier = Modifier
-                        .focusRequester(parentFocusRequester)
-                        .focus()
-                        .focusRequester(focusRequester)
+                        .focusReference(parentFocusReference)
+                        .focusModifier()
+                        .focusReference(focusReference)
                         .testTag(TextfieldTag),
                     value = "input",
                     onValueChange = {},
@@ -214,18 +213,17 @@
         }
 
         // Shows keyboard when the text field is focused.
-        rule.runOnIdle { focusRequester.requestFocus() }
+        rule.runOnIdle { focusReference.requestFocus() }
         rule.runOnIdle { assertThat(hostView.isSoftwareKeyboardShown).isTrue() }
 
         // Hides keyboard when the text field is not focused.
-        rule.runOnIdle { parentFocusRequester.requestFocus() }
+        rule.runOnIdle { parentFocusReference.requestFocus() }
         rule.runOnIdle { assertThat(hostView.isSoftwareKeyboardShown).isFalse() }
     }
 
     @Test
     fun testTextField_clickingOnTextAfterDismissingKeyboard_showsKeyboard() {
-        val parentFocusRequester = FocusRequester()
-        val focusRequester = FocusRequester()
+        val (focusReference, parentFocusReference) = FocusReference.createRefs()
         lateinit var softwareKeyboardController: SoftwareKeyboardController
         lateinit var hostView: View
         rule.setMaterialContent {
@@ -233,9 +231,9 @@
             Box {
                 TextField(
                     modifier = Modifier
-                        .focusRequester(parentFocusRequester)
-                        .focus()
-                        .focusRequester(focusRequester)
+                        .focusReference(parentFocusReference)
+                        .focusModifier()
+                        .focusReference(focusReference)
                         .testTag(TextfieldTag),
                     value = "input",
                     onValueChange = {},
@@ -246,7 +244,7 @@
         }
 
         // Shows keyboard when the text field is focused.
-        rule.runOnIdle { focusRequester.requestFocus() }
+        rule.runOnIdle { focusReference.requestFocus() }
         rule.runOnIdle { assertThat(hostView.isSoftwareKeyboardShown).isTrue() }
 
         // Hide keyboard.
@@ -258,6 +256,48 @@
     }
 
     @Test
+    fun testTextField_labelPosition_initial_singleLine() {
+        val labelSize = Ref<IntSize>()
+        val labelPosition = Ref<Offset>()
+        rule.setMaterialContent {
+            Box {
+                TextField(
+                    value = "",
+                    onValueChange = {},
+                    singleLine = true,
+                    label = {
+                        Text(
+                            text = "label",
+                            fontSize = 10.sp,
+                            modifier = Modifier
+                                .onGloballyPositioned {
+                                    labelPosition.value = it.positionInRoot
+                                    labelSize.value = it.size
+                                }
+                        )
+                    },
+                    modifier = Modifier.preferredHeight(56.dp)
+                )
+            }
+        }
+
+        rule.runOnIdleWithDensity {
+            // size
+            assertThat(labelSize.value).isNotNull()
+            assertThat(labelSize.value?.height).isGreaterThan(0)
+            assertThat(labelSize.value?.width).isGreaterThan(0)
+            // centered position
+            assertThat(labelPosition.value?.x).isEqualTo(
+                ExpectedPadding.toIntPx().toFloat()
+            )
+            assertThat(labelPosition.value?.y).isEqualTo(
+                ((ExpectedMinimumTextFieldHeight.toIntPx() - labelSize.value!!.height) / 2f)
+                    .roundToInt().toFloat()
+            )
+        }
+    }
+
+    @Test
     fun testTextField_labelPosition_initial_withDefaultHeight() {
         val labelSize = Ref<IntSize>()
         val labelPosition = Ref<Offset>()
@@ -292,8 +332,7 @@
                 ExpectedPadding.toIntPx().toFloat()
             )
             assertThat(labelPosition.value?.y).isEqualTo(
-                ((ExpectedMinimumTextFieldHeight.toIntPx() - labelSize.value!!.height) / 2f)
-                    .roundToInt().toFloat()
+                TextFieldPadding.toIntPx()
             )
         }
     }
@@ -332,7 +371,7 @@
                 ExpectedPadding.toIntPx().toFloat()
             )
             assertThat(labelPosition.value?.y).isEqualTo(
-                ((height.toIntPx() - labelSize.value!!.height) / 2f).roundToInt().toFloat()
+                TextFieldPadding.toIntPx()
             )
         }
     }
@@ -504,9 +543,7 @@
                 ExpectedPadding.toIntPx().toFloat()
             )
             assertThat(placeholderPosition.value?.y).isEqualTo(
-                ((height.toIntPx().toFloat() - placeholderSize.value!!.height) / 2f)
-                    .roundToInt()
-                    .toFloat()
+                TextFieldPadding.toIntPx()
             )
         }
     }
@@ -823,7 +860,7 @@
             Box(Modifier.background(color = Color.White)) {
                 TextField(
                     modifier = Modifier
-                        .focusObserver { if (it.isFocused) latch.countDown() }
+                        .onFocusChanged { if (it.isFocused) latch.countDown() }
                         .testTag(TextfieldTag),
                     value = "",
                     onValueChange = {},
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 2a7221d..593921c 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
@@ -90,6 +90,7 @@
             // TODO: move the modifiers to FlowRow when it supports a modifier parameter
             Box(Modifier.fillMaxWidth().padding(all = 8.dp)) {
                 @OptIn(ExperimentalLayout::class)
+                @Suppress("DEPRECATION")
                 FlowRow(
                     mainAxisSize = SizeMode.Expand,
                     mainAxisAlignment = MainAxisAlignment.End,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
index 8fef88c..682db4a 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
@@ -23,8 +23,8 @@
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
@@ -38,6 +38,7 @@
 import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.gesture.nestedscroll.nestedScroll
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.gesture.tapGestureFilter
 import androidx.compose.ui.graphics.Color
@@ -48,6 +49,7 @@
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.offset
 import androidx.compose.ui.util.fastForEach
@@ -86,7 +88,7 @@
 class BackdropScaffoldState(
     initialValue: BackdropValue,
     clock: AnimationClockObservable,
-    animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
+    animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
     confirmStateChange: (BackdropValue) -> Boolean = { true },
     val snackbarHostState: SnackbarHostState = SnackbarHostState()
 ) : SwipeableState<BackdropValue>(
@@ -139,6 +141,8 @@
         )
     }
 
+    internal val nestedScrollConnection = this.PreUpPostDownNestedScrollConnection
+
     companion object {
         /**
          * The default [Saver] implementation for [BackdropScaffoldState].
@@ -177,7 +181,7 @@
 fun rememberBackdropScaffoldState(
     initialValue: BackdropValue,
     clock: AnimationClockObservable = AmbientAnimationClock.current,
-    animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
+    animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
     confirmStateChange: (BackdropValue) -> Boolean = { true },
     snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }
 ): BackdropScaffoldState {
@@ -268,17 +272,17 @@
     modifier: Modifier = Modifier,
     scaffoldState: BackdropScaffoldState = rememberBackdropScaffoldState(Concealed),
     gesturesEnabled: Boolean = true,
-    peekHeight: Dp = BackdropScaffoldConstants.DefaultPeekHeight,
-    headerHeight: Dp = BackdropScaffoldConstants.DefaultHeaderHeight,
+    peekHeight: Dp = BackdropScaffoldDefaults.PeekHeight,
+    headerHeight: Dp = BackdropScaffoldDefaults.HeaderHeight,
     persistentAppBar: Boolean = true,
     stickyFrontLayer: Boolean = true,
     backLayerBackgroundColor: Color = MaterialTheme.colors.primary,
     backLayerContentColor: Color = contentColorFor(backLayerBackgroundColor),
-    frontLayerShape: Shape = BackdropScaffoldConstants.DefaultFrontLayerShape,
-    frontLayerElevation: Dp = BackdropScaffoldConstants.DefaultFrontLayerElevation,
+    frontLayerShape: Shape = BackdropScaffoldDefaults.frontLayerShape,
+    frontLayerElevation: Dp = BackdropScaffoldDefaults.FrontLayerElevation,
     frontLayerBackgroundColor: Color = MaterialTheme.colors.surface,
     frontLayerContentColor: Color = contentColorFor(frontLayerBackgroundColor),
-    frontLayerScrimColor: Color = BackdropScaffoldConstants.DefaultFrontLayerScrimColor,
+    frontLayerScrimColor: Color = BackdropScaffoldDefaults.frontLayerScrimColor,
     snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
     appBar: @Composable () -> Unit,
     backLayerContent: @Composable () -> Unit,
@@ -317,19 +321,23 @@
                 revealedHeight = min(revealedHeight, backLayerHeight)
             }
 
-            val swipeable = Modifier.swipeable(
-                state = scaffoldState,
-                anchors = mapOf(
-                    peekHeightPx to Concealed,
-                    revealedHeight to Revealed
-                ),
-                orientation = Orientation.Vertical,
-                enabled = gesturesEnabled
-            )
+            val swipeable = Modifier
+                .nestedScroll(scaffoldState.nestedScrollConnection)
+                .swipeable(
+                    state = scaffoldState,
+                    anchors = mapOf(
+                        peekHeightPx to Concealed,
+                        revealedHeight to Revealed
+                    ),
+                    orientation = Orientation.Vertical,
+                    enabled = gesturesEnabled
+                )
 
             // Front layer
             Surface(
-                Modifier.offset(y = { scaffoldState.offset.value }).then(swipeable),
+                Modifier
+                    .offset { IntOffset(0, scaffoldState.offset.value.roundToInt()) }
+                    .then(swipeable),
                 shape = frontLayerShape,
                 elevation = frontLayerElevation,
                 color = frontLayerBackgroundColor,
@@ -461,6 +469,13 @@
 /**
  * Contains useful constants for [BackdropScaffold].
  */
+@Deprecated(
+    "BackdropScaffoldConstants has been replaced with BackdropScaffoldDefaults",
+    ReplaceWith(
+        "BackdropScaffoldDefaults",
+        "androidx.compose.material.BackdropScaffoldDefaults"
+    )
+)
 object BackdropScaffoldConstants {
 
     /**
@@ -493,4 +508,39 @@
         @Composable get() = MaterialTheme.colors.surface.copy(alpha = 0.60f)
 }
 
+/**
+ * Contains useful defaults for [BackdropScaffold].
+ */
+object BackdropScaffoldDefaults {
+
+    /**
+     * The default peek height of the back layer.
+     */
+    val PeekHeight = 56.dp
+
+    /**
+     * The default header height of the front layer.
+     */
+    val HeaderHeight = 48.dp
+
+    /**
+     * The default shape of the front layer.
+     */
+    val frontLayerShape: Shape
+        @Composable
+        get() = MaterialTheme.shapes.large
+            .copy(topLeft = CornerSize(16.dp), topRight = CornerSize(16.dp))
+
+    /**
+     * The default elevation of the front layer.
+     */
+    val FrontLayerElevation = 1.dp
+
+    /**
+     * The default color of the scrim applied to the front layer.
+     */
+    val frontLayerScrimColor: Color
+        @Composable get() = MaterialTheme.colors.surface.copy(alpha = 0.60f)
+}
+
 private val AnimationSlideOffset = 20.dp
\ No newline at end of file
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
index ba1e9b6..c210065 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
@@ -37,12 +37,13 @@
 import androidx.compose.runtime.savedinstancestate.Saver
 import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.WithConstraints
+import androidx.compose.ui.gesture.nestedscroll.nestedScroll
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.WithConstraints
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.platform.AmbientDensity
@@ -79,7 +80,7 @@
 class BottomSheetState(
     initialValue: BottomSheetValue,
     clock: AnimationClockObservable,
-    animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
+    animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
     confirmStateChange: (BottomSheetValue) -> Boolean = { true }
 ) : SwipeableState<BottomSheetValue>(
     initialValue = initialValue,
@@ -151,6 +152,8 @@
             }
         )
     }
+
+    internal val nestedScrollConnection = this.PreUpPostDownNestedScrollConnection
 }
 
 /**
@@ -164,7 +167,7 @@
 @ExperimentalMaterialApi
 fun rememberBottomSheetState(
     initialValue: BottomSheetValue,
-    animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
+    animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
     confirmStateChange: (BottomSheetValue) -> Boolean = { true }
 ): BottomSheetState {
     val disposableClock = AmbientAnimationClock.current.asDisposableClock()
@@ -279,17 +282,17 @@
     floatingActionButtonPosition: FabPosition = FabPosition.End,
     sheetGesturesEnabled: Boolean = true,
     sheetShape: Shape = MaterialTheme.shapes.large,
-    sheetElevation: Dp = BottomSheetScaffoldConstants.DefaultSheetElevation,
+    sheetElevation: Dp = BottomSheetScaffoldDefaults.SheetElevation,
     sheetBackgroundColor: Color = MaterialTheme.colors.surface,
     sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
-    sheetPeekHeight: Dp = BottomSheetScaffoldConstants.DefaultSheetPeekHeight,
+    sheetPeekHeight: Dp = BottomSheetScaffoldDefaults.SheetPeekHeight,
     drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
     drawerGesturesEnabled: Boolean = true,
     drawerShape: Shape = MaterialTheme.shapes.large,
-    drawerElevation: Dp = DrawerConstants.DefaultElevation,
+    drawerElevation: Dp = DrawerDefaults.Elevation,
     drawerBackgroundColor: Color = MaterialTheme.colors.surface,
     drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
-    drawerScrimColor: Color = DrawerConstants.defaultScrimColor,
+    drawerScrimColor: Color = DrawerDefaults.scrimColor,
     backgroundColor: Color = MaterialTheme.colors.background,
     contentColor: Color = contentColorFor(backgroundColor),
     bodyContent: @Composable (PaddingValues) -> Unit
@@ -299,16 +302,18 @@
         val peekHeightPx = with(AmbientDensity.current) { sheetPeekHeight.toPx() }
         var bottomSheetHeight by remember { mutableStateOf(fullHeight) }
 
-        val swipeable = Modifier.swipeable(
-            state = scaffoldState.bottomSheetState,
-            anchors = mapOf(
-                fullHeight - peekHeightPx to BottomSheetValue.Collapsed,
-                fullHeight - bottomSheetHeight to BottomSheetValue.Expanded
-            ),
-            orientation = Orientation.Vertical,
-            enabled = sheetGesturesEnabled,
-            resistance = null
-        )
+        val swipeable = Modifier
+            .nestedScroll(scaffoldState.bottomSheetState.nestedScrollConnection)
+            .swipeable(
+                state = scaffoldState.bottomSheetState,
+                anchors = mapOf(
+                    fullHeight - peekHeightPx to BottomSheetValue.Collapsed,
+                    fullHeight - bottomSheetHeight to BottomSheetValue.Expanded
+                ),
+                orientation = Orientation.Vertical,
+                enabled = sheetGesturesEnabled,
+                resistance = null
+            )
 
         val child = @Composable {
             BottomSheetScaffoldStack(
@@ -422,6 +427,13 @@
 /**
  * Contains useful constants for [BottomSheetScaffold].
  */
+@Deprecated(
+    message = "BottomSheetScaffoldConstants has been replaced with BottomSheetScaffoldDefaults",
+    ReplaceWith(
+        "BottomSheetScaffoldDefaults",
+        "androidx.compose.material.BottomSheetScaffoldDefaults"
+    )
+)
 object BottomSheetScaffoldConstants {
 
     /**
@@ -433,4 +445,20 @@
      * The default peek height used by [BottomSheetScaffold].
      */
     val DefaultSheetPeekHeight = 56.dp
+}
+
+/**
+ * Contains useful defaults for [BottomSheetScaffold].
+ */
+object BottomSheetScaffoldDefaults {
+
+    /**
+     * The default elevation used by [BottomSheetScaffold].
+     */
+    val SheetElevation = 8.dp
+
+    /**
+     * The default peek height used by [BottomSheetScaffold].
+     */
+    val SheetPeekHeight = 56.dp
 }
\ No newline at end of file
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 3ab1eb9..b23e6fe 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
@@ -19,10 +19,10 @@
 package androidx.compose.material
 
 import androidx.compose.animation.AnimatedValueModel
-import androidx.compose.animation.VectorConverter
 import androidx.compose.animation.asDisposableClock
 import androidx.compose.animation.core.AnimationClockObservable
 import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.VectorConverter
 import androidx.compose.foundation.AmbientIndication
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.Interaction
@@ -82,11 +82,11 @@
  * it is [Interaction.Pressed].
  * @param elevation [ButtonElevation] used to resolve the elevation for this button in different
  * states. This controls the size of the shadow below the button. Pass `null` here to disable
- * elevation for this button. See [ButtonConstants.defaultElevation].
+ * elevation for this button. See [ButtonDefaults.elevation].
  * @param shape Defines the button's shape as well as its shadow
  * @param border Border to draw around the button
  * @param colors [ButtonColors] that will be used to resolve the background and content color for
- * this button in different states. See [ButtonConstants.defaultButtonColors].
+ * this button in different states. See [ButtonDefaults.buttonColors].
  * @param contentPadding The spacing values to apply internally between the container and the content
  */
 @OptIn(ExperimentalMaterialApi::class)
@@ -96,11 +96,11 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     interactionState: InteractionState = remember { InteractionState() },
-    elevation: ButtonElevation? = ButtonConstants.defaultElevation(),
+    elevation: ButtonElevation? = ButtonDefaults.elevation(),
     shape: Shape = MaterialTheme.shapes.small,
     border: BorderStroke? = null,
-    colors: ButtonColors = ButtonConstants.defaultButtonColors(),
-    contentPadding: PaddingValues = ButtonConstants.DefaultContentPadding,
+    colors: ButtonColors = ButtonDefaults.buttonColors(),
+    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
     content: @Composable RowScope.() -> Unit
 ) {
     // TODO(aelias): Avoid manually putting the clickable above the clip and
@@ -128,8 +128,8 @@
                 Row(
                     Modifier
                         .defaultMinSizeConstraints(
-                            minWidth = ButtonConstants.DefaultMinWidth,
-                            minHeight = ButtonConstants.DefaultMinHeight
+                            minWidth = ButtonDefaults.MinWidth,
+                            minHeight = ButtonDefaults.MinHeight
                         )
                         .indication(interactionState, AmbientIndication.current())
                         .padding(contentPadding),
@@ -176,7 +176,7 @@
  * @param shape Defines the button's shape as well as its shadow
  * @param border Border to draw around the button
  * @param colors [ButtonColors] that will be used to resolve the background and content color for
- * this button in different states. See [ButtonConstants.defaultOutlinedButtonColors].
+ * this button in different states. See [ButtonDefaults.outlinedButtonColors].
  * @param contentPadding The spacing values to apply internally between the container and the content
  */
 @OptIn(ExperimentalMaterialApi::class)
@@ -188,9 +188,9 @@
     interactionState: InteractionState = remember { InteractionState() },
     elevation: ButtonElevation? = null,
     shape: Shape = MaterialTheme.shapes.small,
-    border: BorderStroke? = ButtonConstants.defaultOutlinedBorder,
-    colors: ButtonColors = ButtonConstants.defaultOutlinedButtonColors(),
-    contentPadding: PaddingValues = ButtonConstants.DefaultContentPadding,
+    border: BorderStroke? = ButtonDefaults.outlinedBorder,
+    colors: ButtonColors = ButtonDefaults.outlinedButtonColors(),
+    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
     noinline content: @Composable RowScope.() -> Unit
 ) = Button(
     onClick = onClick,
@@ -236,7 +236,7 @@
  * @param shape Defines the button's shape as well as its shadow
  * @param border Border to draw around the button
  * @param colors [ButtonColors] that will be used to resolve the background and content color for
- * this button in different states. See [ButtonConstants.defaultTextButtonColors].
+ * this button in different states. See [ButtonDefaults.textButtonColors].
  * @param contentPadding The spacing values to apply internally between the container and the content
  */
 @OptIn(ExperimentalMaterialApi::class)
@@ -249,8 +249,8 @@
     elevation: ButtonElevation? = null,
     shape: Shape = MaterialTheme.shapes.small,
     border: BorderStroke? = null,
-    colors: ButtonColors = ButtonConstants.defaultTextButtonColors(),
-    contentPadding: PaddingValues = ButtonConstants.DefaultTextContentPadding,
+    colors: ButtonColors = ButtonDefaults.textButtonColors(),
+    contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
     noinline content: @Composable RowScope.() -> Unit
 ) = Button(
     onClick = onClick,
@@ -268,7 +268,7 @@
 /**
  * Represents the elevation for a button in different states.
  *
- * See [ButtonConstants.defaultElevation] for the default elevation used in a [Button].
+ * See [ButtonDefaults.elevation] for the default elevation used in a [Button].
  */
 @ExperimentalMaterialApi
 @Stable
@@ -285,10 +285,10 @@
 /**
  * Represents the background and content colors used in a button in different states.
  *
- * See [ButtonConstants.defaultButtonColors] for the default colors used in a [Button].
- * See [ButtonConstants.defaultOutlinedButtonColors] for the default colors used in a
+ * See [ButtonDefaults.buttonColors] for the default colors used in a [Button].
+ * See [ButtonDefaults.outlinedButtonColors] for the default colors used in a
  * [OutlinedButton].
- * See [ButtonConstants.defaultTextButtonColors] for the default colors used in a [TextButton].
+ * See [ButtonDefaults.textButtonColors] for the default colors used in a [TextButton].
  */
 @ExperimentalMaterialApi
 @Stable
@@ -311,6 +311,13 @@
 /**
  * Contains the default values used by [Button]
  */
+@Deprecated(
+    "ButtonConstants has been replaced with ButtonDefaults",
+    ReplaceWith(
+        "ButtonDefaults",
+        "androidx.compose.material.ButtonDefaults"
+    )
+)
 object ButtonConstants {
     private val ButtonHorizontalPadding = 16.dp
     private val ButtonVerticalPadding = 8.dp
@@ -364,6 +371,13 @@
      */
     @OptIn(ExperimentalMaterialApi::class)
     @Composable
+    @Deprecated(
+        "ButtonConstants has been replaced with ButtonDefaults",
+        ReplaceWith(
+            "ButtonDefaults.elevation(elevation, pressedElevation, disabledElevation)",
+            "androidx.compose.material.ButtonDefaults"
+        )
+    )
     fun defaultElevation(
         defaultElevation: Dp = 2.dp,
         pressedElevation: Dp = 8.dp,
@@ -393,6 +407,14 @@
      */
     @OptIn(ExperimentalMaterialApi::class)
     @Composable
+    @Deprecated(
+        "ButtonConstants has been replaced with ButtonDefaults",
+        ReplaceWith(
+            "ButtonDefaults.buttonColors(backgroundColor, disabledBackgroundColor, contentColor, " +
+                "disabledContentColor)",
+            "androidx.compose.material.ButtonDefaults"
+        )
+    )
     fun defaultButtonColors(
         backgroundColor: Color = MaterialTheme.colors.primary,
         disabledBackgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.12f)
@@ -417,6 +439,14 @@
      */
     @OptIn(ExperimentalMaterialApi::class)
     @Composable
+    @Deprecated(
+        "ButtonConstants has been replaced with ButtonDefaults",
+        ReplaceWith(
+            "ButtonDefaults.outlinedButtonColors(backgroundColor, disabledBackgroundColor, " +
+                "contentColor, disabledContentColor)",
+            "androidx.compose.material.ButtonDefaults"
+        )
+    )
     fun defaultOutlinedButtonColors(
         backgroundColor: Color = MaterialTheme.colors.surface,
         contentColor: Color = MaterialTheme.colors.primary,
@@ -439,6 +469,14 @@
      */
     @OptIn(ExperimentalMaterialApi::class)
     @Composable
+    @Deprecated(
+        "ButtonConstants has been replaced with ButtonDefaults",
+        ReplaceWith(
+            "ButtonDefaults.textButtonColors(backgroundColor, disabledBackgroundColor, " +
+                "contentColor, disabledContentColor)",
+            "androidx.compose.material.ButtonDefaults"
+        )
+    )
     fun defaultTextButtonColors(
         backgroundColor: Color = Color.Transparent,
         contentColor: Color = MaterialTheme.colors.primary,
@@ -482,6 +520,179 @@
 }
 
 /**
+ * Contains the default values used by [Button]
+ */
+object ButtonDefaults {
+    private val ButtonHorizontalPadding = 16.dp
+    private val ButtonVerticalPadding = 8.dp
+
+    /**
+     * The default content padding used by [Button]
+     */
+    val ContentPadding = PaddingValues(
+        start = ButtonHorizontalPadding,
+        top = ButtonVerticalPadding,
+        end = ButtonHorizontalPadding,
+        bottom = ButtonVerticalPadding
+    )
+
+    /**
+     * The default min width applied for the [Button].
+     * Note that you can override it by applying Modifier.widthIn directly on [Button].
+     */
+    val MinWidth = 64.dp
+
+    /**
+     * The default min width applied for the [Button].
+     * Note that you can override it by applying Modifier.heightIn directly on [Button].
+     */
+    val MinHeight = 36.dp
+
+    /**
+     * The default size of the icon when used inside a [Button].
+     *
+     * @sample androidx.compose.material.samples.ButtonWithIconSample
+     */
+    val IconSize = 18.dp
+
+    /**
+     * The default size of the spacing between an icon and a text when they used inside a [Button].
+     *
+     * @sample androidx.compose.material.samples.ButtonWithIconSample
+     */
+    val IconSpacing = 8.dp
+
+    // TODO: b/152525426 add support for focused and hovered states
+    /**
+     * Creates a [ButtonElevation] that will animate between the provided values according to the
+     * Material specification for a [Button].
+     *
+     * @param defaultElevation the elevation to use when the [Button] is enabled, and has no
+     * other [Interaction]s.
+     * @param pressedElevation the elevation to use when the [Button] is enabled and
+     * is [Interaction.Pressed].
+     * @param disabledElevation the elevation to use when the [Button] is not enabled.
+     */
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    fun elevation(
+        defaultElevation: Dp = 2.dp,
+        pressedElevation: Dp = 8.dp,
+        // focused: Dp = 4.dp,
+        // hovered: Dp = 4.dp,
+        disabledElevation: Dp = 0.dp
+    ): ButtonElevation {
+        val clock = AmbientAnimationClock.current.asDisposableClock()
+        return remember(defaultElevation, pressedElevation, disabledElevation, clock) {
+            DefaultButtonElevation(
+                defaultElevation = defaultElevation,
+                pressedElevation = pressedElevation,
+                disabledElevation = disabledElevation,
+                clock = clock
+            )
+        }
+    }
+
+    /**
+     * Creates a [ButtonColors] that represents the default background and content colors used in
+     * a [Button].
+     *
+     * @param backgroundColor the background color of this [Button] when enabled
+     * @param disabledBackgroundColor the background color of this [Button] when not enabled
+     * @param contentColor the content color of this [Button] when enabled
+     * @param disabledContentColor the content color of this [Button] when not enabled
+     */
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    fun buttonColors(
+        backgroundColor: Color = MaterialTheme.colors.primary,
+        disabledBackgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.12f)
+            .compositeOver(MaterialTheme.colors.surface),
+        contentColor: Color = contentColorFor(backgroundColor),
+        disabledContentColor: Color = MaterialTheme.colors.onSurface
+            .copy(alpha = ContentAlpha.disabled)
+    ): ButtonColors = DefaultButtonColors(
+        backgroundColor,
+        disabledBackgroundColor,
+        contentColor,
+        disabledContentColor
+    )
+
+    /**
+     * Creates a [ButtonColors] that represents the default background and content colors used in
+     * an [OutlinedButton].
+     *
+     * @param backgroundColor the background color of this [OutlinedButton]
+     * @param contentColor the content color of this [OutlinedButton] when enabled
+     * @param disabledContentColor the content color of this [OutlinedButton] when not enabled
+     */
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    fun outlinedButtonColors(
+        backgroundColor: Color = MaterialTheme.colors.surface,
+        contentColor: Color = MaterialTheme.colors.primary,
+        disabledContentColor: Color = MaterialTheme.colors.onSurface
+            .copy(alpha = ContentAlpha.disabled)
+    ): ButtonColors = DefaultButtonColors(
+        backgroundColor,
+        backgroundColor,
+        contentColor,
+        disabledContentColor
+    )
+
+    /**
+     * Creates a [ButtonColors] that represents the default background and content colors used in
+     * a [TextButton].
+     *
+     * @param backgroundColor the background color of this [TextButton]
+     * @param contentColor the content color of this [TextButton] when enabled
+     * @param disabledContentColor the content color of this [TextButton] when not enabled
+     */
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    fun textButtonColors(
+        backgroundColor: Color = Color.Transparent,
+        contentColor: Color = MaterialTheme.colors.primary,
+        disabledContentColor: Color = MaterialTheme.colors.onSurface
+            .copy(alpha = ContentAlpha.disabled)
+    ): ButtonColors = DefaultButtonColors(
+        backgroundColor,
+        backgroundColor,
+        contentColor,
+        disabledContentColor
+    )
+
+    /**
+     * The default color opacity used for an [OutlinedButton]'s border color
+     */
+    const val OutlinedBorderOpacity = 0.12f
+
+    /**
+     * The default [OutlinedButton]'s border size
+     */
+    val OutlinedBorderSize = 1.dp
+
+    /**
+     * The default disabled content color used by all types of [Button]s
+     */
+    val outlinedBorder: BorderStroke
+        @Composable
+        get() = BorderStroke(
+            OutlinedBorderSize, MaterialTheme.colors.onSurface.copy(alpha = OutlinedBorderOpacity)
+        )
+
+    private val TextButtonHorizontalPadding = 8.dp
+
+    /**
+     * The default content padding used by [TextButton]
+     */
+    val TextButtonContentPadding = ContentPadding.copy(
+        start = TextButtonHorizontalPadding,
+        end = TextButtonHorizontalPadding
+    )
+}
+
+/**
  * Default [ButtonElevation] implementation.
  */
 @OptIn(ExperimentalMaterialApi::class)
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 6b8cfc3..811d694 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
@@ -76,7 +76,7 @@
  * [InteractionState] if you want to read the [InteractionState] and customize the appearance /
  * behavior of this Checkbox in different [Interaction]s.
  * @param colors [CheckboxColors] that will be used to determine the color of the checkmark / box
- * / border in different states. See [CheckboxConstants.defaultColors].
+ * / border in different states. See [CheckboxDefaults.colors].
  */
 @OptIn(ExperimentalMaterialApi::class)
 @Composable
@@ -86,7 +86,7 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     interactionState: InteractionState = remember { InteractionState() },
-    colors: CheckboxColors = CheckboxConstants.defaultColors()
+    colors: CheckboxColors = CheckboxDefaults.colors()
 ) {
     TriStateCheckbox(
         state = ToggleableState(checked),
@@ -120,7 +120,7 @@
  * [InteractionState] if you want to read the [InteractionState] and customize the appearance /
  * behavior of this TriStateCheckbox in different [Interaction]s.
  * @param colors [CheckboxColors] that will be used to determine the color of the checkmark / box
- * / border in different states. See [CheckboxConstants.defaultColors].
+ * / border in different states. See [CheckboxDefaults.colors].
  */
 @OptIn(ExperimentalMaterialApi::class)
 @Composable
@@ -130,7 +130,7 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     interactionState: InteractionState = remember { InteractionState() },
-    colors: CheckboxColors = CheckboxConstants.defaultColors()
+    colors: CheckboxColors = CheckboxDefaults.colors()
 ) {
     CheckboxImpl(
         enabled = enabled,
@@ -155,7 +155,7 @@
  * Represents the colors used by the three different sections (checkmark, box, and border) of a
  * [Checkbox] or [TriStateCheckbox] in different states.
  *
- * See [CheckboxConstants.defaultColors] for the default implementation that follows Material
+ * See [CheckboxDefaults.colors] for the default implementation that follows Material
  * specifications.
  */
 @ExperimentalMaterialApi
@@ -190,6 +190,13 @@
 /**
  * Constants used in [Checkbox] and [TriStateCheckbox].
  */
+@Deprecated(
+    "CheckboxConstants has been replaced with CheckboxDefaults",
+    ReplaceWith(
+        "CheckboxDefaults",
+        "androidx.compose.material.CheckboxDefaults"
+    )
+)
 object CheckboxConstants {
     /**
      * Creates a [CheckboxColors] that will animate between the provided colors according to the
@@ -204,6 +211,14 @@
      */
     @OptIn(ExperimentalMaterialApi::class)
     @Composable
+    @Deprecated(
+        "CheckboxConstants has been replaced with CheckboxDefaults",
+        ReplaceWith(
+            "CheckboxDefaults.colors(checkedColor, uncheckedColor, checkmarkColor, disabledColor," +
+                " disabledIndeterminateColor)",
+            "androidx.compose.material.CheckboxDefaults"
+        )
+    )
     fun defaultColors(
         checkedColor: Color = MaterialTheme.colors.secondary,
         uncheckedColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
@@ -238,6 +253,57 @@
     }
 }
 
+/**
+ * Defaults used in [Checkbox] and [TriStateCheckbox].
+ */
+object CheckboxDefaults {
+    /**
+     * Creates a [CheckboxColors] that will animate between the provided colors according to the
+     * Material specification.
+     *
+     * @param checkedColor the color that will be used for the border and box when checked
+     * @param uncheckedColor color that will be used for the border when unchecked
+     * @param checkmarkColor color that will be used for the checkmark when checked
+     * @param disabledColor color that will be used for the box and border when disabled
+     * @param disabledIndeterminateColor color that will be used for the box and
+     * border in a [TriStateCheckbox] when disabled AND in an [ToggleableState.Indeterminate] state.
+     */
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    fun colors(
+        checkedColor: Color = MaterialTheme.colors.secondary,
+        uncheckedColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
+        checkmarkColor: Color = MaterialTheme.colors.surface,
+        disabledColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled),
+        disabledIndeterminateColor: Color = checkedColor.copy(alpha = ContentAlpha.disabled)
+    ): CheckboxColors {
+        val clock = AmbientAnimationClock.current.asDisposableClock()
+        return remember(
+            checkedColor,
+            uncheckedColor,
+            checkmarkColor,
+            disabledColor,
+            disabledIndeterminateColor,
+            clock
+        ) {
+            DefaultCheckboxColors(
+                checkedBorderColor = checkedColor,
+                checkedBoxColor = checkedColor,
+                checkedCheckmarkColor = checkmarkColor,
+                uncheckedCheckmarkColor = checkmarkColor.copy(alpha = 0f),
+                uncheckedBoxColor = checkedColor.copy(alpha = 0f),
+                disabledCheckedBoxColor = disabledColor,
+                disabledUncheckedBoxColor = disabledColor.copy(alpha = 0f),
+                disabledIndeterminateBoxColor = disabledIndeterminateColor,
+                uncheckedBorderColor = uncheckedColor,
+                disabledBorderColor = disabledColor,
+                disabledIndeterminateBorderColor = disabledIndeterminateColor,
+                clock = clock
+            )
+        }
+    }
+}
+
 @OptIn(ExperimentalMaterialApi::class)
 @Composable
 private fun CheckboxImpl(
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Colors.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Colors.kt
index 493497b..2881b3c 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Colors.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Colors.kt
@@ -24,7 +24,7 @@
 import androidx.compose.runtime.staticAmbientOf
 import androidx.compose.runtime.structuralEqualityPolicy
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.useOrElse
+import androidx.compose.ui.graphics.takeOrElse
 
 /**
  * Collection of colors in the
@@ -260,7 +260,7 @@
  */
 @Composable
 fun contentColorFor(color: Color) =
-    MaterialTheme.colors.contentColorFor(color).useOrElse { AmbientContentColor.current }
+    MaterialTheme.colors.contentColorFor(color).takeOrElse { AmbientContentColor.current }
 
 /**
  * Updates the internal values of the given [Colors] with values from the [other] [Colors]. This
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
index 6966244..95656b7 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
@@ -21,9 +21,9 @@
 import androidx.compose.animation.core.AnimationEndReason
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
@@ -34,20 +34,23 @@
 import androidx.compose.runtime.savedinstancestate.Saver
 import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.WithConstraints
+import androidx.compose.ui.gesture.nestedscroll.nestedScroll
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.gesture.tapGestureFilter
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.layout.WithConstraints
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.AmbientLayoutDirection
 import androidx.compose.ui.semantics.dismiss
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.lerp
+import kotlin.math.roundToInt
 
 /**
  * Possible values of [DrawerState].
@@ -253,6 +256,8 @@
         )
     }
 
+    internal val nestedScrollConnection = this.PreUpPostDownNestedScrollConnection
+
     companion object {
         /**
          * The default [Saver] implementation for [BottomDrawerState].
@@ -342,10 +347,10 @@
     drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
     gesturesEnabled: Boolean = true,
     drawerShape: Shape = MaterialTheme.shapes.large,
-    drawerElevation: Dp = DrawerConstants.DefaultElevation,
+    drawerElevation: Dp = DrawerDefaults.Elevation,
     drawerBackgroundColor: Color = MaterialTheme.colors.surface,
     drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
-    scrimColor: Color = DrawerConstants.defaultScrimColor,
+    scrimColor: Color = DrawerDefaults.scrimColor,
     bodyContent: @Composable () -> Unit
 ) {
     WithConstraints(modifier.fillMaxSize()) {
@@ -394,7 +399,7 @@
                             dismiss(action = { drawerState.close(); true })
                         }
                     }
-                    .offset(x = { drawerState.offset.value })
+                    .offset { IntOffset(drawerState.offset.value.roundToInt(), 0) }
                     .padding(end = VerticalDrawerPadding),
                 shape = drawerShape,
                 color = drawerBackgroundColor,
@@ -445,10 +450,10 @@
     drawerState: BottomDrawerState = rememberBottomDrawerState(BottomDrawerValue.Closed),
     gesturesEnabled: Boolean = true,
     drawerShape: Shape = MaterialTheme.shapes.large,
-    drawerElevation: Dp = DrawerConstants.DefaultElevation,
+    drawerElevation: Dp = DrawerDefaults.Elevation,
     drawerBackgroundColor: Color = MaterialTheme.colors.surface,
     drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
-    scrimColor: Color = DrawerConstants.defaultScrimColor,
+    scrimColor: Color = DrawerDefaults.scrimColor,
     bodyContent: @Composable () -> Unit
 ) {
     WithConstraints(modifier.fillMaxSize()) {
@@ -481,13 +486,15 @@
                 )
             }
         Box(
-            Modifier.swipeable(
-                state = drawerState,
-                anchors = anchors,
-                orientation = Orientation.Vertical,
-                enabled = gesturesEnabled,
-                resistance = null
-            )
+            Modifier
+                .nestedScroll(drawerState.nestedScrollConnection)
+                .swipeable(
+                    state = drawerState,
+                    anchors = anchors,
+                    orientation = Orientation.Vertical,
+                    enabled = gesturesEnabled,
+                    resistance = null
+                )
         ) {
             Box {
                 bodyContent()
@@ -514,8 +521,7 @@
                         if (drawerState.isOpen) {
                             dismiss(action = { drawerState.close(); true })
                         }
-                    }
-                    .offset(y = { drawerState.offset.value }),
+                    }.offset { IntOffset(0, drawerState.offset.value.roundToInt()) },
                 shape = drawerShape,
                 color = drawerBackgroundColor,
                 contentColor = drawerContentColor,
@@ -530,6 +536,13 @@
 /**
  * Object to hold default values for [ModalDrawerLayout] and [BottomDrawerLayout]
  */
+@Deprecated(
+    "DrawerConstants has been replaced with DrawerDefaults",
+    ReplaceWith(
+        "DrawerDefaults",
+        "androidx.compose.material.DrawerDefaults"
+    )
+)
 object DrawerConstants {
 
     /**
@@ -547,6 +560,26 @@
     const val ScrimDefaultOpacity = 0.32f
 }
 
+/**
+ * Object to hold default values for [ModalDrawerLayout] and [BottomDrawerLayout]
+ */
+object DrawerDefaults {
+
+    /**
+     * Default Elevation for drawer sheet as specified in material specs
+     */
+    val Elevation = 16.dp
+
+    val scrimColor: Color
+        @Composable
+        get() = MaterialTheme.colors.onSurface.copy(alpha = ScrimOpacity)
+
+    /**
+     * Default alpha for scrim color
+     */
+    const val ScrimOpacity = 0.32f
+}
+
 private fun calculateFraction(a: Float, b: Float, pos: Float) =
     ((pos - a) / (b - a)).coerceIn(0f, 1f)
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Elevation.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Elevation.kt
index 781185d..daa9ef7 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Elevation.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Elevation.kt
@@ -29,8 +29,8 @@
 /**
  * Animates the [Dp] value of [this] between [from] and [to] [Interaction]s, to [target]. The
  * [AnimationSpec] used depends on the values for [from] and [to], see
- * [ElevationConstants.incomingAnimationSpecForInteraction] and
- * [ElevationConstants.outgoingAnimationSpecForInteraction] for more details.
+ * [ElevationDefaults.incomingAnimationSpecForInteraction] and
+ * [ElevationDefaults.outgoingAnimationSpecForInteraction] for more details.
  *
  * @param from the previous [Interaction] that was used to calculate elevation. `null` if there
  * was no previous [Interaction], such as when the component is in its default state.
@@ -47,9 +47,9 @@
 ) {
     val spec = when {
         // Moving to a new state
-        to != null -> ElevationConstants.incomingAnimationSpecForInteraction(to)
+        to != null -> ElevationDefaults.incomingAnimationSpecForInteraction(to)
         // Moving to default, from a previous state
-        from != null -> ElevationConstants.outgoingAnimationSpecForInteraction(from)
+        from != null -> ElevationDefaults.outgoingAnimationSpecForInteraction(from)
         // Loading the initial state, or moving back to the baseline state from a disabled /
         // unknown state, so just snap to the final value.
         else -> null
@@ -62,11 +62,18 @@
  *
  * Typically you should use [animateElevation] instead, which uses these [AnimationSpec]s
  * internally. [animateElevation] in turn is used by the defaults for [Button] and
- * [FloatingActionButton] - inside [ButtonConstants.defaultElevation] and
- * [FloatingActionButtonConstants.defaultElevation] respectively.
+ * [FloatingActionButton] - inside [ButtonDefaults.elevation] and
+ * [FloatingActionButtonDefaults.elevation] respectively.
  *
  * @see animateElevation
  */
+@Deprecated(
+    "ElevationConstants has been replaced with ElevationDefaults",
+    ReplaceWith(
+        "ElevationDefaults",
+        "androidx.compose.material.ElevationDefaults"
+    )
+)
 object ElevationConstants {
     /**
      * Returns the [AnimationSpec]s used when animating elevation to [interaction], either from a
@@ -99,6 +106,48 @@
     }
 }
 
+/**
+ * Contains default [AnimationSpec]s used for animating elevation between different [Interaction]s.
+ *
+ * Typically you should use [animateElevation] instead, which uses these [AnimationSpec]s
+ * internally. [animateElevation] in turn is used by the defaults for [Button] and
+ * [FloatingActionButton] - inside [ButtonDefaults.elevation] and
+ * [FloatingActionButtonDefaults.elevation] respectively.
+ *
+ * @see animateElevation
+ */
+object ElevationDefaults {
+    /**
+     * Returns the [AnimationSpec]s used when animating elevation to [interaction], either from a
+     * previous [Interaction], or from the default state. If [interaction] is unknown, then
+     * returns `null`.
+     *
+     * @param interaction the [Interaction] that is being animated to
+     */
+    fun incomingAnimationSpecForInteraction(interaction: Interaction): AnimationSpec<Dp>? {
+        return when (interaction) {
+            is Interaction.Pressed -> DefaultIncomingSpec
+            is Interaction.Dragged -> DefaultIncomingSpec
+            else -> null
+        }
+    }
+
+    /**
+     * Returns the [AnimationSpec]s used when animating elevation away from [interaction], to the
+     * default state. If [interaction] is unknown, then returns `null`.
+     *
+     * @param interaction the [Interaction] that is being animated away from
+     */
+    fun outgoingAnimationSpecForInteraction(interaction: Interaction): AnimationSpec<Dp>? {
+        return when (interaction) {
+            is Interaction.Pressed -> DefaultOutgoingSpec
+            is Interaction.Dragged -> DefaultOutgoingSpec
+            // TODO: use [HoveredOutgoingSpec] when hovered
+            else -> null
+        }
+    }
+}
+
 private val DefaultIncomingSpec = TweenSpec<Dp>(
     durationMillis = 120,
     easing = FastOutSlowInEasing
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 ab7e7e6..40fcf2b 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
@@ -17,10 +17,10 @@
 package androidx.compose.material
 
 import androidx.compose.animation.AnimatedValueModel
-import androidx.compose.animation.VectorConverter
 import androidx.compose.animation.asDisposableClock
 import androidx.compose.animation.core.AnimationClockObservable
 import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.VectorConverter
 import androidx.compose.foundation.AmbientIndication
 import androidx.compose.foundation.Interaction
 import androidx.compose.foundation.InteractionState
@@ -79,7 +79,7 @@
     shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
     backgroundColor: Color = MaterialTheme.colors.secondary,
     contentColor: Color = contentColorFor(backgroundColor),
-    elevation: FloatingActionButtonElevation = FloatingActionButtonConstants.defaultElevation(),
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
     content: @Composable () -> Unit
 ) {
     // TODO(aelias): Avoid manually managing the ripple once http://b/157687898
@@ -151,7 +151,7 @@
     shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
     backgroundColor: Color = MaterialTheme.colors.secondary,
     contentColor: Color = contentColorFor(backgroundColor),
-    elevation: FloatingActionButtonElevation = FloatingActionButtonConstants.defaultElevation()
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation()
 ) {
     FloatingActionButton(
         modifier = modifier.preferredSizeIn(
@@ -188,7 +188,7 @@
 /**
  * Represents the elevation for a floating action button in different states.
  *
- * See [FloatingActionButtonConstants.defaultElevation] for the default elevation used in a
+ * See [FloatingActionButtonDefaults.elevation] for the default elevation used in a
  * [FloatingActionButton] and [ExtendedFloatingActionButton].
  */
 @ExperimentalMaterialApi
@@ -205,6 +205,13 @@
 /**
  * Contains the default values used by [FloatingActionButton]
  */
+@Deprecated(
+    "FloatingActionButtonConstants has been replaced with FloatingActionButtonDefaults",
+    ReplaceWith(
+        "FloatingActionButtonDefaults",
+        "androidx.compose.material.FloatingActionButtonDefaults"
+    )
+)
 object FloatingActionButtonConstants {
     // TODO: b/152525426 add support for focused and hovered states
     /**
@@ -218,6 +225,15 @@
      */
     @OptIn(ExperimentalMaterialApi::class)
     @Composable
+    @Deprecated(
+        "FloatingActionButtonConstants has been replaced with " +
+            "FloatingActionButtonDefaults",
+        ReplaceWith(
+            "FloatingActionButtonDefaults.elevation(elevation, pressedElevation, " +
+                "disabledElevation)",
+            "androidx.compose.material.FloatingActionButtonDefaults"
+        )
+    )
     fun defaultElevation(
         defaultElevation: Dp = 6.dp,
         pressedElevation: Dp = 12.dp
@@ -236,6 +252,39 @@
 }
 
 /**
+ * Contains the default values used by [FloatingActionButton]
+ */
+object FloatingActionButtonDefaults {
+    // TODO: b/152525426 add support for focused and hovered states
+    /**
+     * Creates a [FloatingActionButtonElevation] that will animate between the provided values
+     * according to the Material specification.
+     *
+     * @param defaultElevation the elevation to use when the [FloatingActionButton] has no
+     * [Interaction]s
+     * @param pressedElevation the elevation to use when the [FloatingActionButton] is
+     * [Interaction.Pressed].
+     */
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    fun elevation(
+        defaultElevation: Dp = 6.dp,
+        pressedElevation: Dp = 12.dp
+        // focused: Dp = 8.dp,
+        // hovered: Dp = 8.dp,
+    ): FloatingActionButtonElevation {
+        val clock = AmbientAnimationClock.current.asDisposableClock()
+        return remember(defaultElevation, pressedElevation, clock) {
+            DefaultFloatingActionButtonElevation(
+                defaultElevation = defaultElevation,
+                pressedElevation = pressedElevation,
+                clock = clock
+            )
+        }
+    }
+}
+
+/**
  * Default [FloatingActionButtonElevation] implementation.
  */
 @OptIn(ExperimentalMaterialApi::class)
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 eaedb99..40b52dc 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
@@ -111,20 +111,20 @@
 private object OneLine {
     // TODO(popam): support wide icons
     // TODO(popam): convert these to sp
-    // List item related constants.
+    // List item related defaults.
     private val MinHeight = 48.dp
     private val MinHeightWithIcon = 56.dp
 
-    // Icon related constants.
+    // Icon related defaults.
     private val IconMinPaddedWidth = 40.dp
     private val IconLeftPadding = 16.dp
     private val IconVerticalPadding = 8.dp
 
-    // Content related constants.
+    // Content related defaults.
     private val ContentLeftPadding = 16.dp
     private val ContentRightPadding = 16.dp
 
-    // Trailing related constants.
+    // Trailing related defaults.
     private val TrailingRightPadding = 16.dp
 
     @Composable
@@ -166,16 +166,16 @@
 }
 
 private object TwoLine {
-    // List item related constants.
+    // List item related defaults.
     private val MinHeight = 64.dp
     private val MinHeightWithIcon = 72.dp
 
-    // Icon related constants.
+    // Icon related defaults.
     private val IconMinPaddedWidth = 40.dp
     private val IconLeftPadding = 16.dp
     private val IconVerticalPadding = 16.dp
 
-    // Content related constants.
+    // Content related defaults.
     private val ContentLeftPadding = 16.dp
     private val ContentRightPadding = 16.dp
     private val OverlineBaselineOffset = 24.dp
@@ -185,7 +185,7 @@
     private val PrimaryToSecondaryBaselineOffsetNoIcon = 20.dp
     private val PrimaryToSecondaryBaselineOffsetWithIcon = 20.dp
 
-    // Trailing related constants.
+    // Trailing related defaults.
     private val TrailingRightPadding = 16.dp
 
     @Composable
@@ -267,15 +267,15 @@
 }
 
 private object ThreeLine {
-    // List item related constants.
+    // List item related defaults.
     private val MinHeight = 88.dp
 
-    // Icon related constants.
+    // Icon related defaults.
     private val IconMinPaddedWidth = 40.dp
     private val IconLeftPadding = 16.dp
     private val IconThreeLineVerticalPadding = 16.dp
 
-    // Content related constants.
+    // Content related defaults.
     private val ContentLeftPadding = 16.dp
     private val ContentRightPadding = 16.dp
     private val ThreeLineBaselineFirstOffset = 28.dp
@@ -283,7 +283,7 @@
     private val ThreeLineBaselineThirdOffset = 20.dp
     private val ThreeLineTrailingTopPadding = 16.dp
 
-    // Trailing related constants.
+    // Trailing related defaults.
     private val TrailingRightPadding = 16.dp
 
     @Composable
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTextSelectionColors.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTextSelectionColors.kt
index 4cd516e..0f032e0 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTextSelectionColors.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTextSelectionColors.kt
@@ -21,7 +21,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.graphics.luminance
-import androidx.compose.ui.graphics.useOrElse
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.selection.TextSelectionColors
 import androidx.compose.ui.util.annotation.VisibleForTesting
 import kotlin.math.max
@@ -41,7 +41,7 @@
     // 'worst case' scenario. We explicitly don't test with ContentAlpha.disabled, as disabled
     // text shouldn't be selectable / is noted as disabled for accessibility purposes.
     val textColorWithLowestAlpha = colors.contentColorFor(backgroundColor)
-        .useOrElse {
+        .takeOrElse {
             AmbientContentColor.current
         }.copy(
             alpha = ContentAlpha.medium
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 f075e67..30cbd64 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
@@ -74,7 +74,8 @@
  * the [toggle], and then screen end-aligned. Vertically, it will try to expand to the bottom
  * of the [toggle], then from the top of the [toggle], and then screen top-aligned. A
  * [dropdownOffset] can be provided to adjust the positioning of the menu for cases when the
- * layout bounds of the [toggle] do not coincide with its visual bounds.
+ * layout bounds of the [toggle] do not coincide with its visual bounds. Note the offset will be
+ * applied in the direction in which the menu will decide to expand.
  *
  * Example usage:
  * @sample androidx.compose.material.samples.MenuSample
@@ -199,7 +200,7 @@
     }
 }
 
-// Size constants.
+// Size defaults.
 private val MenuElevation = 8.dp
 private val MenuVerticalMargin = 32.dp
 private val DropdownMenuHorizontalPadding = 16.dp
@@ -305,8 +306,8 @@
         val contentOffsetY = with(density) { contentOffset.y.toIntPx() }
 
         // Compute horizontal position.
-        val toRight = parentGlobalBounds.right + contentOffsetX
-        val toLeft = parentGlobalBounds.left - contentOffsetX - popupContentSize.width
+        val toRight = parentGlobalBounds.left + contentOffsetX
+        val toLeft = parentGlobalBounds.right - contentOffsetX - popupContentSize.width
         val toDisplayRight = windowGlobalBounds.width - popupContentSize.width
         val toDisplayLeft = 0
         val x = if (layoutDirection == LayoutDirection.Ltr) {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
index 0605c16..08925b9 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
@@ -23,9 +23,9 @@
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.offset
@@ -34,6 +34,7 @@
 import androidx.compose.runtime.savedinstancestate.Saver
 import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.gesture.nestedscroll.nestedScroll
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.gesture.tapGestureFilter
 import androidx.compose.ui.graphics.Color
@@ -42,8 +43,10 @@
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import kotlin.math.max
+import kotlin.math.roundToInt
 
 /**
  * Possible values of [ModalBottomSheetState].
@@ -79,7 +82,7 @@
 class ModalBottomSheetState(
     initialValue: ModalBottomSheetValue,
     clock: AnimationClockObservable,
-    animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
+    animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
     confirmStateChange: (ModalBottomSheetValue) -> Boolean = { true }
 ) : SwipeableState<ModalBottomSheetValue>(
     initialValue = initialValue,
@@ -131,6 +134,8 @@
         )
     }
 
+    internal val nestedScrollConnection = this.PreUpPostDownNestedScrollConnection
+
     companion object {
         /**
          * The default [Saver] implementation for [ModalBottomSheetState].
@@ -167,7 +172,7 @@
 fun rememberModalBottomSheetState(
     initialValue: ModalBottomSheetValue,
     clock: AnimationClockObservable = AmbientAnimationClock.current,
-    animationSpec: AnimationSpec<Float> = SwipeableConstants.DefaultAnimationSpec,
+    animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec,
     confirmStateChange: (ModalBottomSheetValue) -> Boolean = { true }
 ): ModalBottomSheetState {
     val disposableClock = clock.asDisposableClock()
@@ -219,10 +224,10 @@
     sheetState: ModalBottomSheetState =
         rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
     sheetShape: Shape = MaterialTheme.shapes.large,
-    sheetElevation: Dp = ModalBottomSheetConstants.DefaultElevation,
+    sheetElevation: Dp = ModalBottomSheetDefaults.Elevation,
     sheetBackgroundColor: Color = MaterialTheme.colors.surface,
     sheetContentColor: Color = contentColorFor(sheetBackgroundColor),
-    scrimColor: Color = ModalBottomSheetConstants.DefaultScrimColor,
+    scrimColor: Color = ModalBottomSheetDefaults.scrimColor,
     content: @Composable () -> Unit
 ) = BottomSheetStack(
     modifier = modifier,
@@ -230,7 +235,8 @@
         Surface(
             Modifier
                 .fillMaxWidth()
-                .offset(y = { sheetState.offset.value }),
+                .nestedScroll(sheetState.nestedScrollConnection)
+                .offset { IntOffset(0, sheetState.offset.value.roundToInt()) },
             shape = sheetShape,
             elevation = sheetElevation,
             color = sheetBackgroundColor,
@@ -322,6 +328,13 @@
 /**
  * Contains useful constants for [ModalBottomSheetLayout].
  */
+@Deprecated(
+    "ModalBottomSheetConstants has been replaced with ModalBottomSheetDefaults",
+    ReplaceWith(
+        "ModalBottomSheetDefaults",
+        "androidx.compose.material.ModalBottomSheetDefaults"
+    )
+)
 object ModalBottomSheetConstants {
 
     /**
@@ -336,3 +349,21 @@
         @Composable
         get() = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
 }
+
+/**
+ * Contains useful Defaults for [ModalBottomSheetLayout].
+ */
+object ModalBottomSheetDefaults {
+
+    /**
+     * The default elevation used by [ModalBottomSheetLayout].
+     */
+    val Elevation = 16.dp
+
+    /**
+     * The default scrim color used by [ModalBottomSheetLayout].
+     */
+    val scrimColor: Color
+        @Composable
+        get() = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
+}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
index fa19662..ee1eceb 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
@@ -30,7 +30,6 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.RectangleShape
@@ -274,7 +273,6 @@
     )
 }
 
-@OptIn(ExperimentalFocus::class)
 @Composable
 internal fun OutlinedTextFieldLayout(
     modifier: Modifier = Modifier,
@@ -283,6 +281,7 @@
     decoratedLabel: @Composable (() -> Unit)?,
     leading: @Composable (() -> Unit)?,
     trailing: @Composable (() -> Unit)?,
+    singleLine: Boolean,
     leadingColor: Color,
     trailingColor: Color,
     labelProgress: Float,
@@ -308,6 +307,7 @@
         textField = decoratedTextField,
         leading = leading,
         trailing = trailing,
+        singleLine = singleLine,
         leadingColor = leadingColor,
         trailingColor = trailingColor,
         onLabelMeasured = {
@@ -336,6 +336,7 @@
     label: @Composable (() -> Unit)?,
     leading: @Composable (() -> Unit)?,
     trailing: @Composable (() -> Unit)?,
+    singleLine: Boolean,
     leadingColor: Color,
     trailingColor: Color,
     animationProgress: Float,
@@ -401,7 +402,9 @@
         )
         val labelPlaceable =
             measurables.find { it.layoutId == LabelId }?.measure(labelConstraints)
-        onLabelMeasured(labelPlaceable?.width ?: 0)
+        labelPlaceable?.let {
+            onLabelMeasured(it.width)
+        }
 
         // measure text field
         // on top we offset either by default padding or by label's half height if its too big
@@ -448,6 +451,7 @@
                 labelPlaceable,
                 placeholderPlaceable,
                 animationProgress,
+                singleLine,
                 density
             )
         }
@@ -526,8 +530,11 @@
     labelPlaceable: Placeable?,
     placeholderPlaceable: Placeable?,
     animationProgress: Float,
+    singleLine: Boolean,
     density: Float
 ) {
+    val topBottomPadding = (TextFieldPadding.value * density).roundToInt()
+
     // placed center vertically and to the start edge horizontally
     leadingPlaceable?.placeRelative(
         0,
@@ -540,31 +547,39 @@
         Alignment.CenterVertically.align(trailingPlaceable.height, height)
     )
 
-    // if animation progress is 0, the label will be centered vertically
-    // if animation progress is 1, vertically it will be centered to the container's top edge
-    // horizontally it is placed after the leading icon
-    if (labelPlaceable != null) {
-        val labelPositionY =
-            Alignment.CenterVertically.align(labelPlaceable.height, height) * (
-                1 -
-                    animationProgress
-                ) - (labelPlaceable.height / 2) * animationProgress
-        val labelPositionX = (TextFieldPadding.value * density) +
+    // label position is animated
+    // in single line text field label is centered vertically before animation starts
+    labelPlaceable?.let {
+        val startPositionY = if (singleLine) {
+            Alignment.CenterVertically.align(it.height, height)
+        } else {
+            topBottomPadding
+        }
+        val positionY =
+            startPositionY * (1 - animationProgress) - (it.height / 2) * animationProgress
+        val positionX = (TextFieldPadding.value * density) +
             widthOrZero(leadingPlaceable) * (1 - animationProgress)
-        labelPlaceable.placeRelative(labelPositionX.roundToInt(), labelPositionY.roundToInt())
+        it.placeRelative(positionX.roundToInt(), positionY.roundToInt())
     }
 
-    // placed center vertically and after the leading icon horizontally
-    textFieldPlaceable.placeRelative(
-        widthOrZero(leadingPlaceable),
+    // placed center vertically and after the leading icon horizontally if single line text field
+    // placed to the top with padding for multi line text field
+    val textVerticalPosition = if (singleLine) {
         Alignment.CenterVertically.align(textFieldPlaceable.height, height)
-    )
+    } else {
+        topBottomPadding
+    }
+    textFieldPlaceable.placeRelative(widthOrZero(leadingPlaceable), textVerticalPosition)
 
-    // placed center vertically and after the leading icon horizontally
-    placeholderPlaceable?.placeRelative(
-        widthOrZero(leadingPlaceable),
-        Alignment.CenterVertically.align(placeholderPlaceable.height, height)
-    )
+    // placed similar to the input text above
+    placeholderPlaceable?.let {
+        val placeholderVerticalPosition = if (singleLine) {
+            Alignment.CenterVertically.align(it.height, height)
+        } else {
+            topBottomPadding
+        }
+        it.placeRelative(widthOrZero(leadingPlaceable), placeholderVerticalPosition)
+    }
 }
 
 /**
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
index 09a97e0..096155b 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
@@ -16,22 +16,21 @@
 
 package androidx.compose.material
 
-import androidx.compose.animation.core.AnimationConstants.Infinite
 import androidx.compose.animation.core.CubicBezierEasing
 import androidx.compose.animation.core.FloatPropKey
 import androidx.compose.animation.core.IntPropKey
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.infiniteRepeatable
 import androidx.compose.animation.core.keyframes
-import androidx.compose.animation.core.repeatable
 import androidx.compose.animation.core.transitionDefinition
 import androidx.compose.animation.core.tween
 import androidx.compose.animation.transition
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.progressSemantics
-import androidx.compose.material.ProgressIndicatorConstants.DefaultIndicatorBackgroundOpacity
+import androidx.compose.material.ProgressIndicatorDefaults.IndicatorBackgroundOpacity
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
@@ -53,7 +52,7 @@
  * A determinate linear progress indicator that represents progress by drawing a horizontal line.
  *
  * By default there is no animation between [progress] values. You can use
- * [ProgressIndicatorConstants.DefaultProgressAnimationSpec] as the default recommended
+ * [ProgressIndicatorDefaults.ProgressAnimationSpec] as the default recommended
  * [AnimationSpec] when animating progress, such as in the following example:
  *
  * @sample androidx.compose.material.samples.LinearProgressIndicatorSample
@@ -69,14 +68,14 @@
     @FloatRange(from = 0.0, to = 1.0) progress: Float,
     modifier: Modifier = Modifier,
     color: Color = MaterialTheme.colors.primary,
-    backgroundColor: Color = color.copy(alpha = DefaultIndicatorBackgroundOpacity)
+    backgroundColor: Color = color.copy(alpha = IndicatorBackgroundOpacity)
 ) {
     Canvas(
         modifier
             .progressSemantics(progress)
             .preferredSize(LinearIndicatorWidth, LinearIndicatorHeight)
     ) {
-        val strokeWidth = ProgressIndicatorConstants.DefaultStrokeWidth.toPx()
+        val strokeWidth = ProgressIndicatorDefaults.StrokeWidth.toPx()
         drawLinearIndicatorBackground(backgroundColor, strokeWidth)
         drawLinearIndicator(0f, progress, color, strokeWidth)
     }
@@ -94,7 +93,7 @@
 fun LinearProgressIndicator(
     modifier: Modifier = Modifier,
     color: Color = MaterialTheme.colors.primary,
-    backgroundColor: Color = color.copy(alpha = DefaultIndicatorBackgroundOpacity)
+    backgroundColor: Color = color.copy(alpha = IndicatorBackgroundOpacity)
 ) {
     val state = transition(
         definition = LinearIndeterminateTransition,
@@ -110,7 +109,7 @@
         val firstLineTail = state[FirstLineTailProp]
         val secondLineHead = state[SecondLineHeadProp]
         val secondLineTail = state[SecondLineTailProp]
-        val strokeWidth = ProgressIndicatorConstants.DefaultStrokeWidth.toPx()
+        val strokeWidth = ProgressIndicatorDefaults.StrokeWidth.toPx()
         drawLinearIndicatorBackground(backgroundColor, strokeWidth)
         if (firstLineHead - firstLineTail > 0) {
             drawLinearIndicator(
@@ -160,7 +159,7 @@
  * 0 to 360 degrees.
  *
  * By default there is no animation between [progress] values. You can use
- * [ProgressIndicatorConstants.DefaultProgressAnimationSpec] as the default recommended
+ * [ProgressIndicatorDefaults.ProgressAnimationSpec] as the default recommended
  * [AnimationSpec] when animating progress, such as in the following example:
  *
  * @sample androidx.compose.material.samples.CircularProgressIndicatorSample
@@ -175,7 +174,7 @@
     @FloatRange(from = 0.0, to = 1.0) progress: Float,
     modifier: Modifier = Modifier,
     color: Color = MaterialTheme.colors.primary,
-    strokeWidth: Dp = ProgressIndicatorConstants.DefaultStrokeWidth
+    strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth
 ) {
     val stroke = with(AmbientDensity.current) {
         Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Butt)
@@ -203,7 +202,7 @@
 fun CircularProgressIndicator(
     modifier: Modifier = Modifier,
     color: Color = MaterialTheme.colors.primary,
-    strokeWidth: Dp = ProgressIndicatorConstants.DefaultStrokeWidth
+    strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth
 ) {
     val stroke = with(AmbientDensity.current) {
         Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Square)
@@ -259,6 +258,13 @@
 /**
  * Contains the default values used for [LinearProgressIndicator] and [CircularProgressIndicator].
  */
+@Deprecated(
+    "ProgressIndicatorConstants has been replaced with ProgressIndicatorDefaults",
+    ReplaceWith(
+        "ProgressIndicatorDefaults",
+        "androidx.compose.material.ProgressIndicatorDefaults"
+    )
+)
 object ProgressIndicatorConstants {
     /**
      * Default stroke width for [CircularProgressIndicator], and default height for
@@ -288,6 +294,38 @@
     )
 }
 
+/**
+ * Contains the default values used for [LinearProgressIndicator] and [CircularProgressIndicator].
+ */
+object ProgressIndicatorDefaults {
+    /**
+     * Default stroke width for [CircularProgressIndicator], and default height for
+     * [LinearProgressIndicator].
+     *
+     * This can be customized with the `strokeWidth` parameter on [CircularProgressIndicator],
+     * and by passing a layout modifier setting the height for [LinearProgressIndicator].
+     */
+    val StrokeWidth = 4.dp
+
+    /**
+     * The default opacity applied to the indicator color to create the background color in a
+     * [LinearProgressIndicator].
+     */
+    const val IndicatorBackgroundOpacity = 0.24f
+
+    /**
+     * The default [AnimationSpec] that should be used when animating between progress in a
+     * determinate progress indicator.
+     */
+    val ProgressAnimationSpec = SpringSpec(
+        dampingRatio = Spring.DampingRatioNoBouncy,
+        stiffness = Spring.StiffnessVeryLow,
+        // The default threshold is 0.01, or 1% of the overall progress range, which is quite
+        // large and noticeable.
+        visibilityThreshold = 1 / 1000f
+    )
+}
+
 private fun DrawScope.drawDeterminateCircularIndicator(
     startAngle: Float,
     sweep: Float,
@@ -322,7 +360,7 @@
 // LinearProgressIndicator Material specs
 // TODO: there are currently 3 fixed widths in Android, should this be flexible? Material says
 // the width should be 240dp here.
-private val LinearIndicatorHeight = ProgressIndicatorConstants.DefaultStrokeWidth
+private val LinearIndicatorHeight = ProgressIndicatorDefaults.StrokeWidth
 private val LinearIndicatorWidth = 240.dp
 
 // CircularProgressIndicator Material specs
@@ -373,32 +411,28 @@
     }
 
     transition(fromState = 0, toState = 1) {
-        FirstLineHeadProp using repeatable(
-            iterations = Infinite,
+        FirstLineHeadProp using infiniteRepeatable(
             animation = keyframes {
                 durationMillis = LinearAnimationDuration
                 0f at FirstLineHeadDelay with FirstLineHeadEasing
                 1f at FirstLineHeadDuration + FirstLineHeadDelay
             }
         )
-        FirstLineTailProp using repeatable(
-            iterations = Infinite,
+        FirstLineTailProp using infiniteRepeatable(
             animation = keyframes {
                 durationMillis = LinearAnimationDuration
                 0f at FirstLineTailDelay with FirstLineTailEasing
                 1f at FirstLineTailDuration + FirstLineTailDelay
             }
         )
-        SecondLineHeadProp using repeatable(
-            iterations = Infinite,
+        SecondLineHeadProp using infiniteRepeatable(
             animation = keyframes {
                 durationMillis = LinearAnimationDuration
                 0f at SecondLineHeadDelay with SecondLineHeadEasing
                 1f at SecondLineHeadDuration + SecondLineHeadDelay
             }
         )
-        SecondLineTailProp using repeatable(
-            iterations = Infinite,
+        SecondLineTailProp using infiniteRepeatable(
             animation = keyframes {
                 durationMillis = LinearAnimationDuration
                 0f at SecondLineTailDelay with SecondLineTailEasing
@@ -462,30 +496,26 @@
     }
 
     transition(fromState = 0, toState = 1) {
-        IterationProp using repeatable(
-            iterations = Infinite,
+        IterationProp using infiniteRepeatable(
             animation = tween(
                 durationMillis = RotationDuration * RotationsPerCycle,
                 easing = LinearEasing
             )
         )
-        BaseRotationProp using repeatable(
-            iterations = Infinite,
+        BaseRotationProp using infiniteRepeatable(
             animation = tween(
                 durationMillis = RotationDuration,
                 easing = LinearEasing
             )
         )
-        HeadRotationProp using repeatable(
-            iterations = Infinite,
+        HeadRotationProp using infiniteRepeatable(
             animation = keyframes {
                 durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
                 0f at 0 with CircularEasing
                 JumpRotationAngle at HeadAndTailAnimationDuration
             }
         )
-        TailRotationProp using repeatable(
-            iterations = Infinite,
+        TailRotationProp using infiniteRepeatable(
             animation = keyframes {
                 durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
                 0f at HeadAndTailDelayDuration with CircularEasing
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 6e9d2ed..61ba138 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
@@ -66,7 +66,7 @@
  * [InteractionState] if you want to read the [InteractionState] and customize the appearance /
  * behavior of this RadioButton in different [Interaction]s.
  * @param colors [RadioButtonColors] that will be used to resolve the color used for this
- * RadioButton in different states. See [RadioButtonConstants.defaultColors].
+ * RadioButton in different states. See [RadioButtonDefaults.colors].
  */
 @OptIn(ExperimentalMaterialApi::class)
 @Composable
@@ -76,7 +76,7 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     interactionState: InteractionState = remember { InteractionState() },
-    colors: RadioButtonColors = RadioButtonConstants.defaultColors()
+    colors: RadioButtonColors = RadioButtonDefaults.colors()
 ) {
     val dotRadius = animate(
         target = if (selected) RadioButtonDotSize / 2 else 0.dp,
@@ -106,7 +106,7 @@
 /**
  * Represents the color used by a [RadioButton] in different states.
  *
- * See [RadioButtonConstants.defaultColors] for the default implementation that follows Material
+ * See [RadioButtonDefaults.colors] for the default implementation that follows Material
  * specifications.
  */
 @ExperimentalMaterialApi
@@ -125,6 +125,13 @@
 /**
  * Constants used in [RadioButton].
  */
+@Deprecated(
+    "RadioButtonConstants has been replaced with RadioButtonDefaults",
+    ReplaceWith(
+        "RadioButtonDefaults",
+        "androidx.compose.material.RadioButtonDefaults"
+    )
+)
 object RadioButtonConstants {
     /**
      * Creates a [RadioButtonColors] that will animate between the provided colors according to
@@ -137,6 +144,13 @@
      */
     @OptIn(ExperimentalMaterialApi::class)
     @Composable
+    @Deprecated(
+        "RadioButtonConstants has been replaced with RadioButtonDefaults",
+        ReplaceWith(
+            "RadioButtonDefaults.colors(selectedColor, unselectedColor, disabledColor)",
+            "androidx.compose.material.RadioButtonDefaults"
+        )
+    )
     fun defaultColors(
         selectedColor: Color = MaterialTheme.colors.secondary,
         unselectedColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
@@ -154,6 +168,38 @@
     }
 }
 
+/**
+ * Defaults used in [RadioButton].
+ */
+object RadioButtonDefaults {
+    /**
+     * Creates a [RadioButtonColors] that will animate between the provided colors according to
+     * the Material specification.
+     *
+     * @param selectedColor the color to use for the RadioButton when selected and enabled.
+     * @param unselectedColor the color to use for the RadioButton when unselected and enabled.
+     * @param disabledColor the color to use for the RadioButton when disabled.
+     * @return the resulting [Color] used for the RadioButton
+     */
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    fun colors(
+        selectedColor: Color = MaterialTheme.colors.secondary,
+        unselectedColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
+        disabledColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
+    ): RadioButtonColors {
+        val clock = AmbientAnimationClock.current.asDisposableClock()
+        return remember(
+            selectedColor,
+            unselectedColor,
+            disabledColor,
+            clock
+        ) {
+            DefaultRadioButtonColors(selectedColor, unselectedColor, disabledColor, clock)
+        }
+    }
+}
+
 private fun DrawScope.drawRadio(color: Color, dotRadius: Dp) {
     val strokeWidth = RadioStrokeWidth.toPx()
     drawCircle(color, RadioRadius.toPx() - strokeWidth / 2, style = Stroke(strokeWidth))
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
index 33f7dd15..00f6b24 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
@@ -160,10 +160,10 @@
     drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
     drawerGesturesEnabled: Boolean = true,
     drawerShape: Shape = MaterialTheme.shapes.large,
-    drawerElevation: Dp = DrawerConstants.DefaultElevation,
+    drawerElevation: Dp = DrawerDefaults.Elevation,
     drawerBackgroundColor: Color = MaterialTheme.colors.surface,
     drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
-    drawerScrimColor: Color = DrawerConstants.defaultScrimColor,
+    drawerScrimColor: Color = DrawerDefaults.scrimColor,
     backgroundColor: Color = MaterialTheme.colors.background,
     contentColor: Color = contentColorFor(backgroundColor),
     bodyContent: @Composable (PaddingValues) -> Unit
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
index 31d5c18..c6b155b 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
@@ -40,8 +40,8 @@
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.preferredWidthIn
 import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.SliderConstants.InactiveTrackColorAlpha
-import androidx.compose.material.SliderConstants.TickColorAlpha
+import androidx.compose.material.SliderDefaults.InactiveTrackColorAlpha
+import androidx.compose.material.SliderDefaults.TickColorAlpha
 import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
@@ -59,8 +59,8 @@
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.AmbientLayoutDirection
 import androidx.compose.ui.semantics.AccessibilityRangeInfo
-import androidx.compose.ui.semantics.accessibilityValue
-import androidx.compose.ui.semantics.accessibilityValueRange
+import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.semantics.stateDescriptionRange
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.setProgress
 import androidx.compose.ui.unit.LayoutDirection
@@ -196,6 +196,13 @@
 /**
  * Object to hold constants used by the [Slider]
  */
+@Deprecated(
+    "SliderConstants has been replaced with SliderDefaults",
+    ReplaceWith(
+        "SliderDefaults",
+        "androidx.compose.material.SliderDefaults"
+    )
+)
 object SliderConstants {
     /**
      * Default alpha of the inactive part of the track
@@ -208,6 +215,21 @@
     const val TickColorAlpha = 0.54f
 }
 
+/**
+ * Object to hold defaults used by [Slider]
+ */
+object SliderDefaults {
+    /**
+     * Default alpha of the inactive part of the track
+     */
+    const val InactiveTrackColorAlpha = 0.24f
+
+    /**
+     * Default alpha of the ticks that are drawn on top of the track
+     */
+    const val TickColorAlpha = 0.54f
+}
+
 @Composable
 private fun SliderImpl(
     positionFraction: Float,
@@ -361,8 +383,8 @@
         else -> (fraction * 100).roundToInt().coerceIn(1, 99)
     }
     return semantics(mergeDescendants = true) {
-        accessibilityValue = Strings.TemplatePercent.format(percent)
-        accessibilityValueRange = AccessibilityRangeInfo(coerced, valueRange, steps)
+        stateDescription = Strings.TemplatePercent.format(percent)
+        stateDescriptionRange = AccessibilityRangeInfo(coerced, valueRange, steps)
         setProgress(
             action = { targetValue ->
                 val newValue = targetValue.coerceIn(position.startValue, position.endValue)
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 c539ad8..dcdafed 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
@@ -59,7 +59,7 @@
  *
  * @param modifier modifiers for the the Snackbar layout
  * @param action action / button component to add as an action to the snackbar. Consider using
- * [SnackbarConstants.defaultActionPrimaryColor] as the color for the action, if you do not
+ * [SnackbarDefaults.primaryActionColor] as the color for the action, if you do not
  * have a predefined color you wish to use instead.
  * @param actionOnNewLine whether or not action should be put on the separate line. Recommended
  * for action with long action text
@@ -79,7 +79,7 @@
     action: @Composable (() -> Unit)? = null,
     actionOnNewLine: Boolean = false,
     shape: Shape = MaterialTheme.shapes.small,
-    backgroundColor: Color = SnackbarConstants.defaultBackgroundColor,
+    backgroundColor: Color = SnackbarDefaults.backgroundColor,
     contentColor: Color = MaterialTheme.colors.surface,
     elevation: Dp = 6.dp,
     text: @Composable () -> Unit
@@ -147,16 +147,16 @@
     modifier: Modifier = Modifier,
     actionOnNewLine: Boolean = false,
     shape: Shape = MaterialTheme.shapes.small,
-    backgroundColor: Color = SnackbarConstants.defaultBackgroundColor,
+    backgroundColor: Color = SnackbarDefaults.backgroundColor,
     contentColor: Color = MaterialTheme.colors.surface,
-    actionColor: Color = SnackbarConstants.defaultActionPrimaryColor,
+    actionColor: Color = SnackbarDefaults.primaryActionColor,
     elevation: Dp = 6.dp
 ) {
     val actionLabel = snackbarData.actionLabel
     val actionComposable: (@Composable () -> Unit)? = if (actionLabel != null) {
         {
             TextButton(
-                colors = ButtonConstants.defaultTextButtonColors(contentColor = actionColor),
+                colors = ButtonDefaults.textButtonColors(contentColor = actionColor),
                 onClick = { snackbarData.performAction() },
                 content = { Text(actionLabel) }
             )
@@ -179,6 +179,13 @@
 /**
  * Object to hold constants used by the [Snackbar]
  */
+@Deprecated(
+    "SnackbarConstants has been replaced with SnackbarDefaults",
+    ReplaceWith(
+        "SnackbarDefaults",
+        "androidx.compose.material.SnackbarDefaults"
+    )
+)
 object SnackbarConstants {
 
     /**
@@ -225,6 +232,55 @@
         }
 }
 
+/**
+ * Object to hold defaults used by [Snackbar]
+ */
+object SnackbarDefaults {
+
+    /**
+     * Default alpha of the overlay applied to the [backgroundColor]
+     */
+    private const val SnackbarOverlayAlpha = 0.8f
+
+    /**
+     * Default background color of the [Snackbar]
+     */
+    val backgroundColor: Color
+        @Composable
+        get() =
+            MaterialTheme.colors.onSurface
+                .copy(alpha = SnackbarOverlayAlpha)
+                .compositeOver(MaterialTheme.colors.surface)
+
+    /**
+     * Provides a best-effort 'primary' color to be used as the primary color inside a [Snackbar].
+     * Given that [Snackbar]s have an 'inverted' theme, i.e in a light theme they appear dark, and
+     * in a dark theme they appear light, just using [Colors.primary] will not work, and has
+     * incorrect contrast.
+     *
+     * If your light theme has a corresponding dark theme, you should instead directly use
+     * [Colors.primary] from the dark theme when in a light theme, and use
+     * [Colors.primaryVariant] from the dark theme when in a dark theme.
+     *
+     * When in a light theme, this function applies a color overlay to [Colors.primary] from
+     * [MaterialTheme.colors] to attempt to reduce the contrast, and when in a dark theme this
+     * function uses [Colors.primaryVariant].
+     */
+    val primaryActionColor: Color
+        @Composable
+        get() {
+            val colors = MaterialTheme.colors
+            return if (colors.isLight) {
+                val primary = colors.primary
+                val overlayColor = colors.surface.copy(alpha = 0.6f)
+
+                overlayColor.compositeOver(primary)
+            } else {
+                colors.primaryVariant
+            }
+        }
+}
+
 @Composable
 private fun TextOnlySnackbar(content: @Composable () -> Unit) {
     Layout(
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt
index b0b7b05..2adefd7 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt
@@ -28,8 +28,8 @@
 import androidx.compose.material.DismissValue.Default
 import androidx.compose.material.DismissValue.DismissedToEnd
 import androidx.compose.material.DismissValue.DismissedToStart
-import androidx.compose.material.SwipeableConstants.StandardResistanceFactor
-import androidx.compose.material.SwipeableConstants.StiffResistanceFactor
+import androidx.compose.material.SwipeableDefaults.StandardResistanceFactor
+import androidx.compose.material.SwipeableDefaults.StiffResistanceFactor
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.savedinstancestate.Saver
@@ -39,7 +39,9 @@
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.platform.AmbientLayoutDirection
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.LayoutDirection
+import kotlin.math.roundToInt
 
 /**
  * The directions in which a [SwipeToDismiss] can be dismissed.
@@ -239,7 +241,7 @@
         )
         Row(
             content = dismissContent,
-            modifier = Modifier.offset(x = { state.offset.value })
+            modifier = Modifier.offset { IntOffset(state.offset.value.roundToInt(), 0) }
         )
     }
 }
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 858066e..2bcbfb2 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
@@ -25,10 +25,10 @@
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.gestures.draggable
-import androidx.compose.material.SwipeableConstants.DefaultAnimationSpec
-import androidx.compose.material.SwipeableConstants.DefaultVelocityThreshold
-import androidx.compose.material.SwipeableConstants.StandardResistanceFactor
-import androidx.compose.material.SwipeableConstants.defaultResistanceConfig
+import androidx.compose.material.SwipeableDefaults.AnimationSpec
+import androidx.compose.material.SwipeableDefaults.VelocityThreshold
+import androidx.compose.material.SwipeableDefaults.StandardResistanceFactor
+import androidx.compose.material.SwipeableDefaults.resistanceConfig
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
@@ -42,12 +42,16 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollSource
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.annotation.FloatRange
 import androidx.compose.ui.util.lerp
@@ -73,7 +77,7 @@
 open class SwipeableState<T>(
     initialValue: T,
     clock: AnimationClockObservable,
-    internal val animationSpec: AnimationSpec<Float> = DefaultAnimationSpec,
+    internal val animationSpec: AnimationSpec<Float> = AnimationSpec,
     internal val confirmStateChange: (newValue: T) -> Boolean = { true }
 ) {
     /**
@@ -109,8 +113,8 @@
      */
     val overflow: State<Float> get() = overflowState
 
-    private var minBound = Float.NEGATIVE_INFINITY
-    private var maxBound = Float.POSITIVE_INFINITY
+    internal var minBound = Float.NEGATIVE_INFINITY
+    internal var maxBound = Float.POSITIVE_INFINITY
 
     private val anchorsState = mutableStateOf(emptyMap<Float, T>())
 
@@ -167,6 +171,8 @@
 
     internal var thresholds: (Float, Float) -> Float by mutableStateOf({ _, _ -> 0f })
 
+    internal var velocityThreshold by mutableStateOf(0f)
+
     internal var resistance: ResistanceConfig? by mutableStateOf(null)
 
     internal val holder: AnimatedFloat = NotificationBasedAnimatedFloat(0f, animationClockProxy) {
@@ -290,6 +296,63 @@
         }
     }
 
+    /**
+     * Perform fling with settling to one of the anchors which is determined by the given
+     * [velocity]. Fling with settling [swipeable] will always consume all the velocity provided
+     * since it will settle at the anchor.
+     *
+     * In general cases, [swipeable] flings by itself when being swiped. This method is to be
+     * used for nested scroll logic that wraps the [swipeable]. In nested scroll developer may
+     * want to trigger settling fling when the child scroll container reaches the bound.
+     *
+     * @param velocity velocity to fling and settle with
+     * @param onEnd callback to be invoked when fling is completed
+     */
+    fun performFling(velocity: Float, onEnd: (() -> Unit)) {
+        val lastAnchor = anchors.getOffset(value)!!
+        val targetValue = computeTarget(
+            offset = offset.value,
+            lastValue = lastAnchor,
+            anchors = anchors.keys,
+            thresholds = thresholds,
+            velocity = velocity,
+            velocityThreshold = velocityThreshold
+        )
+        val targetState = anchors[targetValue]
+        if (targetState != null && confirmStateChange(targetState)) {
+            animateTo(targetState, onEnd = { _, _ -> onEnd() })
+        } else {
+            // If the user vetoed the state change, rollback to the previous state.
+            holder.animateTo(lastAnchor, animationSpec, onEnd = { _, _ -> onEnd() })
+        }
+    }
+
+    /**
+     * Force [swipeable] to consume drag delta provided from outside of the regular [swipeable]
+     * gesture flow.
+     *
+     * Note: This method performs generic drag and it won't settle to any particular anchor, *
+     * leaving swipeable in between anchors. When done dragging, [performFling] must be
+     * called as well to ensure swipeable will settle at the anchor.
+     *
+     * In general cases, [swipeable] drags by itself when being swiped. This method is to be
+     * used for nested scroll logic that wraps the [swipeable]. In nested scroll developer may
+     * want to force drag when the child scroll container reaches the bound.
+     *
+     * @param delta delta in pixels to drag by
+     *
+     * @return the amount of [delta] consumed
+     */
+    fun performDrag(delta: Float): Float {
+        val potentiallyConsumed = holder.value + delta
+        val clamped = potentiallyConsumed.coerceIn(minBound, maxBound)
+        val deltaToConsume = clamped - holder.value
+        if (abs(deltaToConsume) > 0) {
+            holder.snapTo(holder.value + deltaToConsume)
+        }
+        return deltaToConsume
+    }
+
     companion object {
         /**
          * The default [Saver] implementation for [SwipeableState].
@@ -333,7 +396,7 @@
 @ExperimentalMaterialApi
 fun <T : Any> rememberSwipeableState(
     initialValue: T,
-    animationSpec: AnimationSpec<Float> = DefaultAnimationSpec,
+    animationSpec: AnimationSpec<Float> = AnimationSpec,
     confirmStateChange: (newValue: T) -> Boolean = { true }
 ): SwipeableState<T> {
     val clock = AmbientAnimationClock.current.asDisposableClock()
@@ -367,7 +430,7 @@
 internal fun <T : Any> rememberSwipeableStateFor(
     value: T,
     onValueChange: (T) -> Unit,
-    animationSpec: AnimationSpec<Float> = DefaultAnimationSpec
+    animationSpec: AnimationSpec<Float> = AnimationSpec
 ): SwipeableState<T> {
     val swipeableState = rememberSwipeableState(
         initialValue = value,
@@ -433,8 +496,8 @@
     reverseDirection: Boolean = false,
     interactionState: InteractionState? = null,
     thresholds: (from: T, to: T) -> ThresholdConfig = { _, _ -> FixedThreshold(56.dp) },
-    resistance: ResistanceConfig? = defaultResistanceConfig(anchors.keys),
-    velocityThreshold: Dp = DefaultVelocityThreshold
+    resistance: ResistanceConfig? = resistanceConfig(anchors.keys),
+    velocityThreshold: Dp = VelocityThreshold
 ) = composed(
     inspectorInfo = debugInspectorInfo {
         name = "swipeable"
@@ -463,6 +526,9 @@
             val to = anchors.getValue(b)
             with(thresholds(from, to)) { density.computeThreshold(a, b) }
         }
+        with(density) {
+            state.velocityThreshold = velocityThreshold.toPx()
+        }
     }
     onCommit {
         state.resistance = resistance
@@ -475,22 +541,7 @@
         interactionState = interactionState,
         startDragImmediately = state.isAnimationRunning,
         onDragStopped = { velocity ->
-            val lastAnchor = anchors.getOffset(state.value)!!
-            val targetValue = computeTarget(
-                offset = state.offset.value,
-                lastValue = lastAnchor,
-                anchors = anchors.keys,
-                thresholds = state.thresholds,
-                velocity = velocity,
-                velocityThreshold = with(density) { velocityThreshold.toPx() }
-            )
-            val targetState = anchors[targetValue]
-            if (targetState != null && state.confirmStateChange(targetState)) {
-                state.animateTo(targetState)
-            } else {
-                // If the user vetoed the state change, rollback to the previous state.
-                state.holder.animateTo(lastAnchor, state.animationSpec)
-            }
+            state.performFling(velocity) {}
         }
     ) { delta ->
         state.holder.snapTo(state.holder.value + delta)
@@ -548,10 +599,10 @@
  *
  * The resistance basis is usually either the size of the component which [swipeable] is applied
  * to, or the distance between the minimum and maximum anchors. For a constructor in which the
- * resistance basis defaults to the latter, consider using [defaultResistanceConfig].
+ * resistance basis defaults to the latter, consider using [resistanceConfig].
  *
  * You may specify different resistance factors for each bound. Consider using one of the default
- * resistance factors in [SwipeableConstants]: `StandardResistanceFactor` to convey that the user
+ * resistance factors in [SwipeableDefaults]: `StandardResistanceFactor` to convey that the user
  * has run out of things to see, and `StiffResistanceFactor` to convey that the user cannot swipe
  * this right now. Also, you can set either factor to 0 to disable resistance at that bound.
  *
@@ -610,9 +661,9 @@
     offset: Float,
     anchors: Set<Float>
 ): List<Float> {
-    // Find the anchors the target lies between.
-    val a = anchors.filter { it <= offset }.maxOrNull()
-    val b = anchors.filter { it >= offset }.minOrNull()
+    // Find the anchors the target lies between with a little bit of rounding error.
+    val a = anchors.filter { it <= offset + 0.001 }.maxOrNull()
+    val b = anchors.filter { it >= offset - 0.001 }.minOrNull()
 
     return when {
         a == null ->
@@ -673,6 +724,13 @@
 /**
  * Contains useful constants for [swipeable] and [SwipeableState].
  */
+@Deprecated(
+    "SwipeableConstants has been replaced with SwipeableDefaults",
+    ReplaceWith(
+        "SwipeableDefaults",
+        "androidx.compose.material.SwipeableDefaults"
+    )
+)
 object SwipeableConstants {
     /**
      * The default animation used by [SwipeableState].
@@ -712,4 +770,101 @@
             ResistanceConfig(basis, factorAtMin, factorAtMax)
         }
     }
-}
\ No newline at end of file
+}
+
+/**
+ * Contains useful defaults for [swipeable] and [SwipeableState].
+ */
+object SwipeableDefaults {
+    /**
+     * The default animation used by [SwipeableState].
+     */
+    val AnimationSpec = SpringSpec<Float>()
+
+    /**
+     * The default velocity threshold (1.8 dp per millisecond) used by [swipeable].
+     */
+    val VelocityThreshold = 125.dp
+
+    /**
+     * A stiff resistance factor which indicates that swiping isn't available right now.
+     */
+    const val StiffResistanceFactor = 20f
+
+    /**
+     * A standard resistance factor which indicates that the user has run out of things to see.
+     */
+    const val StandardResistanceFactor = 10f
+
+    /**
+     * The default resistance config used by [swipeable].
+     *
+     * This returns `null` if there is one anchor. If there are at least two anchors, it returns
+     * a [ResistanceConfig] with the resistance basis equal to the distance between the two bounds.
+     */
+    fun resistanceConfig(
+        anchors: Set<Float>,
+        factorAtMin: Float = StandardResistanceFactor,
+        factorAtMax: Float = StandardResistanceFactor
+    ): ResistanceConfig? {
+        return if (anchors.size <= 1) {
+            null
+        } else {
+            val basis = anchors.maxOrNull()!! - anchors.minOrNull()!!
+            ResistanceConfig(basis, factorAtMin, factorAtMax)
+        }
+    }
+}
+
+// temp default nested scroll connection for swipeables which desire as an opt in
+// revisit in b/174756744 as all types will have their own specific connection probably
+@ExperimentalMaterialApi
+internal val <T> SwipeableState<T>.PreUpPostDownNestedScrollConnection: NestedScrollConnection
+    get() = object : NestedScrollConnection {
+        override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+            val delta = available.toFloat()
+            return if (delta < 0 && source == NestedScrollSource.Drag) {
+                performDrag(delta).toOffset()
+            } else {
+                Offset.Zero
+            }
+        }
+
+        override fun onPostScroll(
+            consumed: Offset,
+            available: Offset,
+            source: NestedScrollSource
+        ): Offset {
+            return if (source == NestedScrollSource.Drag) {
+                performDrag(available.toFloat()).toOffset()
+            } else {
+                Offset.Zero
+            }
+        }
+
+        override fun onPreFling(available: Velocity): Velocity {
+            val toFling = available.pixelsPerSecond.toFloat()
+            return if (toFling < 0 && offset.value > minBound) {
+                performFling(velocity = toFling) {}
+                // since we go to the anchor with tween settling, consume all for the best UX
+                available
+            } else {
+                Velocity.Zero
+            }
+        }
+
+        override fun onPostFling(
+            consumed: Velocity,
+            available: Velocity,
+            onFinished: (Velocity) -> Unit
+        ) {
+            performFling(velocity = available.pixelsPerSecond.toFloat()) {
+                // since we go to the anchor with tween settling, consume all for the best UX
+                onFinished.invoke(available)
+            }
+        }
+
+        private fun Float.toOffset(): Offset = Offset(0f, this)
+
+        private fun Offset.toFloat(): Float = this.y
+    }
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 9881050..fc9dbcd 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
@@ -47,8 +47,10 @@
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.AmbientLayoutDirection
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import kotlin.math.roundToInt
 
 /**
  * A Switch is a two state toggleable component that provides on/off like options
@@ -65,7 +67,7 @@
  * [InteractionState] if you want to read the [InteractionState] and customize the appearance /
  * behavior of this Switch in different [Interaction]s.
  * @param colors [SwitchColors] that will be used to determine the color of the thumb and track
- * in different states. See [SwitchConstants.defaultColors].
+ * in different states. See [SwitchDefaults.colors].
  */
 @Composable
 @OptIn(ExperimentalMaterialApi::class)
@@ -75,7 +77,7 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     interactionState: InteractionState = remember { InteractionState() },
-    colors: SwitchColors = SwitchConstants.defaultColors()
+    colors: SwitchColors = SwitchDefaults.colors()
 ) {
     val minBound = 0f
     val maxBound = with(AmbientDensity.current) { ThumbPathLength.toPx() }
@@ -117,7 +119,7 @@
 /**
  * Represents the colors used by a [Switch] in different states
  *
- * See [SwitchConstants.defaultColors] for the default implementation that follows Material
+ * See [SwitchDefaults.colors] for the default implementation that follows Material
  * specifications.
  */
 @ExperimentalMaterialApi
@@ -168,7 +170,7 @@
         elevation = elevation,
         modifier = Modifier
             .align(Alignment.CenterStart)
-            .offset(x = { thumbValue.value })
+            .offset { IntOffset(thumbValue.value.roundToInt(), 0) }
             .indication(
                 interactionState = interactionState,
                 indication = rememberRipple(bounded = false, radius = ThumbRippleRadius)
@@ -208,6 +210,13 @@
 /**
  * Contains the default values used by [Switch]
  */
+@Deprecated(
+    "SwitchConstants has been replaced with SwitchDefaults",
+    ReplaceWith(
+        "SwitchDefaults",
+        "androidx.compose.material.SwitchDefaults"
+    )
+)
 object SwitchConstants {
     /**
      * Creates a [SwitchColors] that represents the different colors used in a [Switch] in
@@ -228,6 +237,16 @@
      */
     @OptIn(ExperimentalMaterialApi::class)
     @Composable
+    @Deprecated(
+        "SwitchConstants has been replaced with SwitchDefaults",
+        ReplaceWith(
+            "SwitchDefaults.colors(checkedThumbColor, checkedTrackColor, checkedTrackAlpha, " +
+                "uncheckedThumbColor, uncheckedTrackColor, uncheckedTrackAlpha, " +
+                "disabledCheckedThumbColor, disabledCheckedTrackColor, " +
+                "disabledUncheckedThumbColor, disabledUncheckedTrackColor)",
+            "androidx.compose.material.SwitchDefaults"
+        )
+    )
     fun defaultColors(
         checkedThumbColor: Color = MaterialTheme.colors.secondaryVariant,
         checkedTrackColor: Color = checkedThumbColor,
@@ -260,6 +279,60 @@
 }
 
 /**
+ * Contains the default values used by [Switch]
+ */
+object SwitchDefaults {
+    /**
+     * Creates a [SwitchColors] that represents the different colors used in a [Switch] in
+     * different states.
+     *
+     * @param checkedThumbColor the color used for the thumb when enabled and checked
+     * @param checkedTrackColor the color used for the track when enabled and checked
+     * @param checkedTrackAlpha the alpha applied to [checkedTrackColor] and
+     * [disabledCheckedTrackColor]
+     * @param uncheckedThumbColor the color used for the thumb when enabled and unchecked
+     * @param uncheckedTrackColor the color used for the track when enabled and unchecked
+     * @param uncheckedTrackAlpha the alpha applied to [uncheckedTrackColor] and
+     * [disabledUncheckedTrackColor]
+     * @param disabledCheckedThumbColor the color used for the thumb when disabled and checked
+     * @param disabledCheckedTrackColor the color used for the track when disabled and checked
+     * @param disabledUncheckedThumbColor the color used for the thumb when disabled and unchecked
+     * @param disabledUncheckedTrackColor the color used for the track when disabled and unchecked
+     */
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    fun colors(
+        checkedThumbColor: Color = MaterialTheme.colors.secondaryVariant,
+        checkedTrackColor: Color = checkedThumbColor,
+        checkedTrackAlpha: Float = 0.54f,
+        uncheckedThumbColor: Color = MaterialTheme.colors.surface,
+        uncheckedTrackColor: Color = MaterialTheme.colors.onSurface,
+        uncheckedTrackAlpha: Float = 0.38f,
+        disabledCheckedThumbColor: Color = checkedThumbColor
+            .copy(alpha = ContentAlpha.disabled)
+            .compositeOver(MaterialTheme.colors.surface),
+        disabledCheckedTrackColor: Color = checkedTrackColor
+            .copy(alpha = ContentAlpha.disabled)
+            .compositeOver(MaterialTheme.colors.surface),
+        disabledUncheckedThumbColor: Color = uncheckedThumbColor
+            .copy(alpha = ContentAlpha.disabled)
+            .compositeOver(MaterialTheme.colors.surface),
+        disabledUncheckedTrackColor: Color = uncheckedTrackColor
+            .copy(alpha = ContentAlpha.disabled)
+            .compositeOver(MaterialTheme.colors.surface)
+    ): SwitchColors = DefaultSwitchColors(
+        checkedThumbColor = checkedThumbColor,
+        checkedTrackColor = checkedTrackColor.copy(alpha = checkedTrackAlpha),
+        uncheckedThumbColor = uncheckedThumbColor,
+        uncheckedTrackColor = uncheckedTrackColor.copy(alpha = uncheckedTrackAlpha),
+        disabledCheckedThumbColor = disabledCheckedThumbColor,
+        disabledCheckedTrackColor = disabledCheckedTrackColor.copy(alpha = checkedTrackAlpha),
+        disabledUncheckedThumbColor = disabledUncheckedThumbColor,
+        disabledUncheckedTrackColor = disabledUncheckedTrackColor.copy(alpha = uncheckedTrackAlpha)
+    )
+}
+
+/**
  * Default [SwitchColors] implementation.
  */
 @OptIn(ExperimentalMaterialApi::class)
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 175501d..8d4ed29 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
@@ -165,6 +165,13 @@
 /**
  * Contains default values used by tabs from the Material specification.
  */
+@Deprecated(
+    "TabConstants has been replaced with TabDefaults",
+    ReplaceWith(
+        "TabDefaults",
+        "androidx.compose.material.TabDefaults"
+    )
+)
 object TabConstants {
     /**
      * Default [Divider], which will be positioned at the bottom of the [TabRow], underneath the
@@ -254,6 +261,98 @@
     val DefaultScrollableTabRowPadding = 52.dp
 }
 
+/**
+ * Contains default values used by tabs from the Material specification.
+ */
+object TabDefaults {
+    /**
+     * Default [Divider], which will be positioned at the bottom of the [TabRow], underneath the
+     * indicator.
+     *
+     * @param modifier modifier for the divider's layout
+     * @param thickness thickness of the divider
+     * @param color color of the divider
+     */
+    @Composable
+    fun Divider(
+        modifier: Modifier = Modifier,
+        thickness: Dp = DividerThickness,
+        color: Color = AmbientContentColor.current.copy(alpha = DividerOpacity)
+    ) {
+        androidx.compose.material.Divider(modifier = modifier, thickness = thickness, color = color)
+    }
+
+    /**
+     * Default indicator, which will be positioned at the bottom of the [TabRow], on top of the
+     * divider.
+     *
+     * @param modifier modifier for the indicator's layout
+     * @param height height of the indicator
+     * @param color color of the indicator
+     */
+    @Composable
+    fun Indicator(
+        modifier: Modifier = Modifier,
+        height: Dp = IndicatorHeight,
+        color: Color = AmbientContentColor.current
+    ) {
+        Box(
+            modifier
+                .fillMaxWidth()
+                .preferredHeight(height)
+                .background(color = color)
+        )
+    }
+
+    /**
+     * [Modifier] that takes up all the available width inside the [TabRow], and then animates
+     * the offset of the indicator it is applied to, depending on the [currentTabPosition].
+     *
+     * @param currentTabPosition [TabPosition] of the currently selected tab. This is used to
+     * calculate the offset of the indicator this modifier is applied to, as well as its width.
+     */
+    fun Modifier.tabIndicatorOffset(
+        currentTabPosition: TabPosition
+    ): Modifier = composed(
+        inspectorInfo = debugInspectorInfo {
+            name = "tabIndicatorOffset"
+            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
+        val indicatorOffset = animate(
+            target = currentTabPosition.left,
+            animSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing)
+        )
+        fillMaxWidth()
+            .wrapContentSize(Alignment.BottomStart)
+            .offset(x = indicatorOffset)
+            .preferredWidth(currentTabWidth)
+    }
+
+    /**
+     * Default opacity for the color of [Divider]
+     */
+    const val DividerOpacity = 0.12f
+
+    /**
+     * Default thickness for [Divider]
+     */
+    val DividerThickness = 1.dp
+
+    /**
+     * Default height for [Indicator]
+     */
+    val IndicatorHeight = 2.dp
+
+    /**
+     * The default padding from the starting edge before a tab in a [ScrollableTabRow].
+     */
+    val ScrollableTabRowPadding = 52.dp
+}
+
 private val TabTintColor = ColorPropKey()
 
 /**
@@ -396,7 +495,7 @@
 
     // Total offset between the last text baseline and the bottom of the Tab layout
     val totalOffset = with(density) {
-        baselineOffset.toIntPx() + TabConstants.DefaultIndicatorHeight.toIntPx()
+        baselineOffset.toIntPx() + TabDefaults.IndicatorHeight.toIntPx()
     }
 
     val textPlaceableY = tabHeight - lastBaseline - totalOffset
@@ -425,7 +524,7 @@
 
     // Total offset between the last text baseline and the bottom of the Tab layout
     val textOffset = with(density) {
-        baselineOffset.toIntPx() + TabConstants.DefaultIndicatorHeight.toIntPx()
+        baselineOffset.toIntPx() + TabDefaults.IndicatorHeight.toIntPx()
     }
 
     // How much space there is between the top of the icon (essentially the top of this layout)
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
index 4486035..04c42c9 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TabRow.kt
@@ -24,7 +24,7 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.rememberScrollState
-import androidx.compose.material.TabConstants.defaultTabIndicatorOffset
+import androidx.compose.material.TabDefaults.tabIndicatorOffset
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.remember
@@ -69,7 +69,7 @@
  *
  * @sample androidx.compose.material.samples.FancyIndicator
  *
- * We can reuse [TabConstants.defaultTabIndicatorOffset] and just provide this indicator,
+ * We can reuse [TabDefaults.tabIndicatorOffset] and just provide this indicator,
  * as we aren't changing how the size and position of the indicator changes between tabs:
  *
  * @sample androidx.compose.material.samples.FancyIndicatorTabs
@@ -96,9 +96,9 @@
  * Defaults to either the matching `onFoo` color for [backgroundColor], or if [backgroundColor] is
  * not a color from the theme, this will keep the same value set above this TabRow.
  * @param indicator the indicator that represents which tab is currently selected. By default this
- * will be a [TabConstants.DefaultIndicator], using a [TabConstants.defaultTabIndicatorOffset]
+ * will be a [TabDefaults.Indicator], using a [TabDefaults.tabIndicatorOffset]
  * modifier to animate its position. Note that this indicator will be forced to fill up the
- * entire TabRow, so you should use [TabConstants.defaultTabIndicatorOffset] or similar to
+ * entire TabRow, so you should use [TabDefaults.tabIndicatorOffset] or similar to
  * animate the actual drawn indicator inside this space, and provide an offset from the start.
  * @param divider the divider displayed at the bottom of the TabRow. This provides a layer of
  * separation between the TabRow and the content displayed underneath.
@@ -113,12 +113,12 @@
     backgroundColor: Color = MaterialTheme.colors.primarySurface,
     contentColor: Color = contentColorFor(backgroundColor),
     indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = { tabPositions ->
-        TabConstants.DefaultIndicator(
-            Modifier.defaultTabIndicatorOffset(tabPositions[selectedTabIndex])
+        TabDefaults.Indicator(
+            Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
         )
     },
     divider: @Composable () -> Unit = {
-        TabConstants.DefaultDivider()
+        TabDefaults.Divider()
     },
     tabs: @Composable () -> Unit
 ) {
@@ -176,9 +176,9 @@
  * the tabs inside the ScrollableTabRow. This padding helps inform the user that this tab row can
  * be scrolled, unlike a [TabRow].
  * @param indicator the indicator that represents which tab is currently selected. By default this
- * will be a [TabConstants.DefaultIndicator], using a [TabConstants.defaultTabIndicatorOffset]
+ * will be a [TabDefaults.Indicator], using a [TabDefaults.tabIndicatorOffset]
  * modifier to animate its position. Note that this indicator will be forced to fill up the
- * entire ScrollableTabRow, so you should use [TabConstants.defaultTabIndicatorOffset] or similar to
+ * entire ScrollableTabRow, so you should use [TabDefaults.tabIndicatorOffset] or similar to
  * animate the actual drawn indicator inside this space, and provide an offset from the start.
  * @param divider the divider displayed at the bottom of the ScrollableTabRow. This provides a layer
  * of separation between the ScrollableTabRow and the content displayed underneath.
@@ -192,14 +192,14 @@
     modifier: Modifier = Modifier,
     backgroundColor: Color = MaterialTheme.colors.primarySurface,
     contentColor: Color = contentColorFor(backgroundColor),
-    edgePadding: Dp = TabConstants.DefaultScrollableTabRowPadding,
+    edgePadding: Dp = TabDefaults.ScrollableTabRowPadding,
     indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = { tabPositions ->
-        TabConstants.DefaultIndicator(
-            Modifier.defaultTabIndicatorOffset(tabPositions[selectedTabIndex])
+        TabDefaults.Indicator(
+            Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
         )
     },
     divider: @Composable () -> Unit = {
-        TabConstants.DefaultDivider()
+        TabDefaults.Divider()
     },
     tabs: @Composable () -> Unit
 ) {
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 d96a672..d0b7854 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
@@ -24,7 +24,7 @@
 import androidx.compose.runtime.structuralEqualityPolicy
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.useOrElse
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.Paragraph
 import androidx.compose.ui.text.TextLayoutResult
@@ -193,8 +193,8 @@
     onTextLayout: (TextLayoutResult) -> Unit = {},
     style: TextStyle = AmbientTextStyle.current
 ) {
-    val textColor = color.useOrElse {
-        style.color.useOrElse {
+    val textColor = color.takeOrElse {
+        style.color.takeOrElse {
             AmbientContentColor.current.copy(alpha = AmbientContentAlpha.current)
         }
     }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
index d4a2614..836a40d 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
@@ -47,8 +47,6 @@
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.offset
 import kotlin.math.max
@@ -322,6 +320,7 @@
     decoratedLabel: @Composable (() -> Unit)?,
     leading: @Composable (() -> Unit)?,
     trailing: @Composable (() -> Unit)?,
+    singleLine: Boolean,
     leadingColor: Color,
     trailingColor: Color,
     labelProgress: Float,
@@ -346,6 +345,7 @@
         label = decoratedLabel,
         leading = leading,
         trailing = trailing,
+        singleLine = singleLine,
         leadingColor = leadingColor,
         trailingColor = trailingColor,
         animationProgress = labelProgress
@@ -364,6 +364,7 @@
     placeholder: @Composable ((Modifier) -> Unit)?,
     leading: @Composable (() -> Unit)?,
     trailing: @Composable (() -> Unit)?,
+    singleLine: Boolean,
     leadingColor: Color,
     trailingColor: Color,
     animationProgress: Float
@@ -404,6 +405,7 @@
         },
         modifier = modifier
     ) { measurables, incomingConstraints ->
+        val topBottomPadding = TextFieldPadding.toIntPx()
         val baseLineOffset = FirstBaselineOffset.toIntPx()
         val bottomPadding = LastBaselineOffset.toIntPx()
         val topPadding = TextFieldTopPadding.toIntPx()
@@ -438,10 +440,16 @@
         val effectiveLabelBaseline = max(lastBaseline, baseLineOffset)
 
         // measure input field
+        // input field is laid out differently depending on whether the label is present or not
+        val verticalConstraintOffset = if (labelPlaceable != null) {
+            -bottomPadding - topPadding - effectiveLabelBaseline
+        } else {
+            -topBottomPadding * 2
+        }
         val textFieldConstraints = incomingConstraints
             .copy(minHeight = 0)
             .offset(
-                vertical = -bottomPadding - topPadding - effectiveLabelBaseline,
+                vertical = verticalConstraintOffset,
                 horizontal = -occupiedSpaceHorizontally
             )
         val textFieldPlaceable = measurables
@@ -464,6 +472,7 @@
         )
         val height = calculateHeight(
             textFieldPlaceable,
+            labelPlaceable,
             effectiveLabelBaseline,
             leadingPlaceable,
             trailingPlaceable,
@@ -474,30 +483,32 @@
 
         layout(width, height) {
             if (widthOrZero(labelPlaceable) != 0) {
-                val labelEndPosition =
-                    (baseLineOffset - lastBaseline).coerceAtLeast(0)
-                place(
+                // label's final position is always relative to the baseline
+                val labelEndPosition = (baseLineOffset - lastBaseline).coerceAtLeast(0)
+                placeWithLabel(
                     width,
                     height,
-                    layoutDirection,
                     textFieldPlaceable,
                     labelPlaceable,
                     placeholderPlaceable,
                     leadingPlaceable,
                     trailingPlaceable,
+                    singleLine,
                     labelEndPosition,
                     effectiveLabelBaseline + topPadding,
-                    animationProgress
+                    animationProgress,
+                    density
                 )
             } else {
-                // text field should be centered vertically if there is no label
                 placeWithoutLabel(
                     width,
                     height,
                     textFieldPlaceable,
                     placeholderPlaceable,
                     leadingPlaceable,
-                    trailingPlaceable
+                    trailingPlaceable,
+                    singleLine,
+                    density
                 )
             }
         }
@@ -526,6 +537,7 @@
 
 private fun calculateHeight(
     textFieldPlaceable: Placeable,
+    labelPlaceable: Placeable?,
     labelBaseline: Int,
     leadingPlaceable: Placeable?,
     trailingPlaceable: Placeable?,
@@ -535,8 +547,14 @@
 ): Int {
     val bottomPadding = LastBaselineOffset.value * density
     val topPadding = TextFieldTopPadding.value * density
+    val topBottomPadding = TextFieldPadding.value * density
+
     val inputFieldHeight = max(textFieldPlaceable.height, heightOrZero(placeholderPlaceable))
-    val middleSectionHeight = labelBaseline + topPadding + inputFieldHeight + bottomPadding
+    val middleSectionHeight = if (labelPlaceable != null) {
+        labelBaseline + topPadding + inputFieldHeight + bottomPadding
+    } else {
+        topBottomPadding * 2 + inputFieldHeight
+    }
     return maxOf(
         middleSectionHeight.roundToInt(),
         max(heightOrZero(leadingPlaceable), heightOrZero(trailingPlaceable)),
@@ -546,21 +564,24 @@
 
 /**
  * Places the provided text field, placeholder and label with respect to the baseline offsets in
- * [TextField]
+ * [TextField] when there is a label. When there is no label, [placeWithoutLabel] is used.
  */
-private fun Placeable.PlacementScope.place(
+private fun Placeable.PlacementScope.placeWithLabel(
     width: Int,
     height: Int,
-    layoutDirection: LayoutDirection,
     textfieldPlaceable: Placeable,
     labelPlaceable: Placeable?,
     placeholderPlaceable: Placeable?,
     leadingPlaceable: Placeable?,
     trailingPlaceable: Placeable?,
+    singleLine: Boolean,
     labelEndPosition: Int,
     textPosition: Int,
-    animationProgress: Float
+    animationProgress: Float,
+    density: Float
 ) {
+    val topBottomPadding = (TextFieldPadding.value * density).roundToInt()
+
     leadingPlaceable?.placeRelative(
         0,
         Alignment.CenterVertically.align(leadingPlaceable.height, height)
@@ -569,23 +590,26 @@
         width - trailingPlaceable.width,
         Alignment.CenterVertically.align(trailingPlaceable.height, height)
     )
-    if (labelPlaceable != null) {
-        val labelCenterPosition = Alignment.CenterStart.align(
-            IntSize(labelPlaceable.width, labelPlaceable.height),
-            IntSize(width, height),
-            layoutDirection
-        )
-        val labelDistance = labelCenterPosition.y - labelEndPosition
-        val labelPositionY =
-            labelCenterPosition.y - (labelDistance * animationProgress).roundToInt()
-        labelPlaceable.placeRelative(widthOrZero(leadingPlaceable), labelPositionY)
+    labelPlaceable?.let {
+        // if it's a single line, the label's start position is in the center of the
+        // container. When it's a multiline text field, the label's start position is at the
+        // top with padding
+        val startPosition = if (singleLine) {
+            Alignment.CenterVertically.align(it.height, height)
+        } else {
+            topBottomPadding
+        }
+        val distance = startPosition - labelEndPosition
+        val positionY = startPosition - (distance * animationProgress).roundToInt()
+        it.placeRelative(widthOrZero(leadingPlaceable), positionY)
     }
     textfieldPlaceable.placeRelative(widthOrZero(leadingPlaceable), textPosition)
     placeholderPlaceable?.placeRelative(widthOrZero(leadingPlaceable), textPosition)
 }
 
 /**
- * Places the provided text field and placeholder center vertically in [TextField]
+ * Places the provided text field and placeholder in [TextField] when there is no label. When
+ * there is a label, [placeWithLabel] is used
  */
 private fun Placeable.PlacementScope.placeWithoutLabel(
     width: Int,
@@ -593,8 +617,12 @@
     textPlaceable: Placeable,
     placeholderPlaceable: Placeable?,
     leadingPlaceable: Placeable?,
-    trailingPlaceable: Placeable?
+    trailingPlaceable: Placeable?,
+    singleLine: Boolean,
+    density: Float
 ) {
+    val topBottomPadding = (TextFieldPadding.value * density).roundToInt()
+
     leadingPlaceable?.placeRelative(
         0,
         Alignment.CenterVertically.align(leadingPlaceable.height, height)
@@ -603,14 +631,31 @@
         width - trailingPlaceable.width,
         Alignment.CenterVertically.align(trailingPlaceable.height, height)
     )
+
+    // Single line text field without label places its input center vertically. Multiline text
+    // field without label places its input at the top with padding
+    val textVerticalPosition = if (singleLine) {
+        Alignment.CenterVertically.align(textPlaceable.height, height)
+    } else {
+        topBottomPadding
+    }
     textPlaceable.placeRelative(
         widthOrZero(leadingPlaceable),
-        Alignment.CenterVertically.align(textPlaceable.height, height)
+        textVerticalPosition
     )
-    placeholderPlaceable?.placeRelative(
-        widthOrZero(leadingPlaceable),
-        Alignment.CenterVertically.align(placeholderPlaceable.height, height)
-    )
+
+    // placeholder is placed similar to the text input above
+    placeholderPlaceable?.let {
+        val placeholderVerticalPosition = if (singleLine) {
+            Alignment.CenterVertically.align(placeholderPlaceable.height, height)
+        } else {
+            topBottomPadding
+        }
+        it.placeRelative(
+            widthOrZero(leadingPlaceable),
+            placeholderVerticalPosition
+        )
+    }
 }
 
 /**
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 1bacb70..f378300 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
@@ -39,12 +39,11 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.alpha
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.useOrElse
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.layout.LayoutModifier
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
@@ -73,10 +72,7 @@
  * Implementation of the [TextField] and [OutlinedTextField]
  */
 @Composable
-@OptIn(
-    ExperimentalFocus::class,
-    ExperimentalFoundationApi::class
-)
+@OptIn(ExperimentalFoundationApi::class)
 internal fun TextFieldImpl(
     type: TextFieldType,
     value: TextFieldValue,
@@ -102,7 +98,7 @@
     shape: Shape
 ) {
     // If color is not provided via the text style, use content color as a default
-    val textColor = textStyle.color.useOrElse {
+    val textColor = textStyle.color.takeOrElse {
         AmbientContentColor.current.copy(alpha = AmbientContentAlpha.current)
     }
     val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
@@ -116,7 +112,7 @@
         else -> InputPhase.UnfocusedNotEmpty
     }
 
-    val decoratedTextField = @Composable { tagModifier: Modifier ->
+    val decoratedTextField: @Composable (Modifier) -> Unit = @Composable { tagModifier ->
         Decoration(
             contentColor = inactiveColor,
             typography = MaterialTheme.typography.subtitle1,
@@ -144,16 +140,16 @@
         }
     }
 
-    val focusRequester = FocusRequester()
+    val focusReference = FocusReference()
     val textFieldModifier = modifier
-        .focusRequester(focusRequester)
+        .focusReference(focusReference)
         .let {
             it.clickable(interactionState = interactionState, indication = null) {
-                focusRequester.requestFocus()
+                focusReference.requestFocus()
                 // TODO(b/163109449): Showing and hiding keyboard should be handled by BaseTextField.
                 //  The requestFocus() call here should be enough to trigger the software keyboard.
                 //  Investiate why this is needed here. If it is really needed, instead of doing
-                //  this in the onClick callback, we should move this logic to the focusObserver
+                //  this in the onClick callback, we should move this logic to onFocusChanged
                 //  so that it can show or hide the keyboard based on the focus state.
                 keyboardController.value?.showSoftwareKeyboard()
             }
@@ -227,6 +223,7 @@
                     decoratedLabel = decoratedLabel,
                     leading = leading,
                     trailing = trailing,
+                    singleLine = singleLine,
                     leadingColor = leadingColor,
                     trailingColor = trailingColor,
                     labelProgress = labelProgress,
@@ -250,6 +247,7 @@
                     decoratedLabel = decoratedLabel,
                     leading = leading,
                     trailing = trailing,
+                    singleLine = singleLine,
                     leadingColor = leadingColor,
                     trailingColor = trailingColor,
                     labelProgress = labelProgress,
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetector.kt
index dba48a2..704b120 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetector.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetector.kt
@@ -143,7 +143,7 @@
             "Primary composable lambda parameter not named `content`",
             "Composable functions with only one composable lambda parameter should use the name " +
                 "`content` for the parameter.",
-            Category.CORRECTNESS, 3, Severity.WARNING,
+            Category.CORRECTNESS, 3, Severity.IGNORE,
             Implementation(
                 ComposableLambdaParameterDetector::class.java,
                 EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
@@ -156,7 +156,7 @@
             "Composable functions with only one composable lambda parameter should place the " +
                 "parameter at the end of the parameter list, so it can be used as a trailing " +
                 "lambda.",
-            Category.CORRECTNESS, 3, Severity.WARNING,
+            Category.CORRECTNESS, 3, Severity.IGNORE,
             Implementation(
                 ComposableLambdaParameterDetector::class.java,
                 EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RememberDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RememberDetector.kt
new file mode 100644
index 0000000..d05d0b1
--- /dev/null
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RememberDetector.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.compose.runtime.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiJavaFile
+import com.intellij.psi.PsiType
+import org.jetbrains.uast.UCallExpression
+import java.util.EnumSet
+
+/**
+ * [Detector] that checks `remember` calls to make sure they are not returning [Unit].
+ */
+class RememberDetector : Detector(), SourceCodeScanner {
+    override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)
+
+    override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
+        override fun visitCallExpression(node: UCallExpression) {
+            val call = node.resolve() ?: return
+            if (call.name == RememberShortName &&
+                (call.containingFile as? PsiJavaFile)?.packageName == RuntimePackageName
+            ) {
+                if (node.getExpressionType() == PsiType.VOID) {
+                    context.report(
+                        RememberReturnType,
+                        node,
+                        context.getNameLocation(node),
+                        "`remember` calls must not return `Unit`"
+                    )
+                }
+            }
+        }
+    }
+
+    companion object {
+        val RememberReturnType = Issue.create(
+            "RememberReturnType",
+            "`remember` calls must not return `Unit`",
+            "A call to `remember` that returns `Unit` is always an error. This typically happens " +
+                "when using `remember` to mutate variables on an object. `remember` is executed " +
+                "during the composition, which means that if the composition fails or is " +
+                "happening on a separate thread, the mutated variables may not reflect the true " +
+                "state of the composition. Instead, use `SideEffect` to make deferred changes " +
+                "once the composition succeeds, or mutate `MutableState` backed variables " +
+                "directly, as these will handle composition failure for you.",
+            Category.CORRECTNESS, 3, Severity.ERROR,
+            Implementation(
+                RememberDetector::class.java,
+                EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+            )
+        )
+    }
+}
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt
index 7ab6018..6d5dcad 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt
@@ -30,6 +30,7 @@
         AmbientNamingDetector.AmbientNaming,
         ComposableLambdaParameterDetector.ComposableLambdaParameterNaming,
         ComposableLambdaParameterDetector.ComposableLambdaParameterPosition,
-        ComposableNamingDetector.ComposableNaming
+        ComposableNamingDetector.ComposableNaming,
+        RememberDetector.RememberReturnType
     )
 }
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/Utils.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/Utils.kt
index 33d78b0..6d8f4d6 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/Utils.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/Utils.kt
@@ -25,5 +25,9 @@
 @Suppress("DEPRECATION")
 val UMethod.isComposable get() = annotations.any { it.qualifiedName == ComposableFqn }
 
-const val ComposableFqn = "androidx.compose.runtime.Composable"
-val ComposableShortName = ComposableFqn.split(".").last()
\ No newline at end of file
+const val RuntimePackageName = "androidx.compose.runtime"
+
+const val ComposableFqn = "$RuntimePackageName.Composable"
+val ComposableShortName = ComposableFqn.split(".").last()
+
+const val RememberShortName = "remember"
diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/RememberDetectorTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/RememberDetectorTest.kt
new file mode 100644
index 0000000..06f8db1
--- /dev/null
+++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/RememberDetectorTest.kt
@@ -0,0 +1,339 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.compose.runtime.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/* ktlint-disable max-line-length */
+@RunWith(JUnit4::class)
+
+/**
+ * Test for [RememberDetector].
+ */
+class RememberDetectorTest : LintDetectorTest() {
+    override fun getDetector(): Detector = RememberDetector()
+
+    override fun getIssues(): MutableList<Issue> =
+        mutableListOf(RememberDetector.RememberReturnType)
+
+    @Test
+    fun returnsUnit() {
+        lint().files(
+            kotlin(
+                """
+                package androidx.compose.runtime.foo
+
+                import androidx.compose.runtime.Composable
+                import androidx.compose.runtime.remember
+
+                class FooState {
+                    fun update(new: Int) {}
+                }
+
+                @Composable
+                fun Test() {
+                    val state = remember { FooState() }
+                    remember {
+                        state.update(5)
+                    }
+                    val unit = remember {
+                        state.update(5)
+                    }
+                }
+
+                @Composable
+                fun Test(number: Int) {
+                    val state = remember { FooState() }
+                    remember(number) {
+                        state.update(number)
+                    }
+                    val unit = remember(number) {
+                        state.update(number)
+                    }
+                }
+
+                @Composable
+                fun Test(number1: Int, number2: Int) {
+                    val state = remember { FooState() }
+                    remember(number1, number2) {
+                        state.update(number)
+                    }
+                    val unit = remember(number1, number2) {
+                        state.update(number)
+                    }
+                }
+
+                @Composable
+                fun Test(number1: Int, number2: Int, number3: Int) {
+                    val state = remember { FooState() }
+                    remember(number1, number2, number3) {
+                        state.update(number)
+                    }
+                    val unit = remember(number1, number2, number3) {
+                        state.update(number)
+                    }
+                }
+
+                @Composable
+                fun Test(number1: Int, number2: Int, number3: Int, flag: Boolean) {
+                    val state = remember { FooState() }
+                    remember(number1, number2, number3, flag) {
+                        state.update(number)
+                    }
+                    val unit = remember(number1, number2, number3, flag) {
+                        state.update(number)
+                    }
+                }
+            """
+            ),
+            composableStub,
+            rememberStub
+        )
+            .run()
+            .expect(
+                """
+src/androidx/compose/runtime/foo/FooState.kt:14: Error: remember calls must not return Unit [RememberReturnType]
+                    remember {
+                    ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:17: Error: remember calls must not return Unit [RememberReturnType]
+                    val unit = remember {
+                               ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:25: Error: remember calls must not return Unit [RememberReturnType]
+                    remember(number) {
+                    ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:28: Error: remember calls must not return Unit [RememberReturnType]
+                    val unit = remember(number) {
+                               ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:36: Error: remember calls must not return Unit [RememberReturnType]
+                    remember(number1, number2) {
+                    ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:39: Error: remember calls must not return Unit [RememberReturnType]
+                    val unit = remember(number1, number2) {
+                               ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:47: Error: remember calls must not return Unit [RememberReturnType]
+                    remember(number1, number2, number3) {
+                    ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:50: Error: remember calls must not return Unit [RememberReturnType]
+                    val unit = remember(number1, number2, number3) {
+                               ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:58: Error: remember calls must not return Unit [RememberReturnType]
+                    remember(number1, number2, number3, flag) {
+                    ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:61: Error: remember calls must not return Unit [RememberReturnType]
+                    val unit = remember(number1, number2, number3, flag) {
+                               ~~~~~~~~
+10 errors, 0 warnings
+            """
+            )
+    }
+
+    @Test
+    fun returnsValue_explicitUnitType() {
+        lint().files(
+            kotlin(
+                """
+                package androidx.compose.runtime.foo
+
+                import androidx.compose.runtime.Composable
+                import androidx.compose.runtime.remember
+
+                class FooState {
+                    fun update(new: Int): Boolean = true
+                }
+
+                @Composable
+                fun Test() {
+                    val state = remember { FooState() }
+                    remember<Unit> {
+                        state.update(5)
+                    }
+                    val result = remember<Unit> {
+                        state.update(5)
+                    }
+                }
+
+                @Composable
+                fun Test(number: Int) {
+                    val state = remember { FooState() }
+                    remember<Unit>(number) {
+                        state.update(number)
+                    }
+                    val result = remember<Unit>(number) {
+                        state.update(number)
+                    }
+                }
+
+                @Composable
+                fun Test(number1: Int, number2: Int) {
+                    val state = remember { FooState() }
+                    remember<Unit>(number1, number2) {
+                        state.update(number)
+                    }
+                    val result = remember<Unit>(number1, number2) {
+                        state.update(number)
+                    }
+                }
+
+                @Composable
+                fun Test(number1: Int, number2: Int, number3: Int) {
+                    val state = remember { FooState() }
+                    remember<Unit>(number1, number2, number3) {
+                        state.update(number)
+                    }
+                    val result = remember<Unit>(number1, number2, number3) {
+                        state.update(number)
+                    }
+                }
+
+                @Composable
+                fun Test(number1: Int, number2: Int, number3: Int, flag: Boolean) {
+                    val state = remember { FooState() }
+                    remember<Unit>(number1, number2, number3, flag) {
+                        state.update(number)
+                    }
+                    val result = remember<Unit>(number1, number2, number3, flag) {
+                        state.update(number)
+                    }
+                }
+            """
+            ),
+            composableStub,
+            rememberStub
+        )
+            .run()
+            .expect(
+                """
+src/androidx/compose/runtime/foo/FooState.kt:14: Error: remember calls must not return Unit [RememberReturnType]
+                    remember<Unit> {
+                    ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:17: Error: remember calls must not return Unit [RememberReturnType]
+                    val result = remember<Unit> {
+                                 ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:25: Error: remember calls must not return Unit [RememberReturnType]
+                    remember<Unit>(number) {
+                    ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:28: Error: remember calls must not return Unit [RememberReturnType]
+                    val result = remember<Unit>(number) {
+                                 ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:36: Error: remember calls must not return Unit [RememberReturnType]
+                    remember<Unit>(number1, number2) {
+                    ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:39: Error: remember calls must not return Unit [RememberReturnType]
+                    val result = remember<Unit>(number1, number2) {
+                                 ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:47: Error: remember calls must not return Unit [RememberReturnType]
+                    remember<Unit>(number1, number2, number3) {
+                    ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:50: Error: remember calls must not return Unit [RememberReturnType]
+                    val result = remember<Unit>(number1, number2, number3) {
+                                 ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:58: Error: remember calls must not return Unit [RememberReturnType]
+                    remember<Unit>(number1, number2, number3, flag) {
+                    ~~~~~~~~
+src/androidx/compose/runtime/foo/FooState.kt:61: Error: remember calls must not return Unit [RememberReturnType]
+                    val result = remember<Unit>(number1, number2, number3, flag) {
+                                 ~~~~~~~~
+10 errors, 0 warnings
+            """
+            )
+    }
+
+    @Test
+    fun noErrors() {
+        lint().files(
+            kotlin(
+                """
+                package androidx.compose.runtime.foo
+
+                import androidx.compose.runtime.Composable
+                import androidx.compose.runtime.remember
+
+                class FooState {
+                    fun update(new: Int): Boolean = true
+                }
+
+                @Composable
+                fun Test() {
+                    val state = remember { FooState() }
+                    remember {
+                        state.update(5)
+                    }
+                    val result = remember {
+                        state.update(5)
+                    }
+                }
+
+                @Composable
+                fun Test(number: Int) {
+                    val state = remember { FooState() }
+                    remember(number) {
+                        state.update(number)
+                    }
+                    val result = remember(number) {
+                        state.update(number)
+                    }
+                }
+
+                @Composable
+                fun Test(number1: Int, number2: Int) {
+                    val state = remember { FooState() }
+                    remember(number1, number2) {
+                        state.update(number)
+                    }
+                    val result = remember(number1, number2) {
+                        state.update(number)
+                    }
+                }
+
+                @Composable
+                fun Test(number1: Int, number2: Int, number3: Int) {
+                    val state = remember { FooState() }
+                    remember(number1, number2, number3) {
+                        state.update(number)
+                    }
+                    val result = remember(number1, number2, number3) {
+                        state.update(number)
+                    }
+                }
+
+                @Composable
+                fun Test(number1: Int, number2: Int, number3: Int, flag: Boolean) {
+                    val state = remember { FooState() }
+                    remember(number1, number2, number3, flag) {
+                        state.update(number)
+                    }
+                    val result = remember(number1, number2, number3, flag) {
+                        state.update(number)
+                    }
+                }
+            """
+            ),
+            composableStub,
+            rememberStub
+        )
+            .run()
+            .expectClean()
+    }
+}
+/* ktlint-enable max-line-length */
diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/Stubs.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/Stubs.kt
index 92428e8..9c086d2 100644
--- a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/Stubs.kt
+++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/Stubs.kt
@@ -34,4 +34,42 @@
         )
         annotation class Composable
     """
-)
\ No newline at end of file
+)
+
+val rememberStub: LintDetectorTest.TestFile = LintDetectorTest.kotlin(
+"""
+        package androidx.compose.runtime
+
+        import androidx.compose.runtime.Composable
+
+        @Composable
+        inline fun <T> remember(calculation: () -> T): T = calculation()
+
+        @Composable
+        inline fun <T, V1> remember(
+            v1: V1,
+            calculation: () -> T
+        ): T = calculation()
+
+        @Composable
+        inline fun <T, V1, V2> remember(
+            v1: V1,
+            v2: V2,
+            calculation: () -> T
+        ): T = calculation()
+
+        @Composable
+        inline fun <T, V1, V2, V3> remember(
+            v1: V1,
+            v2: V2,
+            v3: V3,
+            calculation: () -> T
+        ): T = calculation()
+
+        @Composable
+        inline fun <V> remember(
+            vararg inputs: Any?,
+            calculation: () -> V
+        ): V = calculation()
+    """
+)
diff --git a/compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/RestorableStateHolder.kt b/compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/RestorableStateHolder.kt
index ad90774..f72bd7f 100644
--- a/compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/RestorableStateHolder.kt
+++ b/compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/RestorableStateHolder.kt
@@ -104,7 +104,7 @@
                 content = content
             )
             onActive {
-                require(key !in registryHolders)
+                require(key !in registryHolders) { "Key $key was used multiple times " }
                 savedStates -= key
                 registryHolders[key] = registryHolder
                 onDispose {
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index e3a6579..546503d 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -1,7 +1,7 @@
 // Signature format: 4.0
 package androidx.compose.runtime {
 
-  @androidx.compose.runtime.ExperimentalComposeApi public abstract class AbstractApplier<T> implements androidx.compose.runtime.Applier<T> {
+  public abstract class AbstractApplier<T> implements androidx.compose.runtime.Applier<T> {
     ctor public AbstractApplier(T? root);
     method public final void clear();
     method public void down(T? node);
@@ -34,14 +34,7 @@
     method public static <T> androidx.compose.runtime.ProvidableAmbient<T> staticAmbientOf(optional kotlin.jvm.functions.Function0<? extends T>? defaultFactory);
   }
 
-  @androidx.compose.runtime.InternalComposeApi public final class Anchor {
-    method public boolean getValid();
-    method public int toIndexFor(androidx.compose.runtime.SlotTable slots);
-    method public int toIndexFor(androidx.compose.runtime.SlotWriter writer);
-    property public final boolean valid;
-  }
-
-  @androidx.compose.runtime.ExperimentalComposeApi public interface Applier<N> {
+  public interface Applier<N> {
     method public void clear();
     method public void down(N? node);
     method public N! getCurrent();
@@ -85,7 +78,7 @@
   }
 
   public final class Composer<N> {
-    ctor public Composer(androidx.compose.runtime.SlotTable slotTable, @kotlin.PublishedApi androidx.compose.runtime.Applier<N> applier, androidx.compose.runtime.CompositionReference parentReference);
+    ctor public Composer(@kotlin.PublishedApi androidx.compose.runtime.Applier<N> applier, androidx.compose.runtime.CompositionReference parentReference);
     method @androidx.compose.runtime.InternalComposeApi public void applyChanges();
     method @androidx.compose.runtime.ComposeCompilerApi public inline <T> T! cache(boolean invalid, kotlin.jvm.functions.Function0<? extends T> block);
     method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(Object? value);
@@ -98,16 +91,17 @@
     method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(double value);
     method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(int value);
     method @androidx.compose.runtime.InternalComposeApi public void collectKeySourceInformation();
+    method @androidx.compose.runtime.InternalComposeApi public void collectParameterInformation();
     method @androidx.compose.runtime.InternalComposeApi public void composeInitial(kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method @androidx.compose.runtime.ComposeCompilerApi public void endDefaults();
     method @androidx.compose.runtime.ComposeCompilerApi public void endMovableGroup();
     method @androidx.compose.runtime.ComposeCompilerApi public void endReplaceableGroup();
     method @androidx.compose.runtime.ComposeCompilerApi public androidx.compose.runtime.ScopeUpdateScope? endRestartGroup();
     method @org.jetbrains.annotations.TestOnly public kotlin.coroutines.CoroutineContext getApplyCoroutineContext();
+    method public androidx.compose.runtime.CompositionData getCompositionData();
     method public int getCurrentCompoundKeyHash();
     method public boolean getDefaultsInvalid();
     method public boolean getSkipping();
-    method public androidx.compose.runtime.SlotTable getSlotTable();
     method @androidx.compose.runtime.ComposeCompilerApi public Object joinKey(Object? left, Object? right);
     method @androidx.compose.runtime.InternalComposeApi public boolean recompose();
     method @androidx.compose.runtime.InternalComposeApi public void recordModificationsOf(java.util.Set<?> values);
@@ -122,11 +116,12 @@
     method @androidx.compose.runtime.ComposeCompilerApi public void startReplaceableGroup(int key, String? sourceInformation);
     method @androidx.compose.runtime.ComposeCompilerApi public void startRestartGroup(int key);
     method @androidx.compose.runtime.ComposeCompilerApi public void startRestartGroup(int key, String? sourceInformation);
+    method @androidx.compose.runtime.InternalComposeApi public void verifyConsistent();
     property @org.jetbrains.annotations.TestOnly public final kotlin.coroutines.CoroutineContext applyCoroutineContext;
+    property public final androidx.compose.runtime.CompositionData compositionData;
     property public final int currentCompoundKeyHash;
     property public final boolean defaultsInvalid;
     property public final boolean skipping;
-    property public final androidx.compose.runtime.SlotTable slotTable;
   }
 
   public final class ComposerKt {
@@ -139,6 +134,24 @@
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public interface CompositionData {
+    method public Iterable<androidx.compose.runtime.CompositionGroup> getCompositionGroups();
+    method public boolean isEmpty();
+    property public abstract Iterable<androidx.compose.runtime.CompositionGroup> compositionGroups;
+    property public abstract boolean isEmpty;
+  }
+
+  public interface CompositionGroup extends androidx.compose.runtime.CompositionData {
+    method public Iterable<java.lang.Object> getData();
+    method public Object getKey();
+    method public Object? getNode();
+    method public String? getSourceInfo();
+    property public abstract Iterable<java.lang.Object> data;
+    property public abstract Object key;
+    property public abstract Object? node;
+    property public abstract String? sourceInfo;
+  }
+
   public final class CompositionKt {
     method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition compositionFor(Object key, androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionReference parent, optional kotlin.jvm.functions.Function0<kotlin.Unit> onCreated);
   }
@@ -264,6 +277,15 @@
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NoLiveLiterals {
   }
 
+  public final class PausableMonotonicFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
+    ctor public PausableMonotonicFrameClock(androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock);
+    method public boolean isPaused();
+    method public void pause();
+    method public void resume();
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public final boolean isPaused;
+  }
+
   public final class ProduceStateKt {
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, @kotlin.BuilderInference kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? subject, @kotlin.BuilderInference kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
@@ -293,11 +315,13 @@
   public final class Recomposer extends androidx.compose.runtime.CompositionReference {
     ctor public Recomposer(kotlin.coroutines.CoroutineContext effectCoroutineContext);
     method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public int getChangeCount();
     method public boolean hasInvalidations();
     method public suspend Object? join(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method @Deprecated public suspend Object? recomposeAndApplyChanges(long frameCount, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public suspend Object? runRecomposeAndApplyChanges(kotlin.coroutines.Continuation<?> p);
     method public void shutDown();
+    property public final int changeCount;
     field public static final androidx.compose.runtime.Recomposer.Companion Companion;
   }
 
@@ -340,150 +364,10 @@
     method public inline void update(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,kotlin.Unit> block);
   }
 
-  @androidx.compose.runtime.InternalComposeApi public final class SlotReader {
-    ctor public SlotReader(androidx.compose.runtime.SlotTable table);
-    method public androidx.compose.runtime.Anchor anchor(optional int index);
-    method public void beginEmpty();
-    method public void close();
-    method public void endEmpty();
-    method public void endGroup();
-    method public java.util.List<androidx.compose.runtime.KeyInfo> extractKeys();
-    method public Object? get(int index);
-    method public int getCurrentEnd();
-    method public int getCurrentGroup();
-    method public Object! getGroupAux();
-    method public int getGroupEnd();
-    method public int getGroupKey();
-    method public Object! getGroupNode();
-    method public Object! getGroupObjectKey();
-    method public int getGroupSize();
-    method public int getGroupSlotCount();
-    method public int getGroupSlotIndex();
-    method public boolean getInEmpty();
-    method public int getNodeCount();
-    method public int getParent();
-    method public int getParentNodes();
-    method public int getSize();
-    method public int getSlot();
-    method public Object? groupAux(int index);
-    method public int groupEnd(int index);
-    method public Object? groupGet(int index);
-    method public int groupKey(int index);
-    method public int groupKey(androidx.compose.runtime.Anchor anchor);
-    method public Object? groupObjectKey(int index);
-    method public int groupSize(int index);
-    method public boolean hasObjectKey(int index);
-    method public boolean isGroupEnd();
-    method public boolean isNode();
-    method public boolean isNode(int index);
-    method public Object? next();
-    method public Object? node(int index);
-    method public int nodeCount(int index);
-    method public int parent(int index);
-    method public int parentOf(int index);
-    method public void reposition(int index);
-    method public void restoreParent(int index);
-    method public int skipGroup();
-    method public void skipToGroupEnd();
-    method public void startGroup();
-    method public void startNode();
-    property public final int currentEnd;
-    property public final int currentGroup;
-    property public final Object! groupAux;
-    property public final int groupEnd;
-    property public final int groupKey;
-    property public final Object! groupNode;
-    property public final Object! groupObjectKey;
-    property public final int groupSize;
-    property public final int groupSlotCount;
-    property public final int groupSlotIndex;
-    property public final boolean inEmpty;
-    property public final boolean isGroupEnd;
-    property public final boolean isNode;
-    property public final int nodeCount;
-    property public final int parent;
-    property public final int parentNodes;
-    property public final int size;
-    property public final int slot;
-  }
-
-  @androidx.compose.runtime.InternalComposeApi public final class SlotTable {
-    ctor public SlotTable();
-    method public int anchorIndex(androidx.compose.runtime.Anchor anchor);
-    method public String asString();
-    method public int[] getGroups();
-    method public int getGroupsSize();
-    method public Object![] getSlots();
-    method public int getSlotsSize();
-    method public boolean isEmpty();
-    method public androidx.compose.runtime.SlotReader openReader();
-    method public androidx.compose.runtime.SlotWriter openWriter();
-    method public boolean ownsAnchor(androidx.compose.runtime.Anchor anchor);
-    method public inline <T> T! read(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SlotReader,? extends T> block);
-    method public void verifyWellFormed();
-    method public inline <T> T! write(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SlotWriter,? extends T> block);
-    property public final int[] groups;
-    property public final int groupsSize;
-    property public final boolean isEmpty;
-    property public final Object![] slots;
-    property public final int slotsSize;
-  }
-
   public final class SlotTableKt {
     method public static java.util.List<java.lang.Integer> slice(int[], Iterable<java.lang.Integer> indices);
   }
 
-  @androidx.compose.runtime.InternalComposeApi public final class SlotWriter {
-    method public void advanceBy(int amount);
-    method public androidx.compose.runtime.Anchor anchor(optional int index);
-    method public int anchorIndex(androidx.compose.runtime.Anchor anchor);
-    method public void beginInsert();
-    method public void close();
-    method public int endGroup();
-    method public void endInsert();
-    method public void ensureStarted(int index);
-    method public void ensureStarted(androidx.compose.runtime.Anchor anchor);
-    method public boolean getClosed();
-    method public int getCurrentGroup();
-    method public int getParent();
-    method public Object? groupAux(int index);
-    method public int groupKey(int index);
-    method public Object? groupObjectKey(int index);
-    method public int groupSize(int index);
-    method public java.util.Iterator<java.lang.Object> groupSlots();
-    method public String groupsAsString();
-    method public boolean isGroupEnd();
-    method public boolean isNode();
-    method public java.util.List<androidx.compose.runtime.Anchor> moveFrom(androidx.compose.runtime.SlotTable table, int index);
-    method public void moveGroup(int offset);
-    method public Object? node(int index);
-    method public int parent(int index);
-    method public int parent(androidx.compose.runtime.Anchor anchor);
-    method public boolean removeGroup();
-    method public void seek(androidx.compose.runtime.Anchor anchor);
-    method public void set(Object? value);
-    method public Object? set(int index, Object? value);
-    method public Object? skip();
-    method public int skipGroup();
-    method public void skipToGroupEnd();
-    method public void startData(int key, Object? objectKey, Object? aux);
-    method public void startData(int key, Object? aux);
-    method public void startGroup();
-    method public void startGroup(int key);
-    method public void startGroup(int key, Object? dataKey);
-    method public void startNode(Object? key);
-    method public void startNode(Object? key, Object? node);
-    method public Object? update(Object? value);
-    method public void updateAux(Object? value);
-    method public void updateNode(Object? value);
-    method public void updateParentNode(Object? value);
-    property public final boolean closed;
-    property public final int currentGroup;
-    property public final boolean isGroupEnd;
-    property public final boolean isNode;
-    property public final int parent;
-  }
-
   public interface SnapshotMutationPolicy<T> {
     method public boolean equivalent(T? a, T? b);
     method @androidx.compose.runtime.ExperimentalComposeApi public default T? merge(T? previous, T? current, T? applied);
@@ -666,6 +550,7 @@
   }
 
   public final class LiveLiteralKt {
+    method @androidx.compose.runtime.InternalComposeApi public static void enableLiveLiterals();
     method public static boolean isLiveLiteralsEnabled();
     method @androidx.compose.runtime.InternalComposeApi public static <T> androidx.compose.runtime.State<T> liveLiteral(String key, T? value);
     method @androidx.compose.runtime.InternalComposeApi public static void updateLiveLiteralValue(String key, Object? value);
@@ -877,7 +762,7 @@
 package androidx.compose.runtime.tooling {
 
   public final class InspectionTablesKt {
-    method public static androidx.compose.runtime.ProvidableAmbient<java.util.Set<androidx.compose.runtime.SlotTable>> getInspectionTables();
+    method public static androidx.compose.runtime.ProvidableAmbient<java.util.Set<androidx.compose.runtime.CompositionData>> getInspectionTables();
   }
 
 }
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index e3a6579..546503d 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -1,7 +1,7 @@
 // Signature format: 4.0
 package androidx.compose.runtime {
 
-  @androidx.compose.runtime.ExperimentalComposeApi public abstract class AbstractApplier<T> implements androidx.compose.runtime.Applier<T> {
+  public abstract class AbstractApplier<T> implements androidx.compose.runtime.Applier<T> {
     ctor public AbstractApplier(T? root);
     method public final void clear();
     method public void down(T? node);
@@ -34,14 +34,7 @@
     method public static <T> androidx.compose.runtime.ProvidableAmbient<T> staticAmbientOf(optional kotlin.jvm.functions.Function0<? extends T>? defaultFactory);
   }
 
-  @androidx.compose.runtime.InternalComposeApi public final class Anchor {
-    method public boolean getValid();
-    method public int toIndexFor(androidx.compose.runtime.SlotTable slots);
-    method public int toIndexFor(androidx.compose.runtime.SlotWriter writer);
-    property public final boolean valid;
-  }
-
-  @androidx.compose.runtime.ExperimentalComposeApi public interface Applier<N> {
+  public interface Applier<N> {
     method public void clear();
     method public void down(N? node);
     method public N! getCurrent();
@@ -85,7 +78,7 @@
   }
 
   public final class Composer<N> {
-    ctor public Composer(androidx.compose.runtime.SlotTable slotTable, @kotlin.PublishedApi androidx.compose.runtime.Applier<N> applier, androidx.compose.runtime.CompositionReference parentReference);
+    ctor public Composer(@kotlin.PublishedApi androidx.compose.runtime.Applier<N> applier, androidx.compose.runtime.CompositionReference parentReference);
     method @androidx.compose.runtime.InternalComposeApi public void applyChanges();
     method @androidx.compose.runtime.ComposeCompilerApi public inline <T> T! cache(boolean invalid, kotlin.jvm.functions.Function0<? extends T> block);
     method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(Object? value);
@@ -98,16 +91,17 @@
     method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(double value);
     method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(int value);
     method @androidx.compose.runtime.InternalComposeApi public void collectKeySourceInformation();
+    method @androidx.compose.runtime.InternalComposeApi public void collectParameterInformation();
     method @androidx.compose.runtime.InternalComposeApi public void composeInitial(kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method @androidx.compose.runtime.ComposeCompilerApi public void endDefaults();
     method @androidx.compose.runtime.ComposeCompilerApi public void endMovableGroup();
     method @androidx.compose.runtime.ComposeCompilerApi public void endReplaceableGroup();
     method @androidx.compose.runtime.ComposeCompilerApi public androidx.compose.runtime.ScopeUpdateScope? endRestartGroup();
     method @org.jetbrains.annotations.TestOnly public kotlin.coroutines.CoroutineContext getApplyCoroutineContext();
+    method public androidx.compose.runtime.CompositionData getCompositionData();
     method public int getCurrentCompoundKeyHash();
     method public boolean getDefaultsInvalid();
     method public boolean getSkipping();
-    method public androidx.compose.runtime.SlotTable getSlotTable();
     method @androidx.compose.runtime.ComposeCompilerApi public Object joinKey(Object? left, Object? right);
     method @androidx.compose.runtime.InternalComposeApi public boolean recompose();
     method @androidx.compose.runtime.InternalComposeApi public void recordModificationsOf(java.util.Set<?> values);
@@ -122,11 +116,12 @@
     method @androidx.compose.runtime.ComposeCompilerApi public void startReplaceableGroup(int key, String? sourceInformation);
     method @androidx.compose.runtime.ComposeCompilerApi public void startRestartGroup(int key);
     method @androidx.compose.runtime.ComposeCompilerApi public void startRestartGroup(int key, String? sourceInformation);
+    method @androidx.compose.runtime.InternalComposeApi public void verifyConsistent();
     property @org.jetbrains.annotations.TestOnly public final kotlin.coroutines.CoroutineContext applyCoroutineContext;
+    property public final androidx.compose.runtime.CompositionData compositionData;
     property public final int currentCompoundKeyHash;
     property public final boolean defaultsInvalid;
     property public final boolean skipping;
-    property public final androidx.compose.runtime.SlotTable slotTable;
   }
 
   public final class ComposerKt {
@@ -139,6 +134,24 @@
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public interface CompositionData {
+    method public Iterable<androidx.compose.runtime.CompositionGroup> getCompositionGroups();
+    method public boolean isEmpty();
+    property public abstract Iterable<androidx.compose.runtime.CompositionGroup> compositionGroups;
+    property public abstract boolean isEmpty;
+  }
+
+  public interface CompositionGroup extends androidx.compose.runtime.CompositionData {
+    method public Iterable<java.lang.Object> getData();
+    method public Object getKey();
+    method public Object? getNode();
+    method public String? getSourceInfo();
+    property public abstract Iterable<java.lang.Object> data;
+    property public abstract Object key;
+    property public abstract Object? node;
+    property public abstract String? sourceInfo;
+  }
+
   public final class CompositionKt {
     method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition compositionFor(Object key, androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionReference parent, optional kotlin.jvm.functions.Function0<kotlin.Unit> onCreated);
   }
@@ -264,6 +277,15 @@
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NoLiveLiterals {
   }
 
+  public final class PausableMonotonicFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
+    ctor public PausableMonotonicFrameClock(androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock);
+    method public boolean isPaused();
+    method public void pause();
+    method public void resume();
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public final boolean isPaused;
+  }
+
   public final class ProduceStateKt {
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, @kotlin.BuilderInference kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? subject, @kotlin.BuilderInference kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
@@ -293,11 +315,13 @@
   public final class Recomposer extends androidx.compose.runtime.CompositionReference {
     ctor public Recomposer(kotlin.coroutines.CoroutineContext effectCoroutineContext);
     method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public int getChangeCount();
     method public boolean hasInvalidations();
     method public suspend Object? join(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method @Deprecated public suspend Object? recomposeAndApplyChanges(long frameCount, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public suspend Object? runRecomposeAndApplyChanges(kotlin.coroutines.Continuation<?> p);
     method public void shutDown();
+    property public final int changeCount;
     field public static final androidx.compose.runtime.Recomposer.Companion Companion;
   }
 
@@ -340,150 +364,10 @@
     method public inline void update(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,kotlin.Unit> block);
   }
 
-  @androidx.compose.runtime.InternalComposeApi public final class SlotReader {
-    ctor public SlotReader(androidx.compose.runtime.SlotTable table);
-    method public androidx.compose.runtime.Anchor anchor(optional int index);
-    method public void beginEmpty();
-    method public void close();
-    method public void endEmpty();
-    method public void endGroup();
-    method public java.util.List<androidx.compose.runtime.KeyInfo> extractKeys();
-    method public Object? get(int index);
-    method public int getCurrentEnd();
-    method public int getCurrentGroup();
-    method public Object! getGroupAux();
-    method public int getGroupEnd();
-    method public int getGroupKey();
-    method public Object! getGroupNode();
-    method public Object! getGroupObjectKey();
-    method public int getGroupSize();
-    method public int getGroupSlotCount();
-    method public int getGroupSlotIndex();
-    method public boolean getInEmpty();
-    method public int getNodeCount();
-    method public int getParent();
-    method public int getParentNodes();
-    method public int getSize();
-    method public int getSlot();
-    method public Object? groupAux(int index);
-    method public int groupEnd(int index);
-    method public Object? groupGet(int index);
-    method public int groupKey(int index);
-    method public int groupKey(androidx.compose.runtime.Anchor anchor);
-    method public Object? groupObjectKey(int index);
-    method public int groupSize(int index);
-    method public boolean hasObjectKey(int index);
-    method public boolean isGroupEnd();
-    method public boolean isNode();
-    method public boolean isNode(int index);
-    method public Object? next();
-    method public Object? node(int index);
-    method public int nodeCount(int index);
-    method public int parent(int index);
-    method public int parentOf(int index);
-    method public void reposition(int index);
-    method public void restoreParent(int index);
-    method public int skipGroup();
-    method public void skipToGroupEnd();
-    method public void startGroup();
-    method public void startNode();
-    property public final int currentEnd;
-    property public final int currentGroup;
-    property public final Object! groupAux;
-    property public final int groupEnd;
-    property public final int groupKey;
-    property public final Object! groupNode;
-    property public final Object! groupObjectKey;
-    property public final int groupSize;
-    property public final int groupSlotCount;
-    property public final int groupSlotIndex;
-    property public final boolean inEmpty;
-    property public final boolean isGroupEnd;
-    property public final boolean isNode;
-    property public final int nodeCount;
-    property public final int parent;
-    property public final int parentNodes;
-    property public final int size;
-    property public final int slot;
-  }
-
-  @androidx.compose.runtime.InternalComposeApi public final class SlotTable {
-    ctor public SlotTable();
-    method public int anchorIndex(androidx.compose.runtime.Anchor anchor);
-    method public String asString();
-    method public int[] getGroups();
-    method public int getGroupsSize();
-    method public Object![] getSlots();
-    method public int getSlotsSize();
-    method public boolean isEmpty();
-    method public androidx.compose.runtime.SlotReader openReader();
-    method public androidx.compose.runtime.SlotWriter openWriter();
-    method public boolean ownsAnchor(androidx.compose.runtime.Anchor anchor);
-    method public inline <T> T! read(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SlotReader,? extends T> block);
-    method public void verifyWellFormed();
-    method public inline <T> T! write(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SlotWriter,? extends T> block);
-    property public final int[] groups;
-    property public final int groupsSize;
-    property public final boolean isEmpty;
-    property public final Object![] slots;
-    property public final int slotsSize;
-  }
-
   public final class SlotTableKt {
     method public static java.util.List<java.lang.Integer> slice(int[], Iterable<java.lang.Integer> indices);
   }
 
-  @androidx.compose.runtime.InternalComposeApi public final class SlotWriter {
-    method public void advanceBy(int amount);
-    method public androidx.compose.runtime.Anchor anchor(optional int index);
-    method public int anchorIndex(androidx.compose.runtime.Anchor anchor);
-    method public void beginInsert();
-    method public void close();
-    method public int endGroup();
-    method public void endInsert();
-    method public void ensureStarted(int index);
-    method public void ensureStarted(androidx.compose.runtime.Anchor anchor);
-    method public boolean getClosed();
-    method public int getCurrentGroup();
-    method public int getParent();
-    method public Object? groupAux(int index);
-    method public int groupKey(int index);
-    method public Object? groupObjectKey(int index);
-    method public int groupSize(int index);
-    method public java.util.Iterator<java.lang.Object> groupSlots();
-    method public String groupsAsString();
-    method public boolean isGroupEnd();
-    method public boolean isNode();
-    method public java.util.List<androidx.compose.runtime.Anchor> moveFrom(androidx.compose.runtime.SlotTable table, int index);
-    method public void moveGroup(int offset);
-    method public Object? node(int index);
-    method public int parent(int index);
-    method public int parent(androidx.compose.runtime.Anchor anchor);
-    method public boolean removeGroup();
-    method public void seek(androidx.compose.runtime.Anchor anchor);
-    method public void set(Object? value);
-    method public Object? set(int index, Object? value);
-    method public Object? skip();
-    method public int skipGroup();
-    method public void skipToGroupEnd();
-    method public void startData(int key, Object? objectKey, Object? aux);
-    method public void startData(int key, Object? aux);
-    method public void startGroup();
-    method public void startGroup(int key);
-    method public void startGroup(int key, Object? dataKey);
-    method public void startNode(Object? key);
-    method public void startNode(Object? key, Object? node);
-    method public Object? update(Object? value);
-    method public void updateAux(Object? value);
-    method public void updateNode(Object? value);
-    method public void updateParentNode(Object? value);
-    property public final boolean closed;
-    property public final int currentGroup;
-    property public final boolean isGroupEnd;
-    property public final boolean isNode;
-    property public final int parent;
-  }
-
   public interface SnapshotMutationPolicy<T> {
     method public boolean equivalent(T? a, T? b);
     method @androidx.compose.runtime.ExperimentalComposeApi public default T? merge(T? previous, T? current, T? applied);
@@ -666,6 +550,7 @@
   }
 
   public final class LiveLiteralKt {
+    method @androidx.compose.runtime.InternalComposeApi public static void enableLiveLiterals();
     method public static boolean isLiveLiteralsEnabled();
     method @androidx.compose.runtime.InternalComposeApi public static <T> androidx.compose.runtime.State<T> liveLiteral(String key, T? value);
     method @androidx.compose.runtime.InternalComposeApi public static void updateLiveLiteralValue(String key, Object? value);
@@ -877,7 +762,7 @@
 package androidx.compose.runtime.tooling {
 
   public final class InspectionTablesKt {
-    method public static androidx.compose.runtime.ProvidableAmbient<java.util.Set<androidx.compose.runtime.SlotTable>> getInspectionTables();
+    method public static androidx.compose.runtime.ProvidableAmbient<java.util.Set<androidx.compose.runtime.CompositionData>> getInspectionTables();
   }
 
 }
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 4dc7d7e..623a343 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -1,7 +1,7 @@
 // Signature format: 4.0
 package androidx.compose.runtime {
 
-  @androidx.compose.runtime.ExperimentalComposeApi public abstract class AbstractApplier<T> implements androidx.compose.runtime.Applier<T> {
+  public abstract class AbstractApplier<T> implements androidx.compose.runtime.Applier<T> {
     ctor public AbstractApplier(T? root);
     method public final void clear();
     method public void down(T? node);
@@ -34,14 +34,7 @@
     method public static <T> androidx.compose.runtime.ProvidableAmbient<T> staticAmbientOf(optional kotlin.jvm.functions.Function0<? extends T>? defaultFactory);
   }
 
-  @androidx.compose.runtime.InternalComposeApi public final class Anchor {
-    method public boolean getValid();
-    method public int toIndexFor(androidx.compose.runtime.SlotTable slots);
-    method public int toIndexFor(androidx.compose.runtime.SlotWriter writer);
-    property public final boolean valid;
-  }
-
-  @androidx.compose.runtime.ExperimentalComposeApi public interface Applier<N> {
+  public interface Applier<N> {
     method public void clear();
     method public void down(N? node);
     method public N! getCurrent();
@@ -85,7 +78,7 @@
   }
 
   public final class Composer<N> {
-    ctor public Composer(androidx.compose.runtime.SlotTable slotTable, @kotlin.PublishedApi androidx.compose.runtime.Applier<N> applier, androidx.compose.runtime.CompositionReference parentReference);
+    ctor public Composer(@kotlin.PublishedApi androidx.compose.runtime.Applier<N> applier, androidx.compose.runtime.CompositionReference parentReference);
     method @androidx.compose.runtime.InternalComposeApi public void applyChanges();
     method @androidx.compose.runtime.ComposeCompilerApi public inline <T> T! cache(boolean invalid, kotlin.jvm.functions.Function0<? extends T> block);
     method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(Object? value);
@@ -98,6 +91,7 @@
     method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(double value);
     method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(int value);
     method @androidx.compose.runtime.InternalComposeApi public void collectKeySourceInformation();
+    method @androidx.compose.runtime.InternalComposeApi public void collectParameterInformation();
     method @androidx.compose.runtime.InternalComposeApi public void composeInitial(kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method @kotlin.PublishedApi internal <T> T! consume(androidx.compose.runtime.Ambient<T> key);
     method @kotlin.PublishedApi internal void emitNode(Object? node);
@@ -107,10 +101,10 @@
     method @androidx.compose.runtime.ComposeCompilerApi public void endReplaceableGroup();
     method @androidx.compose.runtime.ComposeCompilerApi public androidx.compose.runtime.ScopeUpdateScope? endRestartGroup();
     method @org.jetbrains.annotations.TestOnly public kotlin.coroutines.CoroutineContext getApplyCoroutineContext();
+    method public androidx.compose.runtime.CompositionData getCompositionData();
     method public int getCurrentCompoundKeyHash();
     method public boolean getDefaultsInvalid();
     method public boolean getSkipping();
-    method public androidx.compose.runtime.SlotTable getSlotTable();
     method @androidx.compose.runtime.ComposeCompilerApi public Object joinKey(Object? left, Object? right);
     method @kotlin.PublishedApi internal Object? nextSlot();
     method @androidx.compose.runtime.InternalComposeApi public boolean recompose();
@@ -129,11 +123,12 @@
     method @androidx.compose.runtime.ComposeCompilerApi public void startRestartGroup(int key, String? sourceInformation);
     method @kotlin.PublishedApi internal void updateValue(Object? value);
     method @kotlin.PublishedApi internal N! useNode();
+    method @androidx.compose.runtime.InternalComposeApi public void verifyConsistent();
     property @org.jetbrains.annotations.TestOnly public final kotlin.coroutines.CoroutineContext applyCoroutineContext;
+    property public final androidx.compose.runtime.CompositionData compositionData;
     property public final int currentCompoundKeyHash;
     property public final boolean defaultsInvalid;
     property public final boolean skipping;
-    property public final androidx.compose.runtime.SlotTable slotTable;
     field @kotlin.PublishedApi internal boolean inserting;
   }
 
@@ -159,6 +154,24 @@
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public interface CompositionData {
+    method public Iterable<androidx.compose.runtime.CompositionGroup> getCompositionGroups();
+    method public boolean isEmpty();
+    property public abstract Iterable<androidx.compose.runtime.CompositionGroup> compositionGroups;
+    property public abstract boolean isEmpty;
+  }
+
+  public interface CompositionGroup extends androidx.compose.runtime.CompositionData {
+    method public Iterable<java.lang.Object> getData();
+    method public Object getKey();
+    method public Object? getNode();
+    method public String? getSourceInfo();
+    property public abstract Iterable<java.lang.Object> data;
+    property public abstract Object key;
+    property public abstract Object? node;
+    property public abstract String? sourceInfo;
+  }
+
   public final class CompositionKt {
     method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition compositionFor(Object key, androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionReference parent, optional kotlin.jvm.functions.Function0<kotlin.Unit> onCreated);
   }
@@ -291,6 +304,15 @@
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NoLiveLiterals {
   }
 
+  public final class PausableMonotonicFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
+    ctor public PausableMonotonicFrameClock(androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock);
+    method public boolean isPaused();
+    method public void pause();
+    method public void resume();
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public final boolean isPaused;
+  }
+
   @kotlin.PublishedApi internal final class PreCommitScopeImpl implements androidx.compose.runtime.CommitScope androidx.compose.runtime.CompositionLifecycleObserver {
     ctor public PreCommitScopeImpl(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.CommitScope,kotlin.Unit> onCommit);
     method public void onDispose(kotlin.jvm.functions.Function0<kotlin.Unit> callback);
@@ -325,11 +347,13 @@
   public final class Recomposer extends androidx.compose.runtime.CompositionReference {
     ctor public Recomposer(kotlin.coroutines.CoroutineContext effectCoroutineContext);
     method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public int getChangeCount();
     method public boolean hasInvalidations();
     method public suspend Object? join(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method @Deprecated public suspend Object? recomposeAndApplyChanges(long frameCount, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public suspend Object? runRecomposeAndApplyChanges(kotlin.coroutines.Continuation<?> p);
     method public void shutDown();
+    property public final int changeCount;
     field public static final androidx.compose.runtime.Recomposer.Companion Companion;
   }
 
@@ -373,151 +397,11 @@
     method public inline void update(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,kotlin.Unit> block);
   }
 
-  @androidx.compose.runtime.InternalComposeApi public final class SlotReader {
-    ctor public SlotReader(androidx.compose.runtime.SlotTable table);
-    method public androidx.compose.runtime.Anchor anchor(optional int index);
-    method public void beginEmpty();
-    method public void close();
-    method public void endEmpty();
-    method public void endGroup();
-    method public java.util.List<androidx.compose.runtime.KeyInfo> extractKeys();
-    method public Object? get(int index);
-    method public int getCurrentEnd();
-    method public int getCurrentGroup();
-    method public Object! getGroupAux();
-    method public int getGroupEnd();
-    method public int getGroupKey();
-    method public Object! getGroupNode();
-    method public Object! getGroupObjectKey();
-    method public int getGroupSize();
-    method public int getGroupSlotCount();
-    method public int getGroupSlotIndex();
-    method public boolean getInEmpty();
-    method public int getNodeCount();
-    method public int getParent();
-    method public int getParentNodes();
-    method public int getSize();
-    method public int getSlot();
-    method public Object? groupAux(int index);
-    method public int groupEnd(int index);
-    method public Object? groupGet(int index);
-    method public int groupKey(int index);
-    method public int groupKey(androidx.compose.runtime.Anchor anchor);
-    method public Object? groupObjectKey(int index);
-    method public int groupSize(int index);
-    method public boolean hasObjectKey(int index);
-    method public boolean isGroupEnd();
-    method public boolean isNode();
-    method public boolean isNode(int index);
-    method public Object? next();
-    method public Object? node(int index);
-    method public int nodeCount(int index);
-    method public int parent(int index);
-    method public int parentOf(int index);
-    method public void reposition(int index);
-    method public void restoreParent(int index);
-    method public int skipGroup();
-    method public void skipToGroupEnd();
-    method public void startGroup();
-    method public void startNode();
-    property public final int currentEnd;
-    property public final int currentGroup;
-    property public final Object! groupAux;
-    property public final int groupEnd;
-    property public final int groupKey;
-    property public final Object! groupNode;
-    property public final Object! groupObjectKey;
-    property public final int groupSize;
-    property public final int groupSlotCount;
-    property public final int groupSlotIndex;
-    property public final boolean inEmpty;
-    property public final boolean isGroupEnd;
-    property public final boolean isNode;
-    property public final int nodeCount;
-    property public final int parent;
-    property public final int parentNodes;
-    property public final int size;
-    property public final int slot;
-  }
-
-  @androidx.compose.runtime.InternalComposeApi public final class SlotTable {
-    ctor public SlotTable();
-    method public int anchorIndex(androidx.compose.runtime.Anchor anchor);
-    method public String asString();
-    method public int[] getGroups();
-    method public int getGroupsSize();
-    method public Object![] getSlots();
-    method public int getSlotsSize();
-    method public boolean isEmpty();
-    method public androidx.compose.runtime.SlotReader openReader();
-    method public androidx.compose.runtime.SlotWriter openWriter();
-    method public boolean ownsAnchor(androidx.compose.runtime.Anchor anchor);
-    method public inline <T> T! read(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SlotReader,? extends T> block);
-    method public void verifyWellFormed();
-    method public inline <T> T! write(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SlotWriter,? extends T> block);
-    property public final int[] groups;
-    property public final int groupsSize;
-    property public final boolean isEmpty;
-    property public final Object![] slots;
-    property public final int slotsSize;
-  }
-
   public final class SlotTableKt {
     method public static java.util.List<java.lang.Integer> slice(int[], Iterable<java.lang.Integer> indices);
     field @kotlin.PublishedApi internal static final Object EMPTY;
   }
 
-  @androidx.compose.runtime.InternalComposeApi public final class SlotWriter {
-    method public void advanceBy(int amount);
-    method public androidx.compose.runtime.Anchor anchor(optional int index);
-    method public int anchorIndex(androidx.compose.runtime.Anchor anchor);
-    method public void beginInsert();
-    method public void close();
-    method public int endGroup();
-    method public void endInsert();
-    method public void ensureStarted(int index);
-    method public void ensureStarted(androidx.compose.runtime.Anchor anchor);
-    method public boolean getClosed();
-    method public int getCurrentGroup();
-    method public int getParent();
-    method public Object? groupAux(int index);
-    method public int groupKey(int index);
-    method public Object? groupObjectKey(int index);
-    method public int groupSize(int index);
-    method public java.util.Iterator<java.lang.Object> groupSlots();
-    method public String groupsAsString();
-    method public boolean isGroupEnd();
-    method public boolean isNode();
-    method public java.util.List<androidx.compose.runtime.Anchor> moveFrom(androidx.compose.runtime.SlotTable table, int index);
-    method public void moveGroup(int offset);
-    method public Object? node(int index);
-    method public int parent(int index);
-    method public int parent(androidx.compose.runtime.Anchor anchor);
-    method public boolean removeGroup();
-    method public void seek(androidx.compose.runtime.Anchor anchor);
-    method public void set(Object? value);
-    method public Object? set(int index, Object? value);
-    method public Object? skip();
-    method public int skipGroup();
-    method public void skipToGroupEnd();
-    method public void startData(int key, Object? objectKey, Object? aux);
-    method public void startData(int key, Object? aux);
-    method public void startGroup();
-    method public void startGroup(int key);
-    method public void startGroup(int key, Object? dataKey);
-    method public void startNode(Object? key);
-    method public void startNode(Object? key, Object? node);
-    method public Object? update(Object? value);
-    method public void updateAux(Object? value);
-    method public void updateNode(Object? value);
-    method public void updateParentNode(Object? value);
-    property public final boolean closed;
-    property public final int currentGroup;
-    property public final boolean isGroupEnd;
-    property public final boolean isNode;
-    property public final int parent;
-  }
-
   public interface SnapshotMutationPolicy<T> {
     method public boolean equivalent(T? a, T? b);
     method @androidx.compose.runtime.ExperimentalComposeApi public default T? merge(T? previous, T? current, T? applied);
@@ -704,6 +588,7 @@
   }
 
   public final class LiveLiteralKt {
+    method @androidx.compose.runtime.InternalComposeApi public static void enableLiveLiterals();
     method public static boolean isLiveLiteralsEnabled();
     method @androidx.compose.runtime.InternalComposeApi public static <T> androidx.compose.runtime.State<T> liveLiteral(String key, T? value);
     method @androidx.compose.runtime.InternalComposeApi public static void updateLiveLiteralValue(String key, Object? value);
@@ -925,7 +810,7 @@
 package androidx.compose.runtime.tooling {
 
   public final class InspectionTablesKt {
-    method public static androidx.compose.runtime.ProvidableAmbient<java.util.Set<androidx.compose.runtime.SlotTable>> getInspectionTables();
+    method public static androidx.compose.runtime.ProvidableAmbient<java.util.Set<androidx.compose.runtime.CompositionData>> getInspectionTables();
   }
 
 }
diff --git a/compose/runtime/runtime/build.gradle b/compose/runtime/runtime/build.gradle
index 5a834fb..ab49bf9 100644
--- a/compose/runtime/runtime/build.gradle
+++ b/compose/runtime/runtime/build.gradle
@@ -51,6 +51,7 @@
         testImplementation KOTLIN_TEST_JUNIT
         testImplementation(JUNIT)
         testImplementation(ROBOLECTRIC)
+        testImplementation(KOTLIN_COROUTINES_TEST)
 
         androidTestImplementation KOTLIN_TEST_JUNIT
         androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
@@ -102,6 +103,7 @@
 
             commonTest.dependencies {
                 implementation kotlin("test-junit")
+                implementation(KOTLIN_COROUTINES_TEST)
             }
             androidAndroidTest.dependencies {
                 implementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmark.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmark.kt
index 569136f..b71973c 100644
--- a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmark.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmark.kt
@@ -28,14 +28,11 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.test.TestMonotonicFrameClock
 import androidx.compose.ui.unit.dp
 import androidx.test.annotation.UiThreadTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runBlockingTest
-import kotlinx.coroutines.withContext
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -43,12 +40,13 @@
 
 @LargeTest
 @RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class ComposeBenchmark : ComposeBenchmarkBase() {
 
     @UiThreadTest
     @Test
-    fun benchmark_01_Compose_OneRect() {
+    fun benchmark_01_Compose_OneRect() = runBlockingTestWithFrameClock {
         val model = ColorModel()
         measureCompose {
             OneRect(model)
@@ -57,7 +55,7 @@
 
     @UiThreadTest
     @Test
-    fun benchmark_02_Compose_TenRects() {
+    fun benchmark_02_Compose_TenRects() = runBlockingTestWithFrameClock {
         val model = ColorModel()
         measureCompose {
             TenRects(model)
@@ -66,7 +64,7 @@
 
     @UiThreadTest
     @Test
-    fun benchmark_03_Compose_100Rects() {
+    fun benchmark_03_Compose_100Rects() = runBlockingTestWithFrameClock {
         val model = ColorModel()
         measureCompose {
             HundredRects(model = model)
@@ -87,19 +85,16 @@
         }
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @UiThreadTest
     @Test
-    fun benchmark_04_Recompose_OneRect_WithRecomposer() = runBlockingTest {
-        withContext(TestMonotonicFrameClock(this)) {
-            val model = ColorModel()
-            measureRecomposeSuspending {
-                compose {
-                    OneRect(model)
-                }
-                update {
-                    model.toggle()
-                }
+    fun benchmark_04_Recompose_OneRect_WithRecomposer() = runBlockingTestWithFrameClock {
+        val model = ColorModel()
+        measureRecomposeSuspending {
+            compose {
+                OneRect(model)
+            }
+            update {
+                model.toggle()
             }
         }
     }
diff --git a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmarkBase.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmarkBase.kt
index b044cc9..1bee4c7 100644
--- a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmarkBase.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmarkBase.kt
@@ -16,9 +16,6 @@
 
 package androidx.compose.runtime.benchmark
 
-import android.app.Activity
-import android.view.View
-import android.view.ViewGroup
 import androidx.benchmark.junit4.BenchmarkRule
 import androidx.benchmark.junit4.measureRepeated
 import androidx.compose.runtime.Composable
@@ -32,12 +29,17 @@
 import androidx.compose.runtime.snapshots.SnapshotReadObserver
 import androidx.compose.runtime.snapshots.SnapshotWriteObserver
 import androidx.compose.runtime.snapshots.takeMutableSnapshot
-import androidx.compose.ui.platform.AndroidOwner
 import androidx.compose.ui.platform.setContent
+import androidx.compose.ui.test.TestMonotonicFrameClock
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.DelayController
+import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.withContext
 import org.junit.Assert.assertTrue
 import org.junit.Assert.assertFalse
 import org.junit.Rule
@@ -51,17 +53,31 @@
     @get:Rule
     val activityRule = androidx.test.rule.ActivityTestRule(ComposeActivity::class.java)
 
-    fun measureCompose(block: @Composable () -> Unit) {
-        val activity = activityRule.activity
+    @ExperimentalCoroutinesApi
+    suspend fun DelayController.measureCompose(block: @Composable () -> Unit) = coroutineScope {
         var composition: Composition? = null
-        benchmarkRule.measureRepeated {
-            composition = activity.setContent(Recomposer.current(), block)
+        val activity = activityRule.activity
+        val recomposer = Recomposer(coroutineContext)
 
-            runWithTimingDisabled {
-                composition?.dispose()
+        try {
+            benchmarkRule.measureRepeatedSuspendable {
+                composition = activity.setContent(recomposer) {
+                    block()
+                }
+
+                runWithTimingDisabled {
+                    composition?.dispose()
+                    advanceUntilIdle()
+                    Runtime.getRuntime().let {
+                        it.gc()
+                    }
+                }
             }
+        } finally {
+            composition?.dispose()
+            advanceUntilIdle()
+            recomposer.shutDown()
         }
-        composition?.dispose()
     }
 
     fun measureRecompose(block: RecomposeReceiver.() -> Unit) {
@@ -147,6 +163,18 @@
     }
 }
 
+@ExperimentalCoroutinesApi
+fun runBlockingTestWithFrameClock(
+    context: CoroutineContext = EmptyCoroutineContext,
+    testBody: suspend TestCoroutineScope.() -> Unit
+) {
+    runBlockingTest(context) {
+        withContext(TestMonotonicFrameClock(this)) {
+            testBody()
+        }
+    }
+}
+
 inline fun BenchmarkRule.measureRepeatedSuspendable(block: BenchmarkRule.Scope.() -> Unit) {
     // Note: this is an extension function to discourage calling from Java.
 
@@ -189,25 +217,3 @@
         updateModelCb = block
     }
 }
-
-// TODO(chuckj): Consider refacgtoring to use AndroidTestCaseRunner from UI
-// This code is copied from AndroidTestCaseRunner.kt
-private fun findComposeView(activity: Activity): AndroidOwner? {
-    return findComposeView(activity.findViewById(android.R.id.content) as ViewGroup)
-}
-
-private fun findComposeView(view: View): AndroidOwner? {
-    if (view is AndroidOwner) {
-        return view
-    }
-
-    if (view is ViewGroup) {
-        for (i in 0 until view.childCount) {
-            val composeView = findComposeView(view.getChildAt(i))
-            if (composeView != null) {
-                return composeView
-            }
-        }
-    }
-    return null
-}
\ No newline at end of file
diff --git a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DeepTreeBenchmark.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DeepTreeBenchmark.kt
index 77fc3c0..3640ebd 100644
--- a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DeepTreeBenchmark.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DeepTreeBenchmark.kt
@@ -20,6 +20,7 @@
 import androidx.test.annotation.UiThreadTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -33,11 +34,12 @@
  */
 @LargeTest
 @RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class DeepTreeBenchmark : ComposeBenchmarkBase() {
     @UiThreadTest
     @Test
-    fun benchmark_deep_tree_01_depth1_breadth100_wrap2() {
+    fun benchmark_deep_tree_01_depth1_breadth100_wrap2() = runBlockingTestWithFrameClock {
         measureCompose {
             DeepTree(depth = 1, breadth = 100, wrap = 2)
         }
@@ -45,7 +47,7 @@
 
     @UiThreadTest
     @Test
-    fun benchmark_deep_tree_02_depth7_breadth3_wrap2() {
+    fun benchmark_deep_tree_02_depth7_breadth3_wrap2() = runBlockingTestWithFrameClock {
         measureCompose {
             DeepTree(depth = 7, breadth = 3, wrap = 2)
         }
@@ -53,7 +55,7 @@
 
     @UiThreadTest
     @Test
-    fun benchmark_deep_tree_03_depth2_breadth10_wrap2() {
+    fun benchmark_deep_tree_03_depth2_breadth10_wrap2() = runBlockingTestWithFrameClock {
         measureCompose {
             DeepTree(depth = 2, breadth = 10, wrap = 2)
         }
@@ -61,7 +63,7 @@
 
     @UiThreadTest
     @Test
-    fun benchmark_deep_tree_04_depth2_breadth10_wrap6() {
+    fun benchmark_deep_tree_04_depth2_breadth10_wrap6() = runBlockingTestWithFrameClock {
         measureCompose {
             DeepTree(depth = 2, breadth = 10, wrap = 6)
         }
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
index b1b6b8f..d932950 100644
--- a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
@@ -17,9 +17,9 @@
 @file:OptIn(ExperimentalComposeApi::class)
 package androidx.compose.runtime
 
+import android.view.View
 import android.widget.TextView
-import androidx.compose.ui.node.LayoutNode
-import androidx.compose.ui.platform.subcomposeInto
+import androidx.compose.ui.node.UiApplier
 import androidx.compose.ui.viewinterop.emitView
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -496,17 +496,20 @@
     }
 
     @Composable fun deferredSubCompose(block: @Composable () -> Unit): () -> Unit {
-        val container = remember { LayoutNode() }
+        val container = remember { View(activity) }
         val ref = Ref<CompositionReference>()
         narrowInvalidateForReference(ref = ref)
         return {
             @OptIn(ExperimentalComposeApi::class)
             // TODO(b/150390669): Review use of @ComposableContract(tracked = false)
-            subcomposeInto(
+            compositionFor(
                 container,
+                UiApplier(container),
                 ref.value
-            ) @ComposableContract(tracked = false) {
-                block()
+            ).apply {
+                setContent @ComposableContract(tracked = false) {
+                    block()
+                }
             }
         }
     }
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
index 4ccd97f..51a66a2 100644
--- a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
@@ -21,13 +21,13 @@
 import android.os.Bundle
 import android.os.Looper
 import android.view.Choreographer
+import android.view.View
 import android.view.ViewGroup
 import android.widget.LinearLayout
 import androidx.compose.runtime.snapshots.Snapshot
-import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.node.UiApplier
 import androidx.compose.ui.platform.AmbientContext
 import androidx.compose.ui.platform.setViewContent
-import androidx.compose.ui.platform.subcomposeInto
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 import kotlin.test.assertTrue
@@ -116,15 +116,18 @@
 
     @Composable
     fun subCompose(block: @Composable () -> Unit) {
-        val container = remember { LayoutNode() }
+        val container = remember { View(activity) }
         val reference = compositionReference()
         // TODO(b/150390669): Review use of @ComposableContract(tracked = false)
         @OptIn(ExperimentalComposeApi::class)
-        subcomposeInto(
+        compositionFor(
             container,
+            UiApplier(container),
             reference
-        ) @ComposableContract(tracked = false) {
-            block()
+        ).apply {
+            setContent @ComposableContract(tracked = false) {
+                block()
+            }
         }
     }
 }
diff --git a/compose/runtime/runtime/lint-baseline.xml b/compose/runtime/runtime/lint-baseline.xml
deleted file mode 100644
index 76c954a..0000000
--- a/compose/runtime/runtime/lint-baseline.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.2.0-alpha15" client="gradle" variant="debug" version="4.2.0-alpha15">
-
-    <issue
-        id="UnknownNullness"
-        message="Should explicitly declare type here since implicit type does not specify nullness"
-        errorLine1="    override fun removeAt(index: Int) = get(index).also { update { it.removeAt(index) } }"
-        errorLine2="                 ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt"
-            line="91"
-            column="18"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Should explicitly declare type here since implicit type does not specify nullness"
-        errorLine1="    override fun set(index: Int, element: T) = get(index).also { update { it.set(index, element) } }"
-        errorLine2="                 ~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt"
-            line="93"
-            column="18"/>
-    </issue>
-
-</issues>
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Applier.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Applier.kt
index e6f906d..2d1be12 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Applier.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Applier.kt
@@ -30,7 +30,6 @@
  * @see Composer
  * @see emit
  */
-@ExperimentalComposeApi
 interface Applier<N> {
     /**
      * The node that operations will be applied on at any given time. It is expected that the
@@ -190,7 +189,6 @@
  * @see Composer
  * @see emit
  */
-@ExperimentalComposeApi
 abstract class AbstractApplier<T>(val root: T) : Applier<T> {
     private val stack = mutableListOf<T>()
     override var current: T = root
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
index 3cc12dd..b669101 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
@@ -28,5 +28,5 @@
      * IMPORTANT: Whenever updating this value, please make sure to also update `versionTable` and
      * `minimumRuntimeVersionInt` in `VersionChecker.kt` of the compiler.
      */
-    const val version: Int = 2000
+    const val version: Int = 2100
 }
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 57c9888..0a39f35 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
@@ -351,11 +351,6 @@
  */
 class Composer<N>(
     /**
-     * Backing storage for the composition
-     */
-    val slotTable: SlotTable,
-
-    /**
      * An adapter that applies changes to the tree using the Applier abstraction.
      */
     @PublishedApi internal val applier: Applier<N>,
@@ -365,6 +360,7 @@
      */
     private val parentReference: CompositionReference
 ) {
+    private val slotTable: SlotTable = SlotTable()
     private val changes = mutableListOf<Change<N>>()
     private val lifecycleObservers = HashMap<
         CompositionLifecycleObserverHolder,
@@ -379,7 +375,7 @@
     private var nodeCountOverrides: IntArray? = null
     private var nodeCountVirtualOverrides: HashMap<Int, Int>? = null
     private var collectKeySources = false
-
+    private var collectParameterInformation = false
     private var nodeExpected = false
     private val observations: MutableList<Any> = mutableListOf()
     private val observationsProcessed: MutableList<Any> = mutableListOf()
@@ -554,6 +550,7 @@
         providersInvalidStack.push(providersInvalid.asInt())
         providersInvalid = changed(parentProvider)
         collectKeySources = parentReference.collectingKeySources
+        collectParameterInformation = parentReference.collectingParameterInformation
         resolveAmbient(InspectionTables, parentProvider)?.let {
             it.add(slotTable)
             parentReference.recordInspectionTable(it)
@@ -632,6 +629,15 @@
     }
 
     /**
+     * Start collecting parameter information. This enables the tools API to always be able to
+     * determine the parameter values of composable calls.
+     */
+    @InternalComposeApi
+    fun collectParameterInformation() {
+        collectParameterInformation = true
+    }
+
+    /**
      * Record that [value] was read from. If [recordWriteOf] or [recordModificationsOf] is called
      * with [value] then the corresponding [currentRecomposeScope] is invalidated.
      *
@@ -665,6 +671,31 @@
     }
 
     /**
+     * Throw a diagnostic exception if the internal tracking tables are inconsistent.
+     */
+    @InternalComposeApi
+    fun verifyConsistent() {
+        if (!isComposing) {
+            slotTable.verifyWellFormed()
+            insertTable.verifyWellFormed()
+            validateRecomposeScopeAnchors(slotTable)
+        }
+    }
+
+    private fun validateRecomposeScopeAnchors(slotTable: SlotTable) {
+        val scopes = slotTable.slots.map { it as? RecomposeScope }.filterNotNull()
+        for (scope in scopes) {
+            scope.anchor?.let { anchor ->
+                check(scope in slotTable.slotsOf(anchor.toIndexFor(slotTable))) {
+                    val dataIndex = slotTable.slots.indexOf(scope)
+                    "Misaligned anchor $anchor in scope $scope encountered, scope found at " +
+                        "$dataIndex"
+                }
+            }
+        }
+    }
+
+    /**
      * Record that the objects in [values] have been modified. This invalidates any recomposes
      * scopes  that were current when [recordReadOf] was called with an instance in [values].
      *
@@ -1137,6 +1168,9 @@
         }
     }
 
+    @InternalComposeApi
+    val compositionData: CompositionData get() = slotTable
+
     /**
      * Schedule a side effect to run when we apply composition changes.
      */
@@ -1287,11 +1321,16 @@
         startGroup(referenceKey, reference)
 
         var ref = nextSlot() as? CompositionReferenceHolder<*>
-        if (ref == null || !inserting) {
+        if (ref == null) {
             val scope = invalidateStack.peek()
             scope.used = true
             ref = CompositionReferenceHolder(
-                CompositionReferenceImpl(scope, currentCompoundKeyHash, collectKeySources)
+                CompositionReferenceImpl(
+                    scope,
+                    currentCompoundKeyHash,
+                    collectKeySources,
+                    collectParameterInformation
+                )
             )
             updateValue(ref)
         }
@@ -1998,7 +2037,7 @@
         val scope = if (invalidateStack.isNotEmpty()) invalidateStack.pop()
         else null
         scope?.requiresRecompose = false
-        val result = if (scope != null && (scope.used || collectKeySources)) {
+        val result = if (scope != null && (scope.used || collectParameterInformation)) {
             if (scope.anchor == null) {
                 scope.anchor = if (inserting) {
                     writer.anchor(writer.parent)
@@ -2061,7 +2100,7 @@
                     if (!complete) abortRoot()
                 }
             }
-            return true
+            return changes.isNotEmpty()
         }
         return false
     }
@@ -2430,9 +2469,10 @@
     private inner class CompositionReferenceImpl(
         val scope: RecomposeScope,
         override val compoundHashKey: Int,
-        override val collectingKeySources: Boolean
+        override val collectingKeySources: Boolean,
+        override val collectingParameterInformation: Boolean
     ) : CompositionReference() {
-        var inspectionTables: MutableSet<MutableSet<SlotTable>>? = null
+        var inspectionTables: MutableSet<MutableSet<CompositionData>>? = null
         val composers = mutableSetOf<Composer<*>>()
 
         fun dispose() {
@@ -2474,7 +2514,17 @@
         }
 
         override fun invalidate(composer: Composer<*>) {
-            invalidate(scope)
+            // Invalidate ourselves with our parent before we invalidate a child composer.
+            // This ensures that when we are scheduling recompositions, parents always
+            // recompose before their children just in case a recomposition in the parent
+            // would also cause other recomposition in the child.
+            // If the parent ends up having no real invalidations to process we will skip work
+            // for that composer along a fast path later.
+            // This invalidation process could be made more efficient as it's currently N^2 with
+            // subcomposition meta-tree depth thanks to the double recursive parent walk
+            // performed here, but we currently assume a low N.
+            parentReference.invalidate(this@Composer)
+            parentReference.invalidate(composer)
         }
 
         override fun <T> getAmbient(key: Ambient<T>): T {
@@ -2492,9 +2542,9 @@
             return ambientScopeAt(scope.anchor?.toIndexFor(slotTable) ?: 0)
         }
 
-        override fun recordInspectionTable(table: MutableSet<SlotTable>) {
+        override fun recordInspectionTable(table: MutableSet<CompositionData>) {
             (
-                inspectionTables ?: HashSet<MutableSet<SlotTable>>().also {
+                inspectionTables ?: HashSet<MutableSet<CompositionData>>().also {
                     inspectionTables = it
                 }
                 ).add(table)
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
index 458e047..15b94e6 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
@@ -73,7 +73,7 @@
 ): Composition = Compositions.findOrCreate(key) {
     CompositionImpl(
         parent,
-        composerFactory = { slots, rcmpsr -> Composer(slots, applier, rcmpsr) },
+        composerFactory = { parent -> Composer(applier, parent) },
         onDispose = { Compositions.onDisposed(key) }
     ).also {
         onCreated()
@@ -87,11 +87,10 @@
  */
 private class CompositionImpl(
     private val parent: CompositionReference,
-    composerFactory: (SlotTable, CompositionReference) -> Composer<*>,
+    composerFactory: (CompositionReference) -> Composer<*>,
     private val onDispose: () -> Unit
 ) : Composition {
-    private val slotTable: SlotTable = SlotTable()
-    private val composer: Composer<*> = composerFactory(slotTable, parent).also {
+    private val composer: Composer<*> = composerFactory(parent).also {
         parent.registerComposer(it)
     }
 
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionData.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionData.kt
new file mode 100644
index 0000000..53aa673
--- /dev/null
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionData.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.runtime
+
+/**
+ * A [CompositionData] is the data tracked by the composer during composition.
+ *
+ * This interface is not intended to be used directly and is provided to allow the tools API to
+ * have access to data tracked during composition. The tools API should be used instead which
+ * provides a more usable interpretation of the slot table.
+ */
+interface CompositionData {
+    /**
+     * Iterate the composition data in the group.  The composition data is structured as a tree of
+     * values that corresponds to the call graph of the functions that produced the tree.
+     * Interspersed are groups that represents the nodes themselves.
+     */
+    val compositionGroups: Iterable<CompositionGroup>
+
+    /**
+     * Returns true if no composition data has been collected. This occurs when the first
+     * composition into this composition data has not completed yet or, if it is a group, it
+     * doesn't contain any child groups.
+     */
+    val isEmpty: Boolean
+}
+
+/**
+ * [CompositionGroup] is a group of data slots tracked independently by composition. These groups
+ * correspond to flow control branches (such as if statements and function calls) as well as
+ * emitting of a node to the tree.
+ *
+ * This interface is not intended to be used directly and is provided to allow the tools API to
+ * have access to data tracked during composition. The tools API should be used instead which
+ * provides a more usable interpretation of the slot table.
+ */
+interface CompositionGroup : CompositionData {
+    /**
+     * A value used to identify the group within its siblings and is typically a compiler
+     * generated integer but can be an object if the [key] composable is used.
+     */
+    val key: Any
+
+    /**
+     * Information recorded by the compiler to help tooling identify the source that generated
+     * the group. The format of this string is internal and is interpreted by the tools API which
+     * translates this information into source file name and offsets.
+     */
+    val sourceInfo: String?
+
+    /**
+     * If the group represents a node this returns a non-null value which is the node that was
+     * emitted for the group.
+     */
+    val node: Any?
+
+    /**
+     * The data stored in the slot table for this group. This information includes the values
+     * stored for parameters that are checked for change, any value passed as a parameter for
+     * [remember] and the last value returned by [remember], etc.
+     */
+    val data: Iterable<Any?>
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt
index 51f7320..cdcb110 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt
@@ -36,11 +36,12 @@
 abstract class CompositionReference internal constructor() {
     internal abstract val compoundHashKey: Int
     internal abstract val collectingKeySources: Boolean
+    internal abstract val collectingParameterInformation: Boolean
     internal abstract val effectCoroutineContext: CoroutineContext
     internal abstract fun composeInitial(composer: Composer<*>, composable: @Composable () -> Unit)
     internal abstract fun invalidate(composer: Composer<*>)
 
-    internal open fun recordInspectionTable(table: MutableSet<SlotTable>) {}
+    internal open fun recordInspectionTable(table: MutableSet<CompositionData>) {}
     internal open fun registerComposer(composer: Composer<*>) {
         registerComposerWithRoot(composer)
     }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/PausableMonotonicFrameClock.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/PausableMonotonicFrameClock.kt
new file mode 100644
index 0000000..2312443
--- /dev/null
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/PausableMonotonicFrameClock.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime
+
+import androidx.compose.runtime.dispatch.MonotonicFrameClock
+
+/**
+ * A [MonotonicFrameClock] wrapper that can be [pause]d and [resume]d.
+ *
+ * A paused clock will not dispatch [withFrameNanos] events until it is resumed.
+ * Pausing a clock does **not** stop or change the frame times reported to [withFrameNanos] calls;
+ * the clock times reported will always remain consistent with [frameClock].
+ *
+ * [PausableMonotonicFrameClock] should be used in cases where frames should not be produced
+ * under some conditions, such as when a window hosting a UI is not currently visible.
+ * As clock times are not altered from the source [frameClock], animations in progress may
+ * be fully complete by the time the clock is resumed and a new frame is produced.
+ */
+class PausableMonotonicFrameClock(
+    private val frameClock: MonotonicFrameClock
+) : MonotonicFrameClock {
+    private val latch = Latch()
+
+    /**
+     * `true` if this clock is currently [paused][pause] or `false` if this clock is currently
+     * [resumed][resume]. A PausableMonotonicFrameClock is not paused at construction time.
+     */
+    val isPaused: Boolean
+        get() = !latch.isOpen
+
+    /**
+     * Pause the generation of frames. Pausing a clock that is already paused has no effect.
+     * While the clock is paused any calls to [withFrameNanos] will suspend until the clock is
+     * resumed before delegating to the wrapped [frameClock]'s [withFrameNanos] method.
+     * Call [resume] to resume generating frames.
+     */
+    fun pause() {
+        latch.closeLatch()
+    }
+
+    /**
+     * Resume the generation of frames. Any queued calls to [withFrameNanos] will resume and
+     * delegate to the wrapped [frameClock]'s [withFrameNanos] method.
+     */
+    fun resume() {
+        latch.openLatch()
+    }
+
+    override suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R {
+        latch.await()
+        return frameClock.withFrameNanos(onFrame)
+    }
+}
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
index b543735..8802e15 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
@@ -53,14 +53,11 @@
 suspend fun <R> withRunningRecomposer(
     block: suspend CoroutineScope.(recomposer: Recomposer) -> R
 ): R = coroutineScope {
-    val recomposerJob = Job(coroutineContext[Job])
-    val recomposer = Recomposer(coroutineContext + recomposerJob)
+    val recomposer = Recomposer(coroutineContext)
     // Will be cancelled when recomposerJob cancels
     launch { recomposer.runRecomposeAndApplyChanges() }
-    try {
-        block(recomposer)
-    } finally {
-        recomposerJob.cancel()
+    block(recomposer).also {
+        recomposer.shutDown()
     }
 }
 
@@ -77,6 +74,13 @@
 class Recomposer(
     effectCoroutineContext: CoroutineContext
 ) : CompositionReference() {
+    /**
+     * This is a running count of the number of times the recomposer awoke and applied changes to
+     * one or more composers. This count is unaffected if the composer awakes and recomposed but
+     * composition did not produce changes to apply.
+     */
+    var changeCount = 0
+        private set
 
     /**
      * This collection is its own lock, shared with [invalidComposersAwaiter]
@@ -306,10 +310,12 @@
 
                             // Actually perform recomposition for any invalidated composers
                             if (toRecompose.isNotEmpty()) {
+                                var changes = false
                                 for (i in 0 until toRecompose.size) {
-                                    performRecompose(toRecompose[i])
+                                    changes = performRecompose(toRecompose[i]) || changes
                                 }
                                 toRecompose.clear()
+                                if (changes) changeCount++
                             }
                         }
                     }
@@ -372,10 +378,9 @@
     private fun performRecompose(composer: Composer<*>): Boolean {
         if (composer.isComposing || composer.isDisposed) return false
         return composing(composer) {
-            composer.recompose().also {
-                Snapshot.notifyObjectsInitialized()
-                composer.applyChanges()
-            }
+            composer.recompose()
+        }.also {
+            composer.applyChanges()
         }
     }
 
@@ -433,7 +438,11 @@
     internal override val collectingKeySources: Boolean
         get() = false
 
-    internal override fun recordInspectionTable(table: MutableSet<SlotTable>) {
+    // Collecting parameter happens at the level of a composer; starts as false
+    internal override val collectingParameterInformation: Boolean
+        get() = false
+
+    internal override fun recordInspectionTable(table: MutableSet<CompositionData>) {
         // TODO: The root recomposer might be a better place to set up inspection
         // than the current configuration with an ambient
     }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
index f5e27ed..f64a51c 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
@@ -73,8 +73,7 @@
 
 // The public API refers only to Index values. Address values are internal.
 
-@InternalComposeApi
-class SlotTable {
+internal class SlotTable : CompositionData, Iterable<CompositionGroup> {
     /**
      * An array to store group information that is stored as groups of [Group_Fields_Size]
      * elements of the array. The [groups] array can be thought of as an array of an inline
@@ -113,7 +112,14 @@
     /**
      * Tracks whether there is an active writer.
      */
-    private var writer = false
+    internal var writer = false
+        private set
+
+    /**
+     * An internal version that is incremented whenever a writer is created. This is used to
+     * detect when an iterator created by [CompositionData] is invalid.
+     */
+    internal var version = 0
 
     /**
      * A list of currently active anchors.
@@ -123,7 +129,7 @@
     /**
      * Returns true if the slot table is empty
      */
-    val isEmpty get() = groupsSize == 0
+    override val isEmpty get() = groupsSize == 0
 
     /**
      * Read the slot table in [block]. Any number of readers can be created but a slot table cannot
@@ -178,6 +184,7 @@
         check(!writer) { "Cannot start a writer when another writer is pending" }
         check(readers <= 0) { "Cannot start a writer when a reader is pending" }
         writer = true
+        version++
         return SlotWriter(this)
     }
 
@@ -438,6 +445,11 @@
         val end = if (group + 1 < groupsSize) groups.dataAnchor(group + 1) else slots.size
         return slots.toList().subList(start, end)
     }
+
+    override val compositionGroups: Iterable<CompositionGroup> get() = this
+
+    override fun iterator(): Iterator<CompositionGroup> =
+        GroupIterator(this, 0, groupsSize)
 }
 
 /**
@@ -448,8 +460,7 @@
  * instead of the [SlotTable] as the anchor index could have shifted due to operations performed
  * on the writer.
  */
-@InternalComposeApi
-class Anchor internal constructor(loc: Int) {
+internal class Anchor(loc: Int) {
     internal var location: Int = loc
     val valid get() = location != Int.MIN_VALUE
     fun toIndexFor(slots: SlotTable) = slots.anchorIndex(this)
@@ -459,8 +470,7 @@
 /**
  * A reader of a slot table. See [SlotTable]
  */
-@InternalComposeApi
-class SlotReader(
+internal class SlotReader(
     /**
      * The table for whom this is a reader.
      */
@@ -773,7 +783,7 @@
     fun reposition(index: Int) {
         require(emptyCount == 0) { "Cannot reposition while in an empty region" }
         currentGroup = index
-        val parent = groups.parentAnchor(index)
+        val parent = if (index < groupsSize) groups.parentAnchor(index) else -1
         this.parent = parent
         if (parent < 0)
             this.currentEnd = groupsSize
@@ -890,8 +900,7 @@
 /**
  * The writer for a slot table. See [SlotTable] for details.
  */
-@InternalComposeApi
-class SlotWriter internal constructor(
+internal class SlotWriter(
     /**
      * The [SlotTable] for whom this is writer.
      */
@@ -2048,22 +2057,14 @@
         return if (len > 0) {
             var anchorsRemoved = false
             val anchors = anchors
-            if (groupGapLen == 0) {
-                // If there is no current gap, just update the anchors and make the removed slots
-                // the gap
-                if (anchors.isNotEmpty()) updateAnchors(groupGapStart, start)
-                groupGapStart = start
-                if (anchors.isNotEmpty()) anchorsRemoved = removeAnchors(start, len)
-                groupGapLen = len
-            } else {
-                // Move the gap to start of the removal and grow the gap
-                moveGroupGapTo(start)
-                if (anchors.isNotEmpty()) anchorsRemoved = removeAnchors(start, len)
-                groupGapStart = start
-                val previousGapLen = groupGapLen
-                val newGapLen = previousGapLen + len
-                groupGapLen = newGapLen
-            }
+
+            // Move the gap to start of the removal and grow the gap
+            moveGroupGapTo(start)
+            if (anchors.isNotEmpty()) anchorsRemoved = removeAnchors(start, len)
+            groupGapStart = start
+            val previousGapLen = groupGapLen
+            val newGapLen = previousGapLen + len
+            groupGapLen = newGapLen
 
             // Adjust the gap owner if necessary.
             val slotsGapOwner = slotsGapOwner
@@ -2350,6 +2351,79 @@
         if (index > parentAnchorPivot) index else size + index - parentAnchorPivot
 }
 
+private class GroupIterator(
+    val table: SlotTable,
+    start: Int,
+    val end: Int
+) : Iterator<CompositionGroup> {
+    private var index = start
+    private val version = table.version
+
+    init {
+        if (table.writer) throw ConcurrentModificationException()
+    }
+
+    override fun hasNext() = index < end
+
+    override fun next(): CompositionGroup {
+        validateRead()
+        val group = index
+        index += table.groups.groupSize(group)
+        return object : CompositionGroup, Iterable<CompositionGroup> {
+            override val isEmpty: Boolean get() = table.groups.groupSize(group) == 0
+
+            override val key: Any
+                get() = if (table.groups.hasObjectKey(group))
+                    table.slots[table.groups.objectKeyIndex(group)]!!
+                else table.groups.key(group)
+
+            override val sourceInfo: String?
+                get() = if (table.groups.hasAux(group))
+                    table.slots[table.groups.auxIndex(group)] as? String
+                else null
+
+            override val node: Any?
+                get() = if (table.groups.isNode(group))
+                    table.slots[table.groups.nodeIndex(group)] else
+                    null
+
+            override val data: Iterable<Any?> get() {
+                val start = table.groups.dataAnchor(group)
+                val end = if (group + 1 < table.groupsSize)
+                    table.groups.dataAnchor(group + 1) else table.slotsSize
+                return object : Iterable<Any?>, Iterator<Any?> {
+                    var index = start
+                    override fun iterator(): Iterator<Any?> = this
+                    override fun hasNext(): Boolean = index < end
+                    override fun next(): Any? =
+                        (
+                            if (index >= 0 && index < table.slots.size)
+                                table.slots[index]
+                            else null
+                            ).also { index++ }
+                }
+            }
+
+            override val compositionGroups: Iterable<CompositionGroup> get() = this
+
+            override fun iterator(): Iterator<CompositionGroup> {
+                validateRead()
+                return GroupIterator(
+                    table,
+                    group + 1,
+                    group + table.groups.groupSize(group)
+                )
+            }
+        }
+    }
+
+    private fun validateRead() {
+        if (table.version != version) {
+            throw ConcurrentModificationException()
+        }
+    }
+}
+
 // Parent -1 is reserved to be the root parent index so the anchor must pivot on -2.
 private const val parentAnchorPivot = -2
 
@@ -2539,7 +2613,6 @@
 /**
  * This is inlined here instead to avoid allocating a lambda for the compare when this is used.
  */
-@OptIn(InternalComposeApi::class)
 private fun ArrayList<Anchor>.search(location: Int, effectiveSize: Int): Int {
     var low = 0
     var high = size - 1
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/LiveLiteral.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/LiveLiteral.kt
index eecd738..b00fb76 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/LiveLiteral.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/LiveLiteral.kt
@@ -51,7 +51,13 @@
 private val liveLiteralCache = HashMap<String, MutableState<Any?>>()
 
 @InternalComposeApi
-val isLiveLiteralsEnabled: Boolean = false
+var isLiveLiteralsEnabled: Boolean = false
+    private set
+
+@InternalComposeApi
+fun enableLiveLiterals() {
+    isLiveLiteralsEnabled = true
+}
 
 @InternalComposeApi
 fun <T> liveLiteral(key: String, value: T): State<T> {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
index aa29075..132b707 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
@@ -88,9 +88,11 @@
     override fun clear() = writable { list = persistentListOf() }
     override fun remove(element: T) = conditionalUpdate { it.remove(element) }
     override fun removeAll(elements: Collection<T>) = conditionalUpdate { it.removeAll(elements) }
-    override fun removeAt(index: Int) = get(index).also { update { it.removeAt(index) } }
+    override fun removeAt(index: Int): T = get(index).also { update { it.removeAt(index) } }
     override fun retainAll(elements: Collection<T>) = mutate { it.retainAll(elements) }
-    override fun set(index: Int, element: T) = get(index).also { update { it.set(index, element) } }
+    override fun set(index: Int, element: T): T = get(index).also {
+        update { it.set(index, element) }
+    }
 
     fun removeRange(fromIndex: Int, toIndex: Int) {
         mutate {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/InspectionTables.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/InspectionTables.kt
index 6a1b9db..e8e35cc 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/InspectionTables.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/InspectionTables.kt
@@ -16,12 +16,12 @@
 
 package androidx.compose.runtime.tooling
 
+import androidx.compose.runtime.CompositionData
 import androidx.compose.runtime.InternalComposeApi
-import androidx.compose.runtime.SlotTable
 import androidx.compose.runtime.staticAmbientOf
 
 /**
  * A set of slot tables that where produced when in inspection mode.
  */
 @InternalComposeApi
-val InspectionTables = staticAmbientOf<MutableSet<SlotTable>?> { null }
+val InspectionTables = staticAmbientOf<MutableSet<CompositionData>?> { null }
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionDataTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionDataTests.kt
new file mode 100644
index 0000000..20ee054
--- /dev/null
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionDataTests.kt
@@ -0,0 +1,225 @@
+/*
+ * 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.runtime
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+@OptIn(InternalComposeApi::class)
+class CompositionDataTests {
+
+    @Test
+    fun canGetCompositionDataFromSlotTable() {
+        val slots = SlotTable()
+        val compositionData = slots as CompositionData
+        assertTrue(compositionData.compositionGroups.toList().isEmpty())
+    }
+
+    @Test
+    fun canIterateASlotTable() {
+        val slots = SlotTable().also {
+            it.write { writer ->
+                writer.insert {
+                    writer.group(1) {
+                        for (i in 1..5) {
+                            writer.group(i * 10) {
+                                for (j in 1..i) {
+                                    writer.update(i * 100 + j)
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        slots.verifyWellFormed()
+
+        val list = mutableListOf<Int>()
+        fun iterate(compositionData: CompositionData) {
+            for (group in compositionData.compositionGroups) {
+                list.add(group.key as Int)
+                for (data in group.data) {
+                    list.add(data as Int)
+                }
+                iterate(group)
+            }
+        }
+
+        iterate(slots)
+
+        assertEquals(
+            listOf(
+                1, 10, 101,
+                20, 201, 202,
+                30, 301, 302, 303,
+                40, 401, 402, 403, 404,
+                50, 501, 502, 503, 504, 505
+            ),
+            list
+        )
+    }
+
+    @Test
+    fun canFindNodes() {
+        val data = List(26) { 'A' + it }
+        val slots = SlotTable().also {
+            it.write { writer ->
+                writer.insert {
+                    writer.group(0) {
+                        fun emit(a: List<Char>) {
+                            if (a.isNotEmpty()) {
+                                writer.group(1) {
+                                    val mid = (a.size - 1) / 2 + 1
+                                    writer.nodeGroup(10, a[0])
+                                    if (mid > 1)
+                                        emit(a.subList(1, mid))
+                                    if (mid < a.size)
+                                        emit(a.subList(mid, a.size))
+                                }
+                            }
+                        }
+
+                        emit(data)
+                    }
+                }
+            }
+        }
+
+        val collected = mutableListOf<Char>()
+
+        fun collect(data: CompositionData) {
+            for (group in data.compositionGroups) {
+                if (group.node != null) {
+                    collected.add(group.node as Char)
+                }
+                collect(group)
+            }
+        }
+
+        collect(slots)
+
+        assertEquals(data, collected)
+    }
+
+    @Test
+    fun canFindSourceInfo() {
+        val slots = SlotTable().also {
+            var data = 0
+            it.write { writer ->
+                writer.insert {
+                    writer.group(0) {
+                        fun emit(depth: Int) {
+                            if (depth == 0) {
+                                writer.startData(100, aux = "$data")
+                                data++
+                                writer.endGroup()
+                            } else {
+                                if (depth == 2) {
+                                    writer.startData(depth * 1000, aux = "$data")
+                                    data++
+                                } else writer.startGroup(depth)
+                                emit(depth - 1)
+                                emit(depth - 1)
+                                writer.endGroup()
+                            }
+                        }
+                        emit(5)
+                    }
+                }
+            }
+        }
+
+        val collected = mutableListOf<String>()
+
+        fun collect(data: CompositionData) {
+            for (group in data.compositionGroups) {
+                val sourceInfo = group.sourceInfo
+                if (sourceInfo != null) {
+                    collected.add(sourceInfo)
+                }
+                collect(group)
+            }
+        }
+
+        collect(slots)
+
+        assertEquals(List(40) { "$it" }, collected)
+    }
+
+    @Test(expected = ConcurrentModificationException::class)
+    fun writeDuringIterationCausesException() {
+        val slots = SlotTable().also {
+            it.write { writer ->
+                writer.insert {
+                    writer.group(0) {
+                        repeat(10) { index ->
+                            writer.group(100 + index) { }
+                        }
+                    }
+                }
+            }
+        }
+
+        fun insertAGroup() {
+            slots.write { writer ->
+                writer.group {
+                    repeat(3) { writer.group { } }
+                    writer.insert {
+                        writer.group(200) { }
+                    }
+                    writer.skipToGroupEnd()
+                }
+            }
+        }
+
+        val groups = slots.compositionGroups.iterator()
+        insertAGroup()
+
+        // Expect this to cause an exception
+        groups.next()
+    }
+
+    @Test(expected = ConcurrentModificationException::class)
+    fun iterationDuringWriteCausesException() {
+        val slots = SlotTable().also {
+            it.write { writer ->
+                writer.insert {
+                    writer.group(0) {
+                        repeat(10) { index ->
+                            writer.group(100 + index) { }
+                        }
+                    }
+                }
+            }
+        }
+
+        slots.write { writer ->
+            writer.group {
+                repeat(3) { writer.group { } }
+                writer.insert {
+                    writer.group(200) { }
+                }
+                writer.skipToGroupEnd()
+
+                // Expect this to throw an exception
+                slots.compositionGroups.first()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt
index 1374742b..095ec84 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt
@@ -17,41 +17,41 @@
 @file:OptIn(ExperimentalComposeApi::class, InternalComposeApi::class)
 package androidx.compose.runtime
 
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
 import androidx.compose.runtime.mock.Contact
 import androidx.compose.runtime.mock.ContactModel
-import androidx.compose.runtime.mock.MockComposeScope
-import androidx.compose.runtime.mock.MockViewListValidator
 import androidx.compose.runtime.mock.MockViewValidator
 import androidx.compose.runtime.mock.Point
 import androidx.compose.runtime.mock.Report
-import androidx.compose.runtime.mock.View
+import androidx.compose.runtime.mock.TestMonotonicFrameClock
 import androidx.compose.runtime.mock.ViewApplier
 import androidx.compose.runtime.mock.contact
-import androidx.compose.runtime.mock.edit
-import androidx.compose.runtime.mock.linear
-import androidx.compose.runtime.mock.memoize
-import androidx.compose.runtime.mock.points
-import androidx.compose.runtime.mock.repeat
-import androidx.compose.runtime.mock.reportsReport
-import androidx.compose.runtime.mock.reportsTo
-import androidx.compose.runtime.mock.selectContact
+import androidx.compose.runtime.mock.Edit
+import androidx.compose.runtime.mock.Linear
+import androidx.compose.runtime.mock.Points
+import androidx.compose.runtime.mock.Repeated
+import androidx.compose.runtime.mock.ReportsReport
+import androidx.compose.runtime.mock.ReportsTo
+import androidx.compose.runtime.mock.SelectContact
+import androidx.compose.runtime.mock.compositionTest
 import androidx.compose.runtime.mock.skip
-import androidx.compose.runtime.mock.text
+import androidx.compose.runtime.mock.Text
+import androidx.compose.runtime.mock.expectChanges
+import androidx.compose.runtime.mock.expectNoChanges
+import androidx.compose.runtime.mock.validate
 import androidx.compose.runtime.snapshots.Snapshot
-import androidx.compose.runtime.snapshots.takeMutableSnapshot
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.test.runBlockingTest
 import kotlin.test.AfterTest
 import kotlin.test.Test
 import kotlin.test.assertEquals
-import kotlin.test.assertFalse
 import kotlin.test.assertNotEquals
 import kotlin.test.assertTrue
 
-@Composable fun Container(content: @Composable () -> Unit) = content()
+@Composable
+fun Container(content: @Composable () -> Unit) = content()
 
 @Stable
 @OptIn(ExperimentalComposeApi::class, InternalComposeApi::class)
@@ -64,21 +64,21 @@
     }
 
     @Test
-    fun testComposeAModel() {
+    fun testComposeAModel() = compositionTest {
         val model = testModel()
-        val result = compose {
-            selectContact(model)
+        compose {
+            SelectContact(model)
         }
 
-        result.validate {
-            linear {
-                linear {
-                    text("Filter:")
-                    edit("")
+        validate {
+            Linear {
+                Linear {
+                    Text("Filter:")
+                    Edit("")
                 }
-                linear {
-                    text(value = "Contacts:")
-                    linear {
+                Linear {
+                    Text(value = "Contacts:")
+                    Linear {
                         contact(bob)
                         contact(jon)
                         contact(steve)
@@ -89,35 +89,36 @@
     }
 
     @Test
-    fun testRecomposeWithoutChanges() {
+    fun testRecomposeWithoutChanges() = compositionTest {
         val model = testModel()
-        val result = compose {
-            selectContact(model)
+        compose {
+            SelectContact(model)
         }
 
-        result.expectNoChanges()
+        expectNoChanges()
 
-        result.validate {
-            selectContact(model)
+        validate {
+            SelectContact(model)
         }
     }
 
     @Test
-    fun testInsertAContact() {
+    fun testInsertAContact() = compositionTest {
         val model =
             testModel(mutableListOf(bob, jon))
         var changed = {}
-        val result = compose {
+
+        compose {
             changed = invalidate
-            selectContact(model)
+            SelectContact(model)
         }
 
-        result.validate {
-            linear {
+        validate {
+            Linear {
                 skip()
-                linear {
+                Linear {
                     skip()
-                    linear {
+                    Linear {
                         contact(bob)
                         contact(jon)
                     }
@@ -127,14 +128,14 @@
 
         model.add(steve, after = bob)
         changed()
-        result.expectChanges()
+        expectChanges()
 
-        result.validate {
-            linear {
+        validate {
+            Linear {
                 skip()
-                linear {
+                Linear {
                     skip()
-                    linear {
+                    Linear {
                         contact(bob)
                         contact(steve)
                         contact(jon)
@@ -145,7 +146,7 @@
     }
 
     @Test
-    fun testMoveAContact() {
+    fun testMoveAContact() = compositionTest {
         val model = testModel(
             mutableListOf(
                 bob,
@@ -154,21 +155,22 @@
             )
         )
         var changed = {}
-        val result = compose {
+
+        compose {
             changed = invalidate
-            selectContact(model)
+            SelectContact(model)
         }
 
         model.move(steve, after = jon)
         changed()
-        result.expectChanges()
+        expectChanges()
 
-        result.validate {
-            linear {
+        validate {
+            Linear {
                 skip()
-                linear {
+                Linear {
                     skip()
-                    linear {
+                    Linear {
                         contact(bob)
                         contact(jon)
                         contact(steve)
@@ -179,7 +181,7 @@
     }
 
     @Test
-    fun testChangeTheFilter() {
+    fun testChangeTheFilter() = compositionTest {
         val model = testModel(
             mutableListOf(
                 bob,
@@ -187,22 +189,23 @@
                 jon
             )
         )
-        var changed: (() -> Unit)? = null
-        val result = compose {
+        var changed = {}
+
+        compose {
             changed = invalidate
-            selectContact(model)
+            SelectContact(model)
         }
 
         model.filter = "Jon"
-        changed!!()
-        result.expectChanges()
+        changed()
+        expectChanges()
 
-        result.validate {
-            linear {
+        validate {
+            Linear {
                 skip()
-                linear {
+                Linear {
                     skip()
-                    linear {
+                    Linear {
                         contact(jon)
                     }
                 }
@@ -211,33 +214,33 @@
     }
 
     @Test
-    fun testComposeCompositionWithMultipleRoots() {
+    fun testComposeCompositionWithMultipleRoots() = compositionTest {
         val reports = listOf(
             jim_reports_to_sally,
             rob_reports_to_alice,
             clark_reports_to_lois
         )
 
-        val result = compose {
-            reportsReport(reports)
+        compose {
+            ReportsReport(reports)
         }
 
-        result.validate {
-            reportsReport(reports)
+        validate {
+            ReportsReport(reports)
         }
     }
 
     @Test
-    fun testMoveCompositionWithMultipleRoots() {
+    fun testMoveCompositionWithMultipleRoots() = compositionTest {
         var reports = listOf(
             jim_reports_to_sally,
             rob_reports_to_alice,
             clark_reports_to_lois
         )
-        var changed: (() -> Unit)? = null
-        val result = compose {
+        var changed = { }
+        compose {
             changed = invalidate
-            reportsReport(reports)
+            ReportsReport(reports)
         }
 
         reports = listOf(
@@ -245,134 +248,146 @@
             clark_reports_to_lois,
             rob_reports_to_alice
         )
-        changed!!()
-        result.expectChanges()
+        changed()
+        expectChanges()
 
-        result.validate {
-            reportsReport(reports)
+        validate {
+            ReportsReport(reports)
         }
     }
 
     @Test
-    fun testReplace() {
+    fun testReplace() = compositionTest {
         var includeA = true
-        var changed: (() -> Unit)? = null
-        @Composable fun MockComposeScope.composition() {
+        var changed = { }
+
+        @Composable
+        fun Composition() {
             changed = invalidate
-            text("Before")
+            Text("Before")
             if (includeA) {
-                linear {
-                    text("A")
+                Linear {
+                    Text("A")
                 }
             } else {
-                edit("B")
+                Edit("B")
             }
-            text("After")
+            Text("After")
         }
-        fun MockViewValidator.composition() {
-            text("Before")
+
+        fun MockViewValidator.Composition() {
+            Text("Before")
             if (includeA) {
-                linear {
-                    text("A")
+                Linear {
+                    Text("A")
                 }
             } else {
-                edit("B")
+                Edit("B")
             }
-            text("After")
+            Text("After")
         }
-        val result = compose {
-            composition()
+
+        compose {
+            Composition()
         }
-        result.validate {
-            composition()
+
+        validate {
+            this.Composition()
         }
+
         includeA = false
-        changed!!()
-        result.expectChanges()
-        result.validate {
-            composition()
+        changed()
+        expectChanges()
+        validate {
+            this.Composition()
         }
         includeA = true
-        changed!!()
-        result.expectChanges()
-        result.validate {
-            composition()
+        changed()
+        expectChanges()
+        validate {
+            this.Composition()
         }
+        changed()
+        expectNoChanges()
     }
 
     @Test
-    fun testInsertWithMultipleRoots() {
+    fun testInsertWithMultipleRoots() = compositionTest {
         var chars = listOf('a', 'b', 'c')
-        var changed: (() -> Unit)? = null
+        var changed = { }
 
-        @Composable fun MockComposeScope.textOf(c: Char) {
-            text(c.toString())
+        @Composable
+        fun TextOf(c: Char) {
+            Text(c.toString())
         }
 
-        fun MockViewValidator.textOf(c: Char) {
-            text(c.toString())
+        fun MockViewValidator.TextOf(c: Char) {
+            Text(c.toString())
         }
 
-        @Composable fun MockComposeScope.chars(chars: Iterable<Char>) {
-            repeat(of = chars) { c -> textOf(c) }
+        @Composable
+        fun Chars(chars: Iterable<Char>) {
+            Repeated(of = chars) { c -> TextOf(c) }
         }
 
-        fun MockViewValidator.validatechars(chars: Iterable<Char>) {
-            repeat(of = chars) { c -> textOf(c) }
+        fun MockViewValidator.validateChars(chars: Iterable<Char>) {
+            Repeated(of = chars) { c -> this.TextOf(c) }
         }
 
-        val result = compose {
+        compose {
             changed = invalidate
-            chars(chars)
-            chars(chars)
-            chars(chars)
+            Chars(chars)
+            Chars(chars)
+            Chars(chars)
         }
 
-        result.validate {
-            validatechars(chars)
-            validatechars(chars)
-            validatechars(chars)
+        validate {
+            validateChars(chars)
+            validateChars(chars)
+            validateChars(chars)
         }
 
         chars = listOf('a', 'b', 'x', 'c')
-        changed!!()
-        result.expectChanges()
+        changed()
+        expectChanges()
 
-        result.validate {
-            validatechars(chars)
-            validatechars(chars)
-            validatechars(chars)
+        validate {
+            validateChars(chars)
+            validateChars(chars)
+            validateChars(chars)
         }
     }
 
     @Test
-    fun testSimpleMemoize() {
+    fun testSimpleSkipping() = compositionTest {
         val points = listOf(Point(1, 2), Point(2, 3))
-        val result = compose {
-            points(points)
+        var changed = {}
+        compose {
+            changed = invalidate
+            Points(points)
         }
 
-        result.validate { points(points) }
+        validate { Points(points) }
 
-        val changes = result.recompose()
-        assertFalse(changes)
+        changed()
+        expectNoChanges()
     }
 
     @Test
-    fun testMovingMemoization() {
+    fun testMovingMemoization() = compositionTest {
         var points = listOf(
             Point(1, 2),
             Point(2, 3),
             Point(4, 5),
             Point(6, 7)
         )
-        var changed: (() -> Unit)? = null
-        val result = compose {
+        var changed = { }
+        compose {
             changed = invalidate
-            points(points)
+            Points(points)
         }
 
-        result.validate { points(points) }
+        validate { Points(points) }
 
         points = listOf(
             Point(1, 2),
@@ -380,27 +395,29 @@
             Point(2, 3),
             Point(6, 7)
         )
-        changed!!()
-        result.expectChanges()
+        changed()
+        expectChanges()
 
-        result.validate { points(points) }
+        validate { Points(points) }
     }
 
     @Test
-    fun testComponent() {
-        @Composable fun MockComposeScope.Reporter(report: Report? = null) {
+    fun testComponent() = compositionTest {
+        @Composable
+        fun Reporter(report: Report? = null) {
             if (report != null) {
-                text(report.from)
-                text("reports to")
-                text(report.to)
+                Text(report.from)
+                Text("reports to")
+                Text(report.to)
             } else {
-                text("no report to report")
+                Text("no report to report")
             }
         }
 
-        @Composable fun MockComposeScope.reportsReport(reports: Iterable<Report>) {
-            linear {
-                repeat(of = reports) { report ->
+        @Composable
+        fun ReportsReport(reports: Iterable<Report>) {
+            Linear {
+                Repeated(of = reports) { report ->
                     Reporter(report)
                 }
             }
@@ -411,246 +428,260 @@
             rob_reports_to_alice,
             clark_reports_to_lois
         )
-        val result = compose {
-            reportsReport(reports)
+        compose {
+            ReportsReport(reports)
         }
 
-        result.validate {
-            linear {
-                reportsTo(jim_reports_to_sally)
-                reportsTo(rob_reports_to_alice)
-                reportsTo(clark_reports_to_lois)
+        validate {
+            Linear {
+                ReportsTo(jim_reports_to_sally)
+                ReportsTo(rob_reports_to_alice)
+                ReportsTo(clark_reports_to_lois)
             }
         }
 
-        result.expectNoChanges()
+        expectNoChanges()
     }
 
     @Test
-    fun testComposeTwoAttributeComponent() {
-        @Composable fun MockComposeScope.Two2(first: Int = 1, second: Int = 2) {
-            linear {
-                text("$first $second")
+    fun testComposeTwoAttributeComponent() = compositionTest {
+        @Composable
+        fun Two2(first: Int = 1, second: Int = 2) {
+            Linear {
+                Text("$first $second")
             }
         }
 
         fun MockViewValidator.two(first: Int, second: Int) {
-            linear {
-                text("$first $second")
+            Linear {
+                Text("$first $second")
             }
         }
 
-        val result = compose {
+        compose {
             Two2(41, 42)
         }
 
-        result.validate {
+        validate {
             two(41, 42)
         }
     }
 
     @Test
-    fun testComposeThreeAttributeComponent() {
-        @Composable fun MockComposeScope.Three3(first: Int = 1, second: Int = 2, third: Int = 3) {
-            linear {
-                text("$first $second $third")
+    fun testComposeThreeAttributeComponent() = compositionTest {
+        @Composable
+        fun Three3(first: Int = 1, second: Int = 2, third: Int = 3) {
+            Linear {
+                Text("$first $second $third")
             }
         }
 
-        fun MockViewValidator.three(first: Int, second: Int, third: Int) {
-            linear {
-                text("$first $second $third")
+        fun MockViewValidator.Three(first: Int, second: Int, third: Int) {
+            Linear {
+                Text("$first $second $third")
             }
         }
 
-        val result = compose {
+        compose {
             Three3(41, 42, 43)
         }
 
-        result.validate {
-            three(41, 42, 43)
+        validate {
+            Three(41, 42, 43)
         }
     }
 
     @Test
-    fun testComposeFourOrMoreAttributeComponent() {
-        @Composable fun MockComposeScope.Four4(
+    fun testComposeFourOrMoreAttributeComponent() = compositionTest {
+        @Composable
+        fun Four4(
             first: Int = 1,
             second: Int = 2,
             third: Int = 3,
             fourth: Int = 4
         ) {
-            linear {
-                text("$first $second $third $fourth")
+            Linear {
+                Text("$first $second $third $fourth")
             }
         }
 
-        fun MockViewValidator.four(first: Int, second: Int, third: Int, fourth: Int) {
-            linear {
-                text("$first $second $third $fourth")
+        fun MockViewValidator.Four(first: Int, second: Int, third: Int, fourth: Int) {
+            Linear {
+                Text("$first $second $third $fourth")
             }
         }
 
-        val result = compose {
+        compose {
             Four4(41, 42, 43, 44)
         }
 
-        result.validate {
-            four(41, 42, 43, 44)
+        validate {
+            Four(41, 42, 43, 44)
         }
     }
 
     @Test
-    fun testSkippingACall() {
+    fun testSkippingACall() = compositionTest {
 
-        @Composable fun MockComposeScope.show(value: Int) {
-            linear {
-                text("$value")
+        @Composable
+        fun Show(value: Int) {
+            Linear {
+                Text("$value")
             }
-            linear {
-                text("value")
+            Linear {
+                Text("value")
             }
         }
 
-        fun MockViewValidator.show(value: Int) {
-            linear {
-                text("$value")
+        fun MockViewValidator.Show(value: Int) {
+            Linear {
+                Text("$value")
             }
-            linear {
-                text("value")
+            Linear {
+                Text("value")
             }
         }
 
-        @Composable fun MockComposeScope.test(showThree: Boolean) {
-            show(1)
-            show(2)
+        @Composable
+        fun Test(showThree: Boolean) {
+            Show(1)
+            Show(2)
             if (showThree) {
-                show(3)
+                Show(3)
             }
         }
 
         var showThree = false
 
-        var recomposeTest: () -> Unit = { }
+        var changed = { }
 
-        @Composable fun MockComposeScope.Test() {
-            recomposeTest = invalidate
-            test(showThree)
+        @Composable
+        fun Test() {
+            changed = invalidate
+            Test(showThree)
         }
 
-        fun MockViewValidator.test(showThree: Boolean) {
-            show(1)
-            show(2)
+        fun MockViewValidator.Test(showThree: Boolean) {
+            this.Show(1)
+            this.Show(2)
             if (showThree) {
-                show(3)
+                this.Show(3)
             }
         }
 
-        val composition: @Composable MockComposeScope.() -> Unit = {
+        fun validate() {
+            validate {
+                this.Test(showThree)
+            }
+        }
+
+        compose {
             Test()
         }
-        val validation: MockViewValidator.() -> Unit = {
-            test(showThree)
-        }
 
-        val result = compose(block = composition)
-        result.validate(validation)
+        validate()
 
         showThree = true
-        recomposeTest()
-        result.expectChanges()
-        result.validate(validation)
+        changed()
+        expectChanges()
+        validate()
     }
 
     @Test
-    fun testComponentWithVarCtorParameter() {
-        @Composable fun MockComposeScope.One(first: Int) {
-            text("$first")
+    fun testComponentWithVarConstructorParameter() = compositionTest {
+        @Composable
+        fun One(first: Int) {
+            Text("$first")
         }
 
-        fun MockViewValidator.one(first: Int) {
-            text("$first")
+        fun MockViewValidator.One(first: Int) {
+            Text("$first")
         }
 
-        @Composable fun MockComposeScope.callOne(value: Int) {
+        @Composable
+        fun CallOne(value: Int) {
             One(first = value)
         }
 
         var value = 42
-        var changed: (() -> Unit)? = null
-        val result = compose {
+        var changed = { }
+        compose {
             changed = invalidate
-            callOne(value)
+            CallOne(value)
         }
 
-        result.validate {
-            one(42)
+        validate {
+            this.One(42)
         }
 
         value = 43
-        changed!!()
-        result.expectChanges()
+        changed()
+        expectChanges()
 
-        result.validate {
-            one(43)
+        validate {
+            this.One(43)
         }
     }
 
     @Test
-    fun testComponentWithValCtorParameter() {
-        @Composable fun MockComposeScope.One(first: Int) {
-            text("$first")
+    fun testComponentWithValConstructorParameter() = compositionTest {
+        @Composable
+        fun One(first: Int) {
+            Text("$first")
         }
 
-        fun MockViewValidator.one(first: Int) {
-            text("$first")
+        fun MockViewValidator.One(first: Int) {
+            Text("$first")
         }
 
-        @Composable fun MockComposeScope.callOne(value: Int) {
+        @Composable
+        fun CallOne(value: Int) {
             One(first = value)
         }
 
         var value = 42
-        var changed: (() -> Unit)? = null
-        val result = compose {
+        var changed = { }
+        compose {
             changed = invalidate
-            callOne(value)
+            CallOne(value)
         }
 
-        result.validate {
-            one(42)
+        validate {
+            this.One(42)
         }
 
         value = 43
-        changed!!()
-        result.expectChanges()
+        changed()
+        expectChanges()
 
-        result.validate {
-            one(43)
+        validate {
+            this.One(43)
         }
 
-        changed!!()
-        result.expectNoChanges()
+        changed()
+        expectNoChanges()
     }
 
     @Test
-    fun testComposePartOfTree() {
-        var recomposeLois: (() -> Unit)? = null
+    fun testComposePartOfTree() = compositionTest {
+        var recomposeLois = { }
 
-        @Composable fun MockComposeScope.Reporter(report: Report? = null) {
+        @Composable
+        fun Reporter(report: Report? = null) {
             if (report != null) {
                 if (report.from == "Lois" || report.to == "Lois") recomposeLois = invalidate
-                text(report.from)
-                text("reports to")
-                text(report.to)
+                Text(report.from)
+                Text("reports to")
+                Text(report.to)
             } else {
-                text("no report to report")
+                Text("no report to report")
             }
         }
 
-        @Composable fun MockComposeScope.reportsReport(reports: Iterable<Report>) {
-            linear {
-                repeat(of = reports) { report ->
+        @Composable
+        fun ReportsReport(reports: Iterable<Report>) {
+            Linear {
+                Repeated(of = reports) { report ->
                     Reporter(report)
                 }
             }
@@ -662,61 +693,62 @@
             rob_reports_to_alice,
             clark_reports_to_lois, r
         )
-        val result = compose {
-            reportsReport(reports)
+        compose {
+            ReportsReport(reports)
         }
 
-        result.validate {
-            linear {
-                reportsTo(jim_reports_to_sally)
-                reportsTo(rob_reports_to_alice)
-                reportsTo(clark_reports_to_lois)
-                reportsTo(r)
+        validate {
+            Linear {
+                ReportsTo(jim_reports_to_sally)
+                ReportsTo(rob_reports_to_alice)
+                ReportsTo(clark_reports_to_lois)
+                ReportsTo(r)
             }
         }
 
-        result.expectNoChanges()
+        expectNoChanges()
 
         // Demote Perry
         r.from = "Perry"
         r.to = "Lois"
 
         // Compose only the Lois report
-        recomposeLois?.let { it() }
+        recomposeLois()
 
-        result.expectChanges()
+        expectChanges()
 
-        result.validate {
-            linear {
-                reportsTo(jim_reports_to_sally)
-                reportsTo(rob_reports_to_alice)
-                reportsTo(clark_reports_to_lois)
-                reportsTo(r)
+        validate {
+            Linear {
+                ReportsTo(jim_reports_to_sally)
+                ReportsTo(rob_reports_to_alice)
+                ReportsTo(clark_reports_to_lois)
+                ReportsTo(r)
             }
         }
     }
 
     @Test
-    fun testRecomposeWithReplace() {
-        var recomposeLois: (() -> Unit)? = null
+    fun testRecomposeWithReplace() = compositionTest {
+        var recomposeLois = { }
         var key = 0
 
-        @Composable fun MockComposeScope.Reporter(report: Report? = null) {
+        @Composable
+        fun Reporter(report: Report? = null) {
             if (report != null) {
                 if (report.from == "Lois" || report.to == "Lois") recomposeLois = invalidate
                 key(key) {
-                    text(report.from)
-                    text("reports to")
-                    text(report.to)
+                    Text(report.from)
+                    Text("reports to")
+                    Text(report.to)
                 }
             } else {
-                text("no report to report")
+                Text("no report to report")
             }
         }
 
-        @Composable fun MockComposeScope.reportsReport(reports: Iterable<Report>) {
-            linear {
-                repeat(of = reports) { report ->
+        @Composable fun ReportsReport(reports: Iterable<Report>) {
+            Linear {
+                Repeated(of = reports) { report ->
                     Reporter(report)
                 }
             }
@@ -728,20 +760,20 @@
             rob_reports_to_alice,
             clark_reports_to_lois, r
         )
-        val result = compose {
-            reportsReport(reports)
+        compose {
+            ReportsReport(reports)
         }
 
-        result.validate {
-            linear {
-                reportsTo(jim_reports_to_sally)
-                reportsTo(rob_reports_to_alice)
-                reportsTo(clark_reports_to_lois)
-                reportsTo(r)
+        validate {
+            Linear {
+                ReportsTo(jim_reports_to_sally)
+                ReportsTo(rob_reports_to_alice)
+                ReportsTo(clark_reports_to_lois)
+                ReportsTo(r)
             }
         }
 
-        result.expectNoChanges()
+        expectNoChanges()
 
         // Demote Perry
         r.from = "Perry"
@@ -751,45 +783,47 @@
         key = 2
 
         // Compose only the Lois report
-        recomposeLois?.let { it() }
+        recomposeLois()
 
-        result.expectChanges()
+        expectChanges()
 
-        result.validate {
-            linear {
-                reportsTo(jim_reports_to_sally)
-                reportsTo(rob_reports_to_alice)
-                reportsTo(clark_reports_to_lois)
-                reportsTo(r)
+        validate {
+            Linear {
+                ReportsTo(jim_reports_to_sally)
+                ReportsTo(rob_reports_to_alice)
+                ReportsTo(clark_reports_to_lois)
+                ReportsTo(r)
             }
         }
     }
 
     @Test
-    fun testInvalidationAfterRemoval() {
-        var recomposeLois = {}
+    fun testInvalidationAfterRemoval() = compositionTest {
+        var recomposeLois = { }
         val key = 0
 
-        @Composable fun MockComposeScope.Reporter(report: Report? = null) {
+        @Composable
+        fun Reporter(report: Report? = null) {
             if (report != null) {
                 val callback = invalidate
                 if (report.from == "Lois" || report.to == "Lois") recomposeLois = callback
                 key(key) {
-                    text(report.from)
-                    text("reports to")
-                    text(report.to)
+                    Text(report.from)
+                    Text("reports to")
+                    Text(report.to)
                 }
             } else {
-                text("no report to report")
+                Text("no report to report")
             }
         }
 
-        @Composable fun MockComposeScope.reportsReport(
+        @Composable
+        fun ReportsReport(
             reports: Iterable<Report>,
             include: (report: Report) -> Boolean
         ) {
-            linear {
-                repeat(of = reports) { report ->
+            Linear {
+                Repeated(of = reports) { report ->
                     if (include(report)) {
                         Reporter(report)
                     }
@@ -808,40 +842,40 @@
         val notLois: (report: Report) -> Boolean = { it.from != "Lois" && it.to != "Lois" }
 
         var filter = all
-        var changed = {}
-        val result = compose {
+        var changed = { }
+        compose {
             changed = invalidate
-            reportsReport(reports, filter)
+            ReportsReport(reports, filter)
         }
 
-        result.validate {
-            linear {
-                reportsTo(jim_reports_to_sally)
-                reportsTo(rob_reports_to_alice)
-                reportsTo(clark_reports_to_lois)
-                reportsTo(r)
+        validate {
+            Linear {
+                ReportsTo(jim_reports_to_sally)
+                ReportsTo(rob_reports_to_alice)
+                ReportsTo(clark_reports_to_lois)
+                ReportsTo(r)
             }
         }
 
         filter = notLois
         changed()
-        result.expectChanges()
+        expectChanges()
 
-        result.validate {
-            linear {
-                reportsTo(jim_reports_to_sally)
-                reportsTo(rob_reports_to_alice)
+        validate {
+            Linear {
+                ReportsTo(jim_reports_to_sally)
+                ReportsTo(rob_reports_to_alice)
             }
         }
 
         // Invalidate Lois which is now removed.
         recomposeLois()
-        result.expectNoChanges()
+        expectNoChanges()
 
-        result.validate {
-            linear {
-                reportsTo(jim_reports_to_sally)
-                reportsTo(rob_reports_to_alice)
+        validate {
+            Linear {
+                ReportsTo(jim_reports_to_sally)
+                ReportsTo(rob_reports_to_alice)
             }
         }
     }
@@ -849,9 +883,9 @@
     // remember()
 
     @Test
-    fun testSimpleRemember() {
+    fun testSimpleRemember() = compositionTest {
         var count = 0
-        var changed: (() -> Unit)? = null
+        var changed = { }
 
         class Wrapper(val value: Int) {
             init {
@@ -859,33 +893,34 @@
             }
         }
 
-        @Composable fun MockComposeScope.test(value: Int) {
+        @Composable
+        fun Test(value: Int) {
             changed = invalidate
             val w = remember { Wrapper(value) }
-            text("value = ${w.value}")
+            Text("value = ${w.value}")
         }
 
-        fun MockViewValidator.test(value: Int) {
-            text("value = $value")
+        fun MockViewValidator.Test(value: Int) {
+            Text("value = $value")
         }
 
-        val result = compose {
-            test(1)
+        compose {
+            Test(1)
         }
 
-        result.validate { test(1) }
+        validate { this.Test(1) }
 
         assertEquals(1, count)
 
-        changed!!()
-        result.expectNoChanges()
+        changed()
+        expectNoChanges()
 
         // Expect the previous instance to be remembered
         assertEquals(1, count)
     }
 
     @Test
-    fun testRememberOneParameter() {
+    fun testRememberOneParameter() = compositionTest {
         var count = 0
 
         class Wrapper(val value: Int) {
@@ -894,40 +929,41 @@
             }
         }
 
-        @Composable fun MockComposeScope.test(value: Int) {
+        @Composable
+        fun Test(value: Int) {
             val w = remember(value) { Wrapper(value) }
-            text("value = ${w.value}")
+            Text("value = ${w.value}")
         }
 
-        fun MockViewValidator.test(value: Int) {
-            text("value = $value")
+        fun MockViewValidator.Test(value: Int) {
+            Text("value = $value")
         }
 
         var value = 1
-        var changed: (() -> Unit)? = null
-        val result = compose {
+        var changed = { }
+        compose {
             changed = invalidate
-            test(value)
+            Test(value)
         }
 
-        result.validate { test(1) }
+        validate { this.Test(1) }
 
         value = 2
-        changed!!()
-        result.expectChanges()
+        changed()
+        expectChanges()
 
-        result.validate { test(2) }
+        validate { this.Test(2) }
 
-        changed!!()
-        result.expectNoChanges()
+        changed()
+        expectNoChanges()
 
-        result.validate { test(2) }
+        validate { this.Test(2) }
 
         assertEquals(2, count)
     }
 
     @Test
-    fun testRememberTwoParameters() {
+    fun testRememberTwoParameters() = compositionTest {
         var count = 0
 
         class Wrapper(val a: Int, val b: Int) {
@@ -936,43 +972,44 @@
             }
         }
 
-        @Composable fun MockComposeScope.test(a: Int, b: Int) {
+        @Composable
+        fun Test(a: Int, b: Int) {
             val w = remember(a, b) { Wrapper(a, b) }
-            text("a = ${w.a} b = ${w.b}")
+            Text("a = ${w.a} b = ${w.b}")
         }
 
-        fun MockViewValidator.test(a: Int, b: Int) {
-            text("a = $a b = $b")
+        fun MockViewValidator.Test(a: Int, b: Int) {
+            Text("a = $a b = $b")
         }
 
         var p1 = 1
         var p2 = 2
-        var changed: (() -> Unit)? = null
+        var changed = { }
 
-        val result = compose {
+        compose {
             changed = invalidate
-            test(p1, p2)
+            Test(p1, p2)
         }
 
-        result.validate { test(1, 2) }
+        validate { this.Test(1, 2) }
 
         p1 = 2
         p2 = 3
-        changed!!()
-        result.expectChanges()
+        changed()
+        expectChanges()
 
-        result.validate { test(2, 3) }
+        validate { this.Test(2, 3) }
 
-        changed!!()
-        result.expectNoChanges()
+        changed()
+        expectNoChanges()
 
-        result.validate { test(2, 3) }
+        validate { this.Test(2, 3) }
 
         assertEquals(2, count)
     }
 
     @Test
-    fun testRememberThreeParameters() {
+    fun testRememberThreeParameters() = compositionTest {
         var count = 0
 
         class Wrapper(val a: Int, val b: Int, val c: Int) {
@@ -981,40 +1018,41 @@
             }
         }
 
-        @Composable fun MockComposeScope.test(a: Int, b: Int, c: Int) {
+        @Composable
+        fun Test(a: Int, b: Int, c: Int) {
             val w = remember(a, b, c) { Wrapper(a, b, c) }
-            text("a = ${w.a} b = ${w.b} c = ${w.c}")
+            Text("a = ${w.a} b = ${w.b} c = ${w.c}")
         }
 
-        fun MockViewValidator.test(a: Int, b: Int, c: Int) {
-            text("a = $a b = $b c = $c")
+        fun MockViewValidator.Test(a: Int, b: Int, c: Int) {
+            Text("a = $a b = $b c = $c")
         }
 
         var p3 = 3
-        var changed: (() -> Unit)? = null
-        val result = compose {
+        var changed = { }
+        compose {
             changed = invalidate
-            test(1, 2, p3)
+            Test(1, 2, p3)
         }
 
-        result.validate { test(1, 2, 3) }
+        validate { this.Test(1, 2, 3) }
 
         p3 = 4
-        changed!!()
-        result.expectChanges()
+        changed()
+        expectChanges()
 
-        result.validate { test(1, 2, 4) }
+        validate { this.Test(1, 2, 4) }
 
-        changed!!()
-        result.expectNoChanges()
+        changed()
+        expectNoChanges()
 
-        result.validate { test(1, 2, 4) }
+        validate { this.Test(1, 2, 4) }
 
         assertEquals(2, count)
     }
 
     @Test
-    fun testRememberFourParameters() {
+    fun testRememberFourParameters() = compositionTest {
         var count = 0
 
         class Wrapper(val a: Int, val b: Int, val c: Int, val d: Int) {
@@ -1023,43 +1061,44 @@
             }
         }
 
-        @Composable fun MockComposeScope.test(a: Int, b: Int, c: Int, d: Int) {
+        @Composable
+        fun Test(a: Int, b: Int, c: Int, d: Int) {
             val w = remember(a, b, c, d) { Wrapper(a, b, c, d) }
-            text("a = ${w.a} b = ${w.b} c = ${w.c} d = ${w.d}")
+            Text("a = ${w.a} b = ${w.b} c = ${w.c} d = ${w.d}")
         }
 
-        fun MockViewValidator.test(a: Int, b: Int, c: Int, d: Int) {
-            text("a = $a b = $b c = $c d = $d")
+        fun MockViewValidator.Test(a: Int, b: Int, c: Int, d: Int) {
+            Text("a = $a b = $b c = $c d = $d")
         }
 
         var p3 = 3
         var p4 = 4
-        var changed: (() -> Unit)? = null
+        var changed = { }
 
-        val result = compose {
+        compose {
             changed = invalidate
-            test(1, 2, p3, p4)
+            Test(1, 2, p3, p4)
         }
 
-        result.validate { test(1, 2, 3, 4) }
+        validate { this.Test(1, 2, 3, 4) }
 
         p3 = 4
         p4 = 5
-        changed!!()
-        result.expectChanges()
+        changed()
+        expectChanges()
 
-        result.validate { test(1, 2, 4, 5) }
+        validate { this.Test(1, 2, 4, 5) }
 
-        changed!!()
-        result.expectNoChanges()
+        changed()
+        expectNoChanges()
 
-        result.validate { test(1, 2, 4, 5) }
+        validate { this.Test(1, 2, 4, 5) }
 
         assertEquals(2, count)
     }
 
     @Test
-    fun testRememberFiveParameters() {
+    fun testRememberFiveParameters() = compositionTest {
         var count = 0
 
         class Wrapper(val a: Int, val b: Int, val c: Int, val d: Int, val e: Int) {
@@ -1068,277 +1107,267 @@
             }
         }
 
-        @Composable fun MockComposeScope.test(a: Int, b: Int, c: Int, d: Int, e: Int) {
+        @Composable
+        fun Test(a: Int, b: Int, c: Int, d: Int, e: Int) {
             val w = remember(a, b, c, d, e) { Wrapper(a, b, c, d, e) }
-            text("a = ${w.a} b = ${w.b} c = ${w.c} d = ${w.d} e = ${w.e}")
+            Text("a = ${w.a} b = ${w.b} c = ${w.c} d = ${w.d} e = ${w.e}")
         }
 
-        fun MockViewValidator.test(a: Int, b: Int, c: Int, d: Int, e: Int) {
-            text("a = $a b = $b c = $c d = $d e = $e")
+        fun MockViewValidator.Test(a: Int, b: Int, c: Int, d: Int, e: Int) {
+            Text("a = $a b = $b c = $c d = $d e = $e")
         }
 
         var lastParameter = 5
-        var changed: (() -> Unit)? = null
-        val result = compose {
+        var changed = { }
+        compose {
             changed = invalidate
-            test(1, 2, 3, 4, lastParameter)
+            Test(1, 2, 3, 4, lastParameter)
         }
 
-        result.validate { test(1, 2, 3, 4, 5) }
+        validate { this.Test(1, 2, 3, 4, 5) }
 
         lastParameter = 6
-        changed!!()
+        changed()
 
-        result.expectChanges()
+        expectChanges()
 
-        result.validate { test(1, 2, 3, 4, 6) }
+        validate { this.Test(1, 2, 3, 4, 6) }
 
-        result.expectNoChanges()
+        expectNoChanges()
 
-        result.validate { test(1, 2, 3, 4, 6) }
+        validate { this.Test(1, 2, 3, 4, 6) }
 
         assertEquals(2, count)
     }
 
     @Test
-    fun testInsertGroupInContainer() {
-        val values = mutableListOf(0)
-        var changed: (() -> Unit)? = null
+    fun testInsertGroupInContainer() = compositionTest {
+        val values = mutableStateListOf(0)
 
-        @Composable fun MockComposeScope.composition() {
-            linear {
-                changed = invalidate
+        @Composable
+        fun Composition() {
+            Linear {
                 for (value in values) {
-                    memoize(value, value) {
-                        text("$value")
-                    }
+                    Text("$value")
                 }
             }
         }
 
-        fun MockViewValidator.composition() {
-            linear {
+        fun MockViewValidator.Composition() {
+            Linear {
                 for (value in values)
-                    text("$value")
+                    Text("$value")
             }
         }
 
-        val result = compose { composition() }
+        compose { Composition() }
 
-        result.validate { composition() }
+        validate { this.Composition() }
 
         for (i in 1..10) {
             values.add(i)
-            changed!!()
-            result.expectChanges()
-            result.validate { composition() }
+            expectChanges()
+            validate { this.Composition() }
         }
     }
 
     // b/148273328
     @Test
-    fun testInsertInGroups() {
+    fun testInsertInGroups() = compositionTest {
+        var threeVisible by mutableStateOf(false)
 
-        var threeVisible = false
-        var changed: (() -> Unit)? = null
-
-        @Composable fun MockComposeScope.composition() {
-            linear {
-                text("one")
-                text("two")
-                changed = invalidate
+        @Composable fun Composition() {
+            Linear {
+                Text("one")
+                Text("two")
                 if (threeVisible) {
-                    text("three")
-                    text("four")
+                    Text("three")
+                    Text("four")
                 }
-                linear {
-                    text("five")
+                Linear {
+                    Text("five")
                 }
             }
         }
 
-        fun MockViewValidator.composition() {
-            linear {
-                text("one")
-                text("two")
+        fun MockViewValidator.Composition() {
+            Linear {
+                Text("one")
+                Text("two")
                 if (threeVisible) {
-                    text("three")
-                    text("four")
+                    Text("three")
+                    Text("four")
                 }
-                linear {
-                    text("five")
+                Linear {
+                    Text("five")
                 }
             }
         }
 
-        val result = compose { composition() }
-        result.validate { composition() }
+        compose { Composition() }
+        validate { this.Composition() }
 
         threeVisible = true
-        changed!!()
-        result.expectChanges()
+        expectChanges()
 
-        result.validate { composition() }
+        validate { this.Composition() }
     }
 
     @Test
-    fun testStartJoin() {
-        var text = "Starting"
-        var myInvalidate: (() -> Unit)? = null
-        @Composable fun MockComposeScope.composition() {
-            linear {
-                myInvalidate = invalidate
-                text(text)
+    fun testStartJoin() = compositionTest {
+        var text by mutableStateOf("Starting")
+
+        @Composable
+        fun Composition() {
+            Linear {
+                Text(text)
             }
         }
 
-        fun MockViewValidator.composition() {
-            linear {
-                text(text)
+        fun MockViewValidator.Composition() {
+            Linear {
+                Text(text)
             }
         }
 
-        val result = compose { composition() }
+        compose { Composition() }
 
-        result.validate { composition() }
+        validate { this.Composition() }
 
         text = "Ending"
-        myInvalidate?.let { it() }
 
-        result.expectChanges()
+        expectChanges()
 
-        result.validate { composition() }
+        validate { this.Composition() }
     }
 
     @Test
-    fun testInvalidateJoin_End() {
-        var text = "Starting"
-        var includeNested = true
-        var invalidate1 = {}
-        var invalidate2 = {}
+    fun testInvalidateJoin_End() = compositionTest {
+        var text by mutableStateOf("Starting")
+        var includeNested by mutableStateOf(true)
 
-        @Composable fun MockComposeScope.composition() {
-            linear {
-                invalidate1 = invalidate
-                text(text)
+        @Composable
+        fun Composition() {
+            Linear {
+                Text(text)
                 if (includeNested) {
-                    invalidate2 = invalidate
-                    text("Nested in $text")
+                    Text("Nested in $text")
                 }
             }
         }
 
-        fun MockViewValidator.composition() {
-            linear {
-                text(text)
+        fun MockViewValidator.Composition() {
+            Linear {
+                Text(text)
                 if (includeNested) {
-                    text("Nested in $text")
+                    Text("Nested in $text")
                 }
             }
         }
 
-        val result = compose { composition() }
+        compose { Composition() }
 
-        result.validate { composition() }
+        validate { this.Composition() }
 
         text = "Ending"
         includeNested = false
-        invalidate1()
-        invalidate2()
 
-        result.expectChanges()
+        expectChanges()
 
-        result.validate { composition() }
+        validate { this.Composition() }
 
-        result.expectNoChanges()
+        expectNoChanges()
 
-        result.validate { composition() }
+        validate { this.Composition() }
     }
 
     @Test
-    fun testInvalidateJoin_Start() {
-        var text = "Starting"
-        var includeNested = true
-        var invalidate1: (() -> Unit)? = null
-        var invalidate2: (() -> Unit)? = null
+    fun testInvalidateJoin_Start() = compositionTest {
+        var text by mutableStateOf("Starting")
+        var includeNested by mutableStateOf(true)
 
-        @Composable fun MockComposeScope.composition() {
-            linear {
-                invalidate1 = invalidate
+        @Composable
+        fun Composition() {
+            Linear {
                 if (includeNested) {
-                    invalidate2 = invalidate
-                    text("Nested in $text")
+                    Text("Nested in $text")
                 }
-                text(text)
+                Text(text)
             }
         }
 
-        fun MockViewValidator.composition() {
-            linear {
+        fun MockViewValidator.Composition() {
+            Linear {
                 if (includeNested) {
-                    text("Nested in $text")
+                    Text("Nested in $text")
                 }
-                text(text)
+                Text(text)
             }
         }
 
-        val result = compose { composition() }
+        compose { Composition() }
 
-        result.validate { composition() }
+        validate { this.Composition() }
 
         text = "Ending"
         includeNested = false
-        invalidate1?.invoke()
-        invalidate2?.invoke()
 
-        result.expectChanges()
+        expectChanges()
 
-        result.validate { composition() }
+        validate { this.Composition() }
 
-        result.expectNoChanges()
+        expectNoChanges()
 
-        result.validate { composition() }
+        validate { this.Composition() }
     }
 
     // b/132638679
     @Test
-    fun testJoinInvalidate() {
+    fun testJoinInvalidate() = compositionTest {
         var texts = 5
-        var invalidateOuter: (() -> Unit)? = null
-        var invalidateInner: (() -> Unit)? = null
+        var invalidateOuter = { }
+        var invalidateInner = { }
+        var forceInvalidate = false
 
-        @Composable fun MockComposeScope.composition() {
-            linear {
+        @Composable fun Composition() {
+            Linear {
                 invalidateOuter = invalidate
                 for (i in 1..texts) {
-                    text("Some text")
+                    Text("Some text")
                 }
 
                 Container {
-                    text("Some text")
+                    Text("Some text")
 
                     // Force the invalidation to survive the compose
                     val innerInvalidate = invalidate
-                    innerInvalidate()
                     invalidateInner = innerInvalidate
+                    if (forceInvalidate) {
+                        innerInvalidate()
+                        forceInvalidate = false
+                    }
                 }
             }
         }
 
-        val result = compose { composition() }
+        compose { Composition() }
 
         texts = 4
-        invalidateOuter?.invoke()
-        invalidateInner?.invoke()
-        result.expectChanges()
+        invalidateOuter()
+        invalidateInner()
+        forceInvalidate = true
+        expectChanges()
 
         texts = 3
-        invalidateOuter?.invoke()
-        result.expectChanges()
+        invalidateOuter()
+        forceInvalidate = true
+        expectChanges()
+
+        expectNoChanges()
     }
 
     @Test
-    fun testLifecycle_Enter_Simple() {
+    fun testLifecycle_Enter_Simple() = compositionTest {
         val lifecycleObject = object : CompositionLifecycleObserver {
             var count = 0
             override fun onEnter() {
@@ -1350,37 +1379,39 @@
             }
         }
 
-        @Composable fun MockComposeScope.composition() {
-            linear {
+        var changed = { }
+
+        @Composable
+        fun Composition() {
+            Linear {
+                changed = invalidate
                 remember { lifecycleObject }
-                text("Some text")
+                Text("Some text")
             }
         }
 
-        fun MockViewValidator.composition() {
-            linear {
-                text("Some text")
+        fun MockViewValidator.Composition() {
+            Linear {
+                Text("Some text")
             }
         }
 
-        var changed: (() -> Unit)? = null
-        val result = compose {
-            changed = invalidate
-            composition()
+        compose {
+            Composition()
         }
-        result.validate { composition() }
+        validate { this.Composition() }
 
         assertEquals(1, lifecycleObject.count, "object should have been notified of an enter")
 
-        changed!!()
-        result.expectNoChanges()
-        result.validate { composition() }
+        changed()
+        expectNoChanges()
+        validate { this.Composition() }
 
         assertEquals(1, lifecycleObject.count, "Object should have only been notified once")
     }
 
     @Test
-    fun testLifecycle_Enter_SingleNotification() {
+    fun testLifecycle_Enter_SingleNotification() = compositionTest {
         val lifecycleObject = object : CompositionLifecycleObserver {
             var count = 0
             override fun onEnter() {
@@ -1392,46 +1423,46 @@
             }
         }
 
-        @Composable fun MockComposeScope.composition() {
-            linear {
+        var value by mutableStateOf(0)
+        @Composable
+        fun Composition() {
+            Linear {
                 val l = remember { lifecycleObject }
                 assertEquals(lifecycleObject, l, "Lifecycle object should be returned")
-                text("Some text")
+                Text("Some text $value")
             }
-            linear {
+            Linear {
                 val l = remember { lifecycleObject }
                 assertEquals(lifecycleObject, l, "Lifecycle object should be returned")
-                text("Some other text")
+                Text("Some other text $value")
             }
         }
 
-        fun MockViewValidator.composition() {
-            linear {
-                text("Some text")
+        fun MockViewValidator.Composition() {
+            Linear {
+                Text("Some text $value")
             }
-            linear {
-                text("Some other text")
+            Linear {
+                Text("Some other text $value")
             }
         }
 
-        var changed: (() -> Unit)? = null
-        val result = compose {
-            changed = invalidate
-            composition()
+        compose {
+            Composition()
         }
-        result.validate { composition() }
+        validate { this.Composition() }
 
         assertEquals(1, lifecycleObject.count, "object should have been notified of an enter")
 
-        changed!!()
-        result.expectNoChanges()
-        result.validate { composition() }
+        value++
+        expectChanges()
+        validate { this.Composition() }
 
         assertEquals(1, lifecycleObject.count, "Object should have only been notified once")
     }
 
     @Test
-    fun testLifecycle_Leave_Simple() {
+    fun testLifecycle_Leave_Simple() = compositionTest {
         val lifecycleObject = object : CompositionLifecycleObserver {
             var count = 0
             override fun onEnter() {
@@ -1443,54 +1474,55 @@
             }
         }
 
-        @Composable fun MockComposeScope.composition(includeLifecycleObject: Boolean) {
-            linear {
+        @Composable
+        fun Composition(includeLifecycleObject: Boolean) {
+            Linear {
                 if (includeLifecycleObject) {
-                    linear {
+                    Linear {
                         val l = remember { lifecycleObject }
                         assertEquals(lifecycleObject, l, "Lifecycle object should be returned")
-                        text("Some text")
+                        Text("Some text")
                     }
                 }
             }
         }
 
-        fun MockViewValidator.composition(includeLifecycleObject: Boolean) {
-            linear {
+        fun MockViewValidator.Composition(includeLifecycleObject: Boolean) {
+            Linear {
                 if (includeLifecycleObject) {
-                    linear {
-                        text("Some text")
+                    Linear {
+                        Text("Some text")
                     }
                 }
             }
         }
 
-        var changed: (() -> Unit)? = null
+        var changed = { }
         var value = true
-        val result = compose {
+        compose {
             changed = invalidate
-            composition(value)
+            Composition(value)
         }
-        result.validate { composition(true) }
+        validate { this.Composition(true) }
 
         assertEquals(1, lifecycleObject.count, "object should have been notified of an enter")
 
-        changed!!()
-        result.expectNoChanges()
-        result.validate { composition(true) }
+        changed()
+        expectNoChanges()
+        validate { this.Composition(true) }
 
         assertEquals(1, lifecycleObject.count, "Object should have only been notified once")
 
         value = false
-        changed!!()
-        result.expectChanges()
-        result.validate { composition(false) }
+        changed()
+        expectChanges()
+        validate { this.Composition(false) }
 
         assertEquals(0, lifecycleObject.count, "Object should have been notified of a leave")
     }
 
     @Test
-    fun testLifecycle_Leave_NoLeaveOnReenter() {
+    fun testLifecycle_Leave_NoLeaveOnReenter() = compositionTest {
         var expectedEnter = true
         var expectedLeave = true
         val lifecycleObject = object : CompositionLifecycleObserver {
@@ -1506,53 +1538,54 @@
             }
         }
 
-        @Composable fun MockComposeScope.composition(a: Boolean, b: Boolean, c: Boolean) {
-            linear {
+        @Composable
+        fun Composition(a: Boolean, b: Boolean, c: Boolean) {
+            Linear {
                 if (a) {
                     key(1) {
-                        linear {
+                        Linear {
                             val l = remember { lifecycleObject }
                             assertEquals(lifecycleObject, l, "Lifecycle object should be returned")
-                            text("a")
+                            Text("a")
                         }
                     }
                 }
                 if (b) {
                     key(2) {
-                        linear {
+                        Linear {
                             val l = remember { lifecycleObject }
                             assertEquals(lifecycleObject, l, "Lifecycle object should be returned")
-                            text("b")
+                            Text("b")
                         }
                     }
                 }
                 if (c) {
                     key(3) {
-                        linear {
+                        Linear {
                             val l = remember { lifecycleObject }
                             assertEquals(lifecycleObject, l, "Lifecycle object should be returned")
-                            text("c")
+                            Text("c")
                         }
                     }
                 }
             }
         }
 
-        fun MockViewValidator.composition(a: Boolean, b: Boolean, c: Boolean) {
-            linear {
+        fun MockViewValidator.Composition(a: Boolean, b: Boolean, c: Boolean) {
+            Linear {
                 if (a) {
-                    linear {
-                        text("a")
+                    Linear {
+                        Text("a")
                     }
                 }
                 if (b) {
-                    linear {
-                        text("b")
+                    Linear {
+                        Text("b")
                     }
                 }
                 if (c) {
-                    linear {
-                        text("c")
+                    Linear {
+                        Text("c")
                     }
                 }
             }
@@ -1564,13 +1597,13 @@
         var a = true
         var b = false
         var c = false
-        var changed: (() -> Unit)? = null
-        val result = compose {
+        var changed = { }
+        compose {
             changed = invalidate
-            composition(a = a, b = b, c = c)
+            Composition(a = a, b = b, c = c)
         }
-        result.validate {
-            composition(
+        validate {
+            this.Composition(
                 a = true,
                 b = false,
                 c = false
@@ -1585,10 +1618,10 @@
 
         expectedEnter = false
         expectedLeave = false
-        changed!!()
-        result.expectNoChanges()
-        result.validate {
-            composition(
+        changed()
+        expectNoChanges()
+        validate {
+            this.Composition(
                 a = true,
                 b = false,
                 c = false
@@ -1605,10 +1638,10 @@
         a = false
         b = true
         c = false
-        changed!!()
-        result.expectChanges()
-        result.validate {
-            composition(
+        changed()
+        expectChanges()
+        validate {
+            this.Composition(
                 a = false,
                 b = true,
                 c = false
@@ -1621,10 +1654,10 @@
         a = false
         b = false
         c = true
-        changed!!()
-        result.expectChanges()
-        result.validate {
-            composition(
+        changed()
+        expectChanges()
+        validate {
+            this.Composition(
                 a = false,
                 b = false,
                 c = true
@@ -1637,10 +1670,10 @@
         a = true
         b = false
         c = false
-        changed!!()
-        result.expectChanges()
-        result.validate {
-            composition(
+        changed()
+        expectChanges()
+        validate {
+            this.Composition(
                 a = true,
                 b = false,
                 c = false
@@ -1653,10 +1686,10 @@
         a = false
         b = false
         c = false
-        changed!!()
-        result.expectChanges()
-        result.validate {
-            composition(
+        changed()
+        expectChanges()
+        validate {
+            this.Composition(
                 a = false,
                 b = false,
                 c = false
@@ -1666,7 +1699,7 @@
     }
 
     @Test
-    fun testLifecycle_Leave_LeaveOnReplace() {
+    fun testLifecycle_Leave_LeaveOnReplace() = compositionTest {
         val lifecycleObject1 = object : CompositionLifecycleObserver {
             var count = 0
             override fun onEnter() {
@@ -1692,50 +1725,51 @@
         var lifecycleObject: Any = lifecycleObject1
         var changed = {}
 
-        @Composable fun MockComposeScope.composition(obj: Any) {
-            linear {
+        @Composable
+        fun Composition(obj: Any) {
+            Linear {
                 key(1) {
-                    linear {
+                    Linear {
                         remember(obj) { obj }
-                        text("Some value")
+                        Text("Some value")
                     }
                 }
             }
         }
 
-        fun MockViewValidator.composition() {
-            linear {
-                linear {
-                    text("Some value")
+        fun MockViewValidator.Composition() {
+            Linear {
+                Linear {
+                    Text("Some value")
                 }
             }
         }
 
-        val result = compose {
+        compose {
             changed = invalidate
-            composition(obj = lifecycleObject)
+            Composition(obj = lifecycleObject)
         }
-        result.validate { composition() }
+        validate { this.Composition() }
         assertEquals(1, lifecycleObject1.count, "first object should enter")
         assertEquals(0, lifecycleObject2.count, "second object should not have entered")
 
         lifecycleObject = lifecycleObject2
         changed()
-        result.expectChanges()
-        result.validate { composition() }
+        expectChanges()
+        validate { Composition() }
         assertEquals(0, lifecycleObject1.count, "first object should have left")
         assertEquals(1, lifecycleObject2.count, "second object should have entered")
 
         lifecycleObject = object {}
         changed()
-        result.expectChanges()
-        result.validate { composition() }
+        expectChanges()
+        validate { Composition() }
         assertEquals(0, lifecycleObject1.count, "first object should have left")
         assertEquals(0, lifecycleObject2.count, "second object should have left")
     }
 
     @Test
-    fun testLifecycle_EnterLeaveOrder() {
+    fun testLifecycle_EnterLeaveOrder() = compositionTest {
         var order = 0
         val objects = mutableListOf<Any>()
         val newLifecycleObject = { name: String ->
@@ -1762,10 +1796,11 @@
             }.also { objects.add(it) }
         }
 
-        @Composable fun MockComposeScope.lifecycleUser(name: String) {
-            linear {
+        @Composable
+        fun LifecycleUser(name: String) {
+            Linear {
                 remember(name) { newLifecycleObject(name) }
-                text(value = name)
+                Text(value = name)
             }
         }
 
@@ -1785,41 +1820,41 @@
         Should leave as: J, I, H, G, F, E, D, C, B, A
         */
 
-        @Composable fun MockComposeScope.tree() {
-            linear {
-                lifecycleUser("A")
-                linear {
-                    lifecycleUser("B")
-                    linear {
-                        lifecycleUser("C")
-                        lifecycleUser("D")
+        @Composable
+        fun Tree() {
+            Linear {
+                LifecycleUser("A")
+                Linear {
+                    LifecycleUser("B")
+                    Linear {
+                        LifecycleUser("C")
+                        LifecycleUser("D")
                     }
-                    lifecycleUser("E")
-                    lifecycleUser("F")
-                    linear {
-                        lifecycleUser("G")
-                        lifecycleUser("H")
-                        linear {
-                            lifecycleUser("I")
+                    LifecycleUser("E")
+                    LifecycleUser("F")
+                    Linear {
+                        LifecycleUser("G")
+                        LifecycleUser("H")
+                        Linear {
+                            LifecycleUser("I")
                         }
                     }
-                    lifecycleUser("J")
+                    LifecycleUser("J")
                 }
             }
         }
 
-        @Composable fun MockComposeScope.composition(includeTree: Boolean) {
-            linear {
-                if (includeTree) tree()
+        @Composable
+        fun Composition(includeTree: Boolean) {
+            Linear {
+                if (includeTree) Tree()
             }
         }
 
-        var value = true
-        var changed: (() -> Unit)? = null
+        var value by mutableStateOf(true)
 
-        val result = compose {
-            changed = invalidate
-            composition(value)
+        compose {
+            Composition(value)
         }
 
         assertTrue(
@@ -1828,8 +1863,7 @@
         )
 
         value = false
-        changed!!()
-        result.expectChanges()
+        expectChanges()
 
         assertTrue(
             objects.mapNotNull { it as? Counted }.map { it.count == 0 }.all { it },
@@ -1854,7 +1888,7 @@
     }
 
     @Test
-    fun testCompoundKeyHashStaysTheSameAfterRecompositions() {
+    fun testCompoundKeyHashStaysTheSameAfterRecompositions() = compositionTest {
         val outerKeys = mutableListOf<Int>()
         val innerKeys = mutableListOf<Int>()
         var previousOuterKeysSize = 0
@@ -1863,11 +1897,11 @@
         var innerInvalidate: (() -> Unit) = {}
 
         @Composable
-        fun MockComposeScope.test() {
+        fun Test() {
             outerInvalidate = invalidate
             outerKeys.add(currentComposer.currentCompoundKeyHash)
             Container {
-                linear {
+                Linear {
                     innerInvalidate = invalidate
                     innerKeys.add(currentComposer.currentCompoundKeyHash)
                 }
@@ -1876,8 +1910,8 @@
             assertEquals(outerKeys.last(), currentComposer.currentCompoundKeyHash)
         }
 
-        val result = compose {
-            test()
+        compose {
+            Test()
         }
 
         assertNotEquals(previousOuterKeysSize, outerKeys.size)
@@ -1885,12 +1919,12 @@
 
         previousOuterKeysSize = outerKeys.size
         outerInvalidate()
-        result.expectNoChanges()
+        expectNoChanges()
         assertNotEquals(previousOuterKeysSize, outerKeys.size)
 
         previousInnerKeysSize = innerKeys.size
         innerInvalidate()
-        result.expectNoChanges()
+        expectNoChanges()
         assertNotEquals(previousInnerKeysSize, innerKeys.size)
 
         assertNotEquals(innerKeys[0], outerKeys[0])
@@ -1903,25 +1937,25 @@
     }
 
     @Test // b/152753046
-    fun testSwappingGroups() {
+    fun testSwappingGroups() = compositionTest {
         val items = mutableListOf(0, 1, 2, 3, 4)
         var invalidateComposition = {}
 
         @Composable
-        fun MockComposeScope.noNodes() { }
+        fun NoNodes() { }
 
         @Composable
-        fun MockComposeScope.test() {
+        fun Test() {
             invalidateComposition = invalidate
             for (item in items) {
                 key(item) {
-                    noNodes()
+                    NoNodes()
                 }
             }
         }
 
-        val result = compose {
-            test()
+        compose {
+            Test()
         }
 
         // Swap 2 and 3
@@ -1929,11 +1963,11 @@
         items[3] = 2
         invalidateComposition()
 
-        result.expectChanges()
+        expectChanges()
     }
 
     @Test // b/154650546
-    fun testInsertOnMultipleLevels() {
+    fun testInsertOnMultipleLevels() = compositionTest {
         val items = mutableListOf(
             1 to mutableListOf(
                 0, 1, 2, 3, 4
@@ -1950,70 +1984,70 @@
         }
 
         @Composable
-        fun MockComposeScope.numbers(numbers: List<Int>) {
-            linear {
-                linear {
+        fun Numbers(numbers: List<Int>) {
+            Linear {
+                Linear {
                     invalidates.add(invalidate)
                     for (number in numbers) {
-                        text("$number")
+                        Text("$number")
                     }
                 }
             }
         }
 
         @Composable
-        fun MockComposeScope.item(number: Int, numbers: List<Int>) {
-            linear {
+        fun Item(number: Int, numbers: List<Int>) {
+            Linear {
                 invalidates.add(invalidate)
-                text("$number")
-                numbers(numbers)
+                Text("$number")
+                Numbers(numbers)
             }
         }
 
         @Composable
-        fun MockComposeScope.test() {
+        fun Test() {
             invalidates.add(invalidate)
 
-            linear {
+            Linear {
                 invalidates.add(invalidate)
                 for ((number, numbers) in items) {
-                    item(number, numbers)
+                    Item(number, numbers)
                 }
             }
         }
 
         fun MockViewValidator.numbers(numbers: List<Int>) {
-            linear {
-                linear {
+            Linear {
+                Linear {
                     for (number in numbers) {
-                        text("$number")
+                        Text("$number")
                     }
                 }
             }
         }
 
         fun MockViewValidator.item(number: Int, numbers: List<Int>) {
-            linear {
-                text("$number")
+            Linear {
+                Text("$number")
                 numbers(numbers)
             }
         }
 
-        fun MockViewValidator.test() {
-            linear {
+        fun MockViewValidator.Test() {
+            Linear {
                 for ((number, numbers) in items) {
                     item(number, numbers)
                 }
             }
         }
 
-        val result = compose {
-            test()
+        compose {
+            Test()
         }
 
         fun validate() {
-            result.validate {
-                test()
+            validate {
+                this.Test()
             }
         }
 
@@ -2028,12 +2062,12 @@
 
         invalidateComposition()
 
-        result.expectChanges()
+        expectChanges()
         validate()
     }
 
     @Test
-    fun testInsertingAfterSkipping() {
+    fun testInsertingAfterSkipping() = compositionTest {
         val items = mutableListOf(
             1 to listOf(0, 1, 2, 3, 4)
         )
@@ -2045,55 +2079,54 @@
         }
 
         @Composable
-        fun MockComposeScope.test() {
+        fun Test() {
             invalidates.add(invalidate)
 
-            linear {
+            Linear {
                 for ((item, numbers) in items) {
-                    text(item.toString())
-                    linear {
+                    Text(item.toString())
+                    Linear {
                         invalidates.add(invalidate)
                         for (number in numbers) {
-                            text(number.toString())
+                            Text(number.toString())
                         }
                     }
                 }
             }
         }
 
-        fun MockViewValidator.test() {
-            linear {
+        fun MockViewValidator.Test() {
+            Linear {
                 for ((item, numbers) in items) {
-                    text(item.toString())
-                    linear {
+                    Text(item.toString())
+                    Linear {
                         for (number in numbers) {
-                            text(number.toString())
+                            Text(number.toString())
                         }
                     }
                 }
             }
         }
 
-        val result = compose {
-            test()
+        compose {
+            Test()
         }
 
-        result.validate {
-            test()
+        validate {
+            this.Test()
         }
 
         items.add(2 to listOf(3, 4, 5, 6))
         invalidateComposition()
 
-        result.expectChanges()
-        result.validate {
-            test()
+        expectChanges()
+        validate {
+            this.Test()
         }
     }
 
     @Test
-    fun evenOddRecomposeGroup() {
-
+    fun evenOddRecomposeGroup() = compositionTest {
         var includeEven = true
         var includeOdd = true
         val invalidates = mutableListOf<() -> Unit>()
@@ -2106,122 +2139,122 @@
         }
 
         @Composable
-        fun MockComposeScope.wrapper(content: @Composable () -> Unit) {
+        fun Wrapper(content: @Composable () -> Unit) {
             content()
         }
 
         @Composable
-        fun MockComposeScope.emitText() {
+        fun EmitText() {
             invalidates.add(invalidate)
             if (includeOdd) {
                 key(1) {
-                    text("odd 1")
+                    Text("odd 1")
                 }
             }
             if (includeEven) {
                 key(2) {
-                    text("even 2")
+                    Text("even 2")
                 }
             }
             if (includeOdd) {
                 key(3) {
-                    text("odd 3")
+                    Text("odd 3")
                 }
             }
             if (includeEven) {
                 key(4) {
-                    text("even 4")
+                    Text("even 4")
                 }
             }
         }
 
         @Composable
-        fun MockComposeScope.test() {
-            linear {
-                wrapper {
-                    emitText()
+        fun Test() {
+            Linear {
+                Wrapper {
+                    EmitText()
                 }
-                emitText()
-                wrapper {
-                    emitText()
+                EmitText()
+                Wrapper {
+                    EmitText()
                 }
-                emitText()
+                EmitText()
             }
         }
 
-        fun MockViewValidator.wrapper(children: () -> Unit) {
+        fun MockViewValidator.Wrapper(children: () -> Unit) {
             children()
         }
 
-        fun MockViewValidator.emitText() {
+        fun MockViewValidator.EmitText() {
             if (includeOdd) {
-                text("odd 1")
+                Text("odd 1")
             }
             if (includeEven) {
-                text("even 2")
+                Text("even 2")
             }
             if (includeOdd) {
-                text("odd 3")
+                Text("odd 3")
             }
             if (includeEven) {
-                text("even 4")
+                Text("even 4")
             }
         }
 
-        fun MockViewValidator.test() {
-            linear {
-                wrapper {
-                    emitText()
+        fun MockViewValidator.Test() {
+            Linear {
+                this.Wrapper {
+                    this.EmitText()
                 }
-                emitText()
-                wrapper {
-                    emitText()
+                this.EmitText()
+                this.Wrapper {
+                    this.EmitText()
                 }
-                emitText()
+                this.EmitText()
             }
         }
 
-        val result = compose {
-            test()
+        compose {
+            Test()
         }
 
         fun validate() {
-            result.validate {
-                test()
+            validate {
+                this.Test()
             }
         }
         validate()
 
         includeEven = false
         invalidateComposition()
-        result.expectChanges()
+        expectChanges()
         validate()
 
         includeEven = true
         includeOdd = false
         invalidateComposition()
-        result.expectChanges()
+        expectChanges()
         validate()
 
         includeEven = false
         includeOdd = false
         invalidateComposition()
-        result.expectChanges()
+        expectChanges()
         validate()
 
         includeEven = true
         invalidateComposition()
-        result.expectChanges()
+        expectChanges()
         validate()
 
         includeOdd = true
         invalidateComposition()
-        result.expectChanges()
+        expectChanges()
         validate()
     }
 
     @Test
-    fun evenOddWithMovement() {
+    fun evenOddWithMovement() = compositionTest {
         var includeEven = true
         var includeOdd = true
         var order = listOf(1, 2, 3, 4)
@@ -2235,84 +2268,84 @@
         }
 
         @Composable
-        fun MockComposeScope.emitText(all: Boolean) {
+        fun EmitText(all: Boolean) {
             invalidates.add(invalidate)
             for (i in order) {
                 if (i % 2 == 1 && (all || includeOdd)) {
                     key(i) {
-                        text("odd $i")
+                        Text("odd $i")
                     }
                 }
                 if (i % 2 == 0 && (all || includeEven)) {
                     key(i) {
-                        text("even $i")
+                        Text("even $i")
                     }
                 }
             }
         }
 
         @Composable
-        fun MockComposeScope.test() {
-            linear {
+        fun Test() {
+            Linear {
                 invalidates.add(invalidate)
                 for (i in order) {
                     key(i) {
-                        text("group $i")
+                        Text("group $i")
                         if (i == 2 || (includeEven && includeOdd)) {
-                            text("including everything")
+                            Text("including everything")
                         } else {
                             if (includeEven) {
-                                text("including evens")
+                                Text("including evens")
                             }
                             if (includeOdd) {
-                                text("including odds")
+                                Text("including odds")
                             }
                         }
-                        emitText(i == 2)
+                        EmitText(i == 2)
                     }
                 }
-                emitText(false)
+                EmitText(false)
             }
         }
 
-        fun MockViewValidator.emitText(all: Boolean) {
+        fun MockViewValidator.EmitText(all: Boolean) {
             for (i in order) {
                 if (i % 2 == 1 && (includeOdd || all)) {
-                    text("odd $i")
+                    Text("odd $i")
                 }
                 if (i % 2 == 0 && (includeEven || all)) {
-                    text("even $i")
+                    Text("even $i")
                 }
             }
         }
 
-        fun MockViewValidator.test() {
-            linear {
+        fun MockViewValidator.Test() {
+            Linear {
                 for (i in order) {
-                    text("group $i")
+                    Text("group $i")
                     if (i == 2 || (includeEven && includeOdd)) {
-                        text("including everything")
+                        Text("including everything")
                     } else {
                         if (includeEven) {
-                            text("including evens")
+                            Text("including evens")
                         }
                         if (includeOdd) {
-                            text("including odds")
+                            Text("including odds")
                         }
                     }
-                    emitText(i == 2)
+                    this.EmitText(i == 2)
                 }
-                emitText(false)
+                this.EmitText(false)
             }
         }
 
-        val result = compose {
-            test()
+        compose {
+            Test()
         }
 
         fun validate() {
-            result.validate {
-                test()
+            validate {
+                this.Test()
             }
         }
         validate()
@@ -2320,78 +2353,75 @@
         order = listOf(1, 2, 4, 3)
         includeEven = false
         invalidateComposition()
-        result.expectChanges()
+        expectChanges()
         validate()
 
         order = listOf(1, 4, 2, 3)
         includeEven = true
         includeOdd = false
         invalidateComposition()
-        result.expectChanges()
+        expectChanges()
         validate()
 
         order = listOf(3, 4, 2, 1)
         includeEven = false
         includeOdd = false
         invalidateComposition()
-        result.expectChanges()
+        expectChanges()
         validate()
 
         order = listOf(4, 3, 2, 1)
         includeEven = true
         invalidateComposition()
-        result.expectChanges()
+        expectChanges()
         validate()
 
         order = listOf(1, 2, 3, 4)
         includeOdd = true
         invalidateComposition()
-        result.expectChanges()
+        expectChanges()
         validate()
     }
 
     @Test
-    fun testObservationScopes() {
+    fun testObservationScopes() = compositionTest {
         val states = mutableListOf<MutableState<Int>>()
         var iterations = 0
 
-        @Composable fun MockComposeScope.test() {
+        @Composable
+        fun Test() {
             val s1 = mutableStateOf(iterations++)
-            text("s1 ${s1.value}")
+            Text("s1 ${s1.value}")
             states.add(s1)
             val s2 = mutableStateOf(iterations++)
-            text("s2 ${s2.value}")
+            Text("s2 ${s2.value}")
             states.add(s2)
         }
 
-        val result = compose {
-            test()
+        compose {
+            Test()
         }
 
-        result.observe {
-            fun invalidateFirst() {
-                states.first().value++
-                Snapshot.sendApplyNotifications()
-            }
-
-            fun invalidateLast() {
-                states.last().value++
-                Snapshot.sendApplyNotifications()
-            }
-
-            repeat(10) {
-                invalidateLast()
-                result.expectChanges()
-            }
-
-            invalidateFirst()
-            result.expectNoChanges()
+        fun invalidateFirst() {
+            states.first().value++
         }
+
+        fun invalidateLast() {
+            states.last().value++
+        }
+
+        repeat(10) {
+            invalidateLast()
+            expectChanges()
+        }
+
+        invalidateFirst()
+        expectNoChanges()
     }
 
     @OptIn(ComposeCompilerApi::class)
     @Test
-    fun testApplierBeginEndCallbacks() {
+    fun testApplierBeginEndCallbacks() = compositionTest {
         val checks = mutableListOf<String>()
         compose {
             val myComposer = currentComposer
@@ -2442,67 +2472,274 @@
     }
 
     @Test // regression test for b/172660922
-    fun testInvalidationOfRemovedContent() {
+    fun testInvalidationOfRemovedContent() = compositionTest {
         var markS1Invalid: () -> Unit = {}
         var viewS1 by mutableStateOf(true)
-        @Composable fun MockComposeScope.s1() {
+        var performBackwardsWrite = true
+        @Composable
+        fun S1() {
             markS1Invalid = invalidate
-            text("In s1")
-            markS1Invalid()
+            Text("In s1")
         }
 
-        @Composable fun MockComposeScope.s2() {
-            text("In s2")
+        @Composable
+        fun S2() {
+            Text("In s2")
         }
 
-        @Composable fun MockComposeScope.test() {
-            text("$viewS1")
+        @Composable
+        fun Test() {
+            Text("$viewS1")
             Wrap {
                 if (viewS1) {
-                    s1()
+                    S1()
                 }
-                s2()
+                S2()
             }
 
-            // This forces the equivalent of a backwards write.
-            markS1Invalid()
+            if (performBackwardsWrite) {
+                // This forces the equivalent of a backwards write.
+                markS1Invalid()
+                performBackwardsWrite = false
+            }
         }
 
-        fun MockViewValidator.s1() {
-            text("In s1")
+        fun MockViewValidator.S1() {
+            Text("In s1")
         }
 
-        fun MockViewValidator.s2() {
-            text("In s2")
+        fun MockViewValidator.S2() {
+            Text("In s2")
         }
 
-        fun MockViewValidator.test() {
-            text("$viewS1")
+        fun MockViewValidator.Test() {
+            Text("$viewS1")
             if (viewS1) {
-                s1()
+                this.S1()
             }
-            s2()
+            this.S2()
         }
 
-        val result = compose {
-            test()
+        compose {
+            Test()
         }
 
-        fun validate() = result.validate { test() }
+        fun validate() = validate { this.Test() }
 
         validate()
 
         markS1Invalid()
-        result.expectNoChanges()
+        performBackwardsWrite = true
+        expectNoChanges()
 
         validate()
 
-        result.observe {
-            viewS1 = false
-            result.expectChanges()
-            validate()
+        viewS1 = false
+        performBackwardsWrite = true
+        expectChanges()
+        validate()
+    }
+
+    /**
+     * This test checks that an updated ComposableLambda capture used in a subcomposition
+     * correctly invalidates that subcomposition and schedules recomposition of that subcomposition.
+     */
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testComposableLambdaSubcompositionInvalidation() = runBlockingTest {
+        localRecomposerTest { recomposer ->
+            val composer = Composer(EmptyApplier(), recomposer)
+            try {
+                var rootState by mutableStateOf(false)
+                val composedResults = mutableListOf<Boolean>()
+                Snapshot.notifyObjectsInitialized()
+                recomposer.composeInitial(composer) {
+                    // Read into local variable, local will be captured below
+                    val capturedValue = rootState
+                    TestSubcomposition {
+                        composedResults.add(capturedValue)
+                    }
+                }
+                composer.applyChanges()
+                assertEquals(listOf(false), composedResults)
+                rootState = true
+                Snapshot.sendApplyNotifications()
+                advanceUntilIdle()
+                assertEquals(listOf(false, true), composedResults)
+            } finally {
+                composer.dispose()
+            }
         }
     }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testCompositionReferenceIsRemembered() = runBlockingTest {
+        localRecomposerTest { recomposer ->
+            val composer = Composer(EmptyApplier(), recomposer)
+            try {
+                lateinit var invalidator: () -> Unit
+                val parentReferences = mutableListOf<CompositionReference>()
+                recomposer.composeInitial(composer) {
+                    invalidator = invalidate
+                    parentReferences += compositionReference()
+                }
+                composer.applyChanges()
+                invalidator()
+                advanceUntilIdle()
+                assert(parentReferences.size > 1) { "expected to be composed more than once" }
+                assert(parentReferences.toSet().size == 1) {
+                    "expected all parentReferences to be the same; saw $parentReferences"
+                }
+            } finally {
+                composer.dispose()
+            }
+        }
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testParentCompositionRecomposesFirst() = runBlockingTest {
+        localRecomposerTest { recomposer ->
+            val composer = Composer(EmptyApplier(), recomposer)
+            val results = mutableListOf<String>()
+            try {
+                var firstState by mutableStateOf("firstInitial")
+                var secondState by mutableStateOf("secondInitial")
+                Snapshot.notifyObjectsInitialized()
+                recomposer.composeInitial(composer) {
+                    results += firstState
+                    TestSubcomposition {
+                        results += secondState
+                    }
+                }
+                secondState = "secondSet"
+                Snapshot.sendApplyNotifications()
+                firstState = "firstSet"
+                Snapshot.sendApplyNotifications()
+                advanceUntilIdle()
+                assertEquals(
+                    listOf("firstInitial", "secondInitial", "firstSet", "secondSet"),
+                    results,
+                    "Expected call ordering during recomposition of subcompositions"
+                )
+            } finally {
+                composer.dispose()
+            }
+        }
+    }
+
+    /**
+     * An [Applier] may inadvertently (or on purpose) run arbitrary user code as a side effect
+     * of performing tree manipulations as a [Composer] is applying changes. This can happen
+     * if the tree type dispatches event callbacks when nodes are added or removed from a tree.
+     * These callbacks may cause snapshot state writes, which can in turn invalidate scopes in the
+     * composition that produced the tree in the first place. Ensure that the recomposition
+     * machinery is robust to this, and that these invalidations are processed on a subsequent
+     * recomposition.
+     */
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testStateWriteInApplier() = runBlockingTest {
+
+        class MutateOnRemoveApplier(
+            private val removeCounter: MutableState<Int>
+        ) : AbstractApplier<Unit>(Unit) {
+            var insertCount: Int = 0
+                private set
+
+            override fun remove(index: Int, count: Int) {
+                removeCounter.value += count
+            }
+
+            override fun onClear() {
+                // do nothing
+            }
+
+            override fun insertTopDown(index: Int, instance: Unit) {
+                insertCount++
+            }
+
+            override fun insertBottomUp(index: Int, instance: Unit) {
+                // do nothing
+            }
+
+            override fun move(from: Int, to: Int, count: Int) {
+                // do nothing
+            }
+        }
+
+        localRecomposerTest { recomposer ->
+            val stateMutatedOnRemove = mutableStateOf(0)
+            var shouldEmitNode by mutableStateOf(true)
+            var compositionCount = 0
+            Snapshot.notifyObjectsInitialized()
+            val applier = MutateOnRemoveApplier(stateMutatedOnRemove)
+            val composer = Composer(applier, recomposer)
+            try {
+                recomposer.composeInitial(composer) {
+                    compositionCount++
+                    // Read the state here so that the emit removal will invalidate it
+                    stateMutatedOnRemove.value
+                    if (shouldEmitNode) {
+                        emit<Unit, MutateOnRemoveApplier>({ Unit }) {}
+                    }
+                }
+                // Initial composition should not contain the node we will remove. We want to test
+                // recomposition for this case in particular.
+                assertEquals(1, applier.insertCount, "expected setup node not inserted")
+                shouldEmitNode = false
+                Snapshot.sendApplyNotifications()
+                advanceUntilIdle()
+                assertEquals(1, stateMutatedOnRemove.value, "observable removals performed")
+                // Only two composition passes should have been performed by this point; a state
+                // invalidation in the applier should not be picked up or acted upon until after
+                // this frame is complete.
+                assertEquals(2, compositionCount, "expected number of (re)compositions performed")
+                // After sending apply notifications we expect the snapshot state change made by
+                // the applier to trigger one final recomposition.
+                Snapshot.sendApplyNotifications()
+                advanceUntilIdle()
+                assertEquals(3, compositionCount, "expected number of (re)compositions performed")
+            } finally {
+                composer.dispose()
+            }
+        }
+    }
+}
+
+@OptIn(InternalComposeApi::class, ExperimentalComposeApi::class)
+@Composable
+private fun TestSubcomposition(
+    content: @Composable () -> Unit
+) {
+    val parentRef = compositionReference()
+    val currentContent by rememberUpdatedState(content)
+    DisposableEffect(parentRef) {
+        val subcomposer = Composer(EmptyApplier(), parentRef)
+        parentRef.composeInitial(subcomposer) {
+            currentContent()
+        }
+        subcomposer.applyChanges()
+        onDispose {
+            subcomposer.dispose()
+        }
+    }
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+private suspend fun <R> localRecomposerTest(
+    block: CoroutineScope.(Recomposer) -> R
+) = coroutineScope {
+    val contextWithClock = coroutineContext + TestMonotonicFrameClock(this)
+    val recomposer = Recomposer(contextWithClock)
+    launch(contextWithClock) {
+        recomposer.runRecomposeAndApplyChanges()
+    }
+    block(recomposer)
+    // This call doesn't need to be in a finally; everything it does will be torn down
+    // in exceptional cases by the coroutineScope failure
+    recomposer.shutDown()
 }
 
 @Composable fun Wrap(content: @Composable () -> Unit) {
@@ -2525,115 +2762,6 @@
     }
 }
 
-@OptIn(InternalComposeApi::class, ExperimentalComposeApi::class)
-private class CompositionResult(
-    val composer: Composer<*>,
-    val root: View
-) {
-    fun validate(block: MockViewValidator.() -> Unit) {
-        MockViewListValidator(root.children).validate(block)
-    }
-
-    fun expectNoChanges() {
-        val changes = recompose() && composer.changeCount > 0
-        assertFalse(changes, "Changes detected when they were not expected")
-    }
-
-    fun expectChanges() {
-        val changes = recompose() && composer.changeCount > 0
-        assertTrue(changes, "Expected changes")
-        composer.applyChanges()
-        Snapshot.notifyObjectsInitialized()
-        composer.slotTable.verifyWellFormed()
-        composer.insertTable.verifyWellFormed()
-    }
-
-    fun recompose(): Boolean = doCompose(composer) { composer.recompose() }
-
-    fun observe(block: () -> Unit) {
-        val unregister = Snapshot.registerApplyObserver { changed, _ ->
-            composer.recordModificationsOf(changed)
-        }
-        try {
-            block()
-        } finally {
-            unregister()
-        }
-    }
-}
-
-@OptIn(InternalComposeApi::class, ExperimentalComposeApi::class)
-private fun <T> doCompose(composer: Composer<*>, block: () -> T): T {
-    val snapshot = takeMutableSnapshot(
-        {
-            composer.recordReadOf(it)
-        },
-        {
-            composer.recordWriteOf(it)
-        }
-    )
-    return try {
-        snapshot.enter { block() }.also {
-            snapshot.apply().check()
-        }
-    } finally {
-        snapshot.dispose()
-    }
-}
-
-@OptIn(InternalComposeApi::class, ExperimentalComposeApi::class)
-private fun compose(
-    block: @Composable MockComposeScope.() -> Unit
-): CompositionResult {
-    val root = View().apply { name = "root" }
-    val composer = run {
-        val scope = CoroutineScope(Job())
-        val clock = object : MonotonicFrameClock {
-            override suspend fun <R> withFrameNanos(onFrame: (Long) -> R): R {
-                // The original version of this test used a mock Recomposer
-                // that never successfully scheduled a frame.
-                suspendCancellableCoroutine<Unit> {}
-                return onFrame(0)
-            }
-        }
-        val recomposer = Recomposer(scope.coroutineContext).apply {
-            scope.launch(clock) { runRecomposeAndApplyChanges() }
-        }
-        Composer(
-            SlotTable(),
-            ViewApplier(root),
-            recomposer
-        )
-    }
-
-    val mockScope = MockComposeScope()
-    doCompose(composer) {
-        composer.composeInitial {
-            mockScope.block()
-        }
-        Snapshot.notifyObjectsInitialized()
-        composer.applyChanges()
-    }
-    composer.slotTable.verifyWellFormed()
-    validateRecomposeScopeAnchors(composer.slotTable)
-
-    return CompositionResult(composer, root)
-}
-
-@OptIn(InternalComposeApi::class)
-fun validateRecomposeScopeAnchors(slotTable: SlotTable) {
-    val scopes = slotTable.slots.map { it as? RecomposeScope }.filterNotNull()
-    for (scope in scopes) {
-        scope.anchor?.let { anchor ->
-            check(scope in slotTable.slotsOf(anchor.toIndexFor(slotTable))) {
-                val dataIndex = slotTable.slots.indexOf(scope)
-                "Misaligned anchor $anchor in scope $scope encountered, scope found at " +
-                    "$dataIndex"
-            }
-        }
-    }
-}
-
 // Contact test data
 private val bob = Contact("Bob Smith", email = "bob@smith.com")
 private val jon = Contact(name = "Jon Alberton", email = "jon@alberton.com")
@@ -2664,3 +2792,23 @@
 private interface Named {
     val name: String
 }
+
+@OptIn(ExperimentalComposeApi::class)
+private class EmptyApplier : Applier<Unit> {
+    override val current: Unit = Unit
+    override fun down(node: Unit) {}
+    override fun up() {}
+    override fun insertTopDown(index: Int, instance: Unit) {
+        error("Unexpected")
+    }
+    override fun insertBottomUp(index: Int, instance: Unit) {
+        error("Unexpected")
+    }
+    override fun remove(index: Int, count: Int) {
+        error("Unexpected")
+    }
+    override fun move(from: Int, to: Int, count: Int) {
+        error("Unexpected")
+    }
+    override fun clear() {}
+}
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt
index 2f61e12..8742564 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt
@@ -3047,37 +3047,83 @@
             }
         }
     }
+
+    @Test
+    fun canRepositionReaderPastEndOfTable() {
+        val slots = SlotTable().also {
+            it.write { writer ->
+                // Create exactly 256 groups
+                repeat(256) {
+                    writer.insert {
+                        writer.startGroup(0)
+                        writer.endGroup()
+                    }
+                }
+            }
+        }
+
+        slots.read { reader ->
+            reader.reposition(reader.size)
+            // Expect the above not to crash.
+        }
+    }
+
+    @Test
+    fun canRemoveFromFullTable() {
+        // Create a table that is exactly 64 entries
+        val slots = SlotTable().also {
+            it.write { writer ->
+                writer.insert {
+                    repeat(7) { outer ->
+                        writer.group(10 + outer) {
+                            repeat(8) { inner ->
+                                writer.group(inner) { }
+                            }
+                        }
+                    }
+                    writer.group(30) { }
+                }
+            }
+        }
+        slots.verifyWellFormed()
+
+        // Remove the first group
+        slots.write { writer ->
+            writer.removeGroup()
+        }
+        slots.verifyWellFormed()
+    }
 }
 
 @OptIn(InternalComposeApi::class)
-private inline fun SlotWriter.group(block: () -> Unit) {
+internal inline fun SlotWriter.group(block: () -> Unit) {
     startGroup()
     block()
     endGroup()
 }
 
 @OptIn(InternalComposeApi::class)
-private inline fun SlotWriter.group(key: Int, block: () -> Unit) {
+internal inline fun SlotWriter.group(key: Int, block: () -> Unit) {
     startGroup(key)
     block()
     endGroup()
 }
 
 @OptIn(InternalComposeApi::class)
-private inline fun SlotWriter.nodeGroup(key: Int, node: Any, block: () -> Unit = { }) {
+internal inline fun SlotWriter.nodeGroup(key: Int, node: Any, block: () -> Unit = { }) {
     startNode(key, node)
     block()
     endGroup()
 }
 @OptIn(InternalComposeApi::class)
-private inline fun SlotWriter.insert(block: () -> Unit) {
+internal inline fun SlotWriter.insert(block: () -> Unit) {
     beginInsert()
     block()
     endInsert()
 }
 
 @OptIn(InternalComposeApi::class)
-private inline fun SlotReader.group(key: Int, block: () -> Unit) {
+internal inline fun SlotReader.group(key: Int, block: () -> Unit) {
     assertEquals(key, groupKey)
     startGroup()
     block()
@@ -3085,7 +3131,7 @@
 }
 
 @OptIn(InternalComposeApi::class)
-private inline fun SlotReader.group(block: () -> Unit) {
+internal inline fun SlotReader.group(block: () -> Unit) {
     startGroup()
     block()
     endGroup()
@@ -3104,7 +3150,7 @@
 private const val elementKey = 100
 
 @OptIn(InternalComposeApi::class)
-fun testSlotsNumbered(): SlotTable {
+private fun testSlotsNumbered(): SlotTable {
     val slotTable = SlotTable()
     slotTable.write { writer ->
         writer.beginInsert()
@@ -3121,7 +3167,7 @@
 
 // Creates 0 until 10 items each with 10 elements numbered 0...n with 0..n slots
 @OptIn(InternalComposeApi::class)
-fun testItems(): SlotTable {
+private fun testItems(): SlotTable {
     val slots = SlotTable()
     slots.write { writer ->
         writer.beginInsert()
@@ -3158,7 +3204,7 @@
 }
 
 @OptIn(InternalComposeApi::class)
-fun validateItems(slots: SlotTable) {
+private fun validateItems(slots: SlotTable) {
     slots.read { reader ->
         check(reader.groupKey == treeRoot) { "Invalid root key" }
         reader.startGroup()
@@ -3209,7 +3255,7 @@
 }
 
 @OptIn(InternalComposeApi::class)
-fun narrowTrees(): Pair<SlotTable, List<Anchor>> {
+private fun narrowTrees(): Pair<SlotTable, List<Anchor>> {
     val slots = SlotTable()
     val anchors = mutableListOf<Anchor>()
     slots.write { writer ->
@@ -3258,13 +3304,13 @@
 }
 
 @OptIn(InternalComposeApi::class)
-fun SlotReader.expectGroup(key: Int): Int {
+private fun SlotReader.expectGroup(key: Int): Int {
     assertEquals(key, groupKey)
     return skipGroup()
 }
 
 @OptIn(InternalComposeApi::class)
-fun SlotReader.expectGroup(
+private fun SlotReader.expectGroup(
     key: Int,
     block: () -> Unit
 ) {
@@ -3275,12 +3321,12 @@
 }
 
 @OptIn(InternalComposeApi::class)
-fun SlotReader.expectData(value: Any) {
+private fun SlotReader.expectData(value: Any) {
     assertEquals(value, next())
 }
 
 @OptIn(InternalComposeApi::class)
-fun SlotReader.expectGroup(
+private fun SlotReader.expectGroup(
     key: Int,
     objectKey: Any?,
     block: () -> Unit = { skipToGroupEnd() }
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeContact.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
index 786d6b6..d85c2d3 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
@@ -24,17 +24,17 @@
 // </linear>
 @Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.contact(contact: Contact) {
-    linear {
-        text(value = "Name: ${contact.name}")
-        text(value = "email: ${contact.email}")
+fun contact(contact: Contact) {
+    Linear {
+        Text(value = "Name: ${contact.name}")
+        Text(value = "email: ${contact.email}")
     }
 }
 
 fun MockViewValidator.contact(contact: Contact) {
-    linear {
-        text(value = "Name: ${contact.name}")
-        text(value = "email: ${contact.email}")
+    Linear {
+        Text(value = "Name: ${contact.name}")
+        Text(value = "email: ${contact.email}")
     }
 }
 
@@ -47,10 +47,10 @@
 // </linear>
 @Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.contacts(contacts: Collection<Contact>, selected: Contact?) {
-    linear {
-        repeat(of = contacts) {
-            selectBox(it == selected) {
+fun contacts(contacts: Collection<Contact>, selected: Contact?) {
+    Linear {
+        Repeated(of = contacts) {
+            SelectBox(it == selected) {
                 contact(it)
             }
         }
@@ -58,9 +58,9 @@
 }
 
 fun MockViewValidator.contacts(contacts: Collection<Contact>, selected: Contact?) {
-    linear {
-        repeat(of = contacts) {
-            selectBox(it == selected) {
+    Linear {
+        Repeated(of = contacts) {
+            SelectBox(it == selected) {
                 contact(it)
             }
         }
@@ -79,29 +79,29 @@
 // </linear>
 @Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.selectContact(model: ContactModel) {
-    linear {
-        linear {
-            text(value = "Filter:")
-            edit(value = model.filter)
+fun SelectContact(model: ContactModel) {
+    Linear {
+        Linear {
+            Text(value = "Filter:")
+            Edit(value = model.filter)
         }
 
-        linear {
-            text(value = "Contacts:")
+        Linear {
+            Text(value = "Contacts:")
             contacts(model.filtered, model.selected)
         }
     }
 }
 
-fun MockViewValidator.selectContact(model: ContactModel) {
-    linear {
-        linear {
-            text(value = "Filter:")
-            edit(value = model.filter)
+fun MockViewValidator.SelectContact(model: ContactModel) {
+    Linear {
+        Linear {
+            Text(value = "Filter:")
+            Edit(value = model.filter)
         }
 
-        linear {
-            text(value = "Contacts:")
+        Linear {
+            Text(value = "Contacts:")
             contacts(model.filtered, model.selected)
         }
     }
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposePoints.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
index 4f7c1b1..dd2aff1 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
@@ -18,28 +18,26 @@
 
 import androidx.compose.runtime.Composable
 
-@Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.point(point: Point) {
-    text("X: ${point.x} Y: ${point.y}")
+fun Point(point: Point) {
+    Text("X: ${point.x} Y: ${point.y}")
 }
 
-fun MockViewValidator.point(point: Point) {
-    text("X: ${point.x} Y: ${point.y}")
+fun MockViewValidator.Point(point: Point) {
+    Text("X: ${point.x} Y: ${point.y}")
 }
 
 private const val SLPoints = 100
 
-@Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.points(points: Iterable<Point>) {
-    repeat(of = points) {
-        memoize(SLPoints, it) { point(it) }
+fun Points(points: Iterable<Point>) {
+    Repeated(of = points) {
+        Point(it)
     }
 }
 
-fun MockViewValidator.points(points: Iterable<Point>) {
-    repeat(of = points) {
-        point(it)
+fun MockViewValidator.Points(points: Iterable<Point>) {
+    Repeated(of = points) {
+        Point(it)
     }
 }
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeReport.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
index cdf27bf..24333a9 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
@@ -18,34 +18,32 @@
 
 import androidx.compose.runtime.Composable
 
-@Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.reportsTo(report: Report) {
-    text(report.from)
-    text("reports to")
-    text(report.to)
+fun ReportsTo(report: Report) {
+    Text(report.from)
+    Text("reports to")
+    Text(report.to)
 }
 
-fun MockViewValidator.reportsTo(report: Report) {
-    text(report.from)
-    text("reports to")
-    text(report.to)
+fun MockViewValidator.ReportsTo(report: Report) {
+    Text(report.from)
+    Text("reports to")
+    Text(report.to)
 }
 
-@Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.reportsReport(reports: Iterable<Report>) {
-    linear {
-        repeat(of = reports) { report ->
-            reportsTo(report)
+fun ReportsReport(reports: Iterable<Report>) {
+    Linear {
+        Repeated(of = reports) { report ->
+            ReportsTo(report)
         }
     }
 }
 
-fun MockViewValidator.reportsReport(reports: Iterable<Report>) {
-    linear {
-        repeat(of = reports) { report ->
-            reportsTo(report)
+fun MockViewValidator.ReportsReport(reports: Iterable<Report>) {
+    Linear {
+        Repeated(of = reports) { report ->
+            ReportsTo(report)
         }
     }
 }
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/CompositionTest.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
new file mode 100644
index 0000000..deaedac
--- /dev/null
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.runtime.mock
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Composition
+import androidx.compose.runtime.ExperimentalComposeApi
+import androidx.compose.runtime.InternalComposeApi
+import androidx.compose.runtime.Recomposer
+import androidx.compose.runtime.compositionFor
+import androidx.compose.runtime.snapshots.Snapshot
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.withContext
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@OptIn(ExperimentalComposeApi::class, InternalComposeApi::class, ExperimentalCoroutinesApi::class)
+fun compositionTest(block: suspend CompositionTestScope.() -> Unit) = runBlockingTest {
+    withContext(TestMonotonicFrameClock(this)) {
+        // Start the recomposer
+        val recomposer = Recomposer(coroutineContext)
+        launch { recomposer.runRecomposeAndApplyChanges() }
+
+        // Create a test scope for the test using the test scope passed in by runBlockingTest
+        val scope = object : CompositionTestScope, TestCoroutineScope by this@runBlockingTest {
+            var composed = false
+            var composition: Composition? = null
+
+            override lateinit var root: View
+
+            override fun compose(block: @Composable () -> Unit) {
+                check(!composed) { "Compose should only be called once" }
+                composed = true
+                root = View().apply { name = "root" }
+                val composition = compositionFor(root, ViewApplier(root), recomposer)
+                this.composition = composition
+                composition.setContent(block)
+            }
+
+            override fun advance(): Boolean {
+                val changeCount = recomposer.changeCount
+                Snapshot.sendApplyNotifications()
+                if (recomposer.hasInvalidations()) {
+                    advanceTimeBy(5_000)
+                    check(!recomposer.hasInvalidations()) {
+                        "Potentially infinite recomposition, still recomposing after advancing"
+                    }
+                }
+                return recomposer.changeCount != changeCount
+            }
+        }
+        scope.block()
+        scope.composition?.dispose()
+        recomposer.shutDown()
+        recomposer.join()
+    }
+}
+
+/**
+ * A test scope used in tests that allows controlling and testing composition.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+interface CompositionTestScope : TestCoroutineScope {
+    /**
+     * Compose a block using the mock view composer.
+     */
+    fun compose(block: @Composable () -> Unit)
+
+    /**
+     * Advance the state which executes any pending compositions, if any. Returns true if
+     * advancing resulted in changes being applied.
+     */
+    fun advance(): Boolean
+
+    /**
+     * The root mock view of the mock views being composed.
+     */
+    val root: View
+}
+
+/**
+ * Create a mock view validator and validate the view.
+ */
+fun CompositionTestScope.validate(block: MockViewValidator.() -> Unit) =
+    MockViewListValidator(root.children).validate(block)
+
+/**
+ * Advance and expect changes
+ */
+fun CompositionTestScope.expectChanges() {
+    val changes = advance()
+    assertTrue(
+        actual = changes,
+        message = "Expected changes but none were found"
+    )
+}
+
+/**
+ * Advance and expect no changes
+ */
+fun CompositionTestScope.expectNoChanges() {
+    val changes = advance()
+    assertFalse(
+        actual = changes,
+        message = "Expected no changes but changes occurred"
+    )
+}
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
index 07b409d..20ae087 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
@@ -56,25 +56,25 @@
     MockViewListValidator(view.children).validate(block)
 }
 
-fun <T> MockViewValidator.repeat(of: Iterable<T>, block: MockViewValidator.(value: T) -> Unit) {
+fun <T> MockViewValidator.Repeated(of: Iterable<T>, block: MockViewValidator.(value: T) -> Unit) {
     for (value in of) {
         block(value)
     }
 }
 
-fun MockViewValidator.linear() = view("linear", null)
-fun MockViewValidator.linear(block: MockViewValidator.() -> Unit) = view("linear", block)
+fun MockViewValidator.Linear() = view("linear", null)
+fun MockViewValidator.Linear(block: MockViewValidator.() -> Unit) = view("linear", block)
 fun MockViewValidator.box(block: MockViewValidator.() -> Unit) = view("box", block)
-fun MockViewValidator.text(value: String) {
+fun MockViewValidator.Text(value: String) {
     view("text")
     assertEquals(value, view.attributes["text"])
 }
-fun MockViewValidator.edit(value: String) {
+fun MockViewValidator.Edit(value: String) {
     view("edit")
     assertEquals(value, view.attributes["value"])
 }
 
-fun MockViewValidator.selectBox(selected: Boolean, block: MockViewValidator.() -> Unit) {
+fun MockViewValidator.SelectBox(selected: Boolean, block: MockViewValidator.() -> Unit) {
     if (selected) {
         box {
             block()
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
new file mode 100644
index 0000000..d7d6eb2
--- /dev/null
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+/*
+ * NOTE: This file is copied from androidx.compose.ui.test for use in testing compose-runtime.
+ * A future patch may graduate this to a formal compose-runtime-test module.
+ */
+package androidx.compose.runtime.mock
+
+import androidx.compose.runtime.dispatch.MonotonicFrameClock
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.test.DelayController
+import kotlin.coroutines.ContinuationInterceptor
+
+private const val DefaultFrameDelay = 16_000_000L
+
+/**
+ * Construct a [TestMonotonicFrameClock] for [coroutineScope], obtaining the [DelayController]
+ * from the scope's [context][CoroutineScope.coroutineContext]. This frame clock may be used to
+ * consistently drive time under controlled tests.
+ *
+ * Calls to [TestMonotonicFrameClock.withFrameNanos] will schedule an upcoming frame
+ * [frameDelayNanos] nanoseconds in the future by launching into [coroutineScope] if such a frame
+ * has not yet been scheduled.
+ */
+@Suppress("MethodNameUnits") // Nanos for high-precision animation clocks
+@ExperimentalCoroutinesApi
+fun TestMonotonicFrameClock(
+    coroutineScope: CoroutineScope,
+    frameDelayNanos: Long = DefaultFrameDelay
+): TestMonotonicFrameClock = TestMonotonicFrameClock(
+    coroutineScope = coroutineScope,
+    delayController = coroutineScope.coroutineContext[ContinuationInterceptor].let { interceptor ->
+        requireNotNull(interceptor as? DelayController) {
+            "ContinuationInterceptor $interceptor of supplied scope must implement DelayController"
+        }
+    },
+    frameDelayNanos = frameDelayNanos
+)
+
+/**
+ * A [MonotonicFrameClock] with a time source controlled by a `kotlinx-coroutines-test`
+ * [DelayController]. This frame clock may be used to consistently drive time under controlled
+ * tests.
+ *
+ * Calls to [withFrameNanos] will schedule an upcoming frame [frameDelayNanos] nanoseconds in the
+ * future by launching into [coroutineScope] if such a frame has not yet been scheduled. The
+ * current frame time for [withFrameNanos] is provided by [delayController]. It is strongly
+ * suggested that [coroutineScope] contain the test dispatcher controlled by [delayController].
+ */
+@ExperimentalCoroutinesApi
+class TestMonotonicFrameClock(
+    private val coroutineScope: CoroutineScope,
+    private val delayController: DelayController,
+    @get:Suppress("MethodNameUnits") // Nanos for high-precision animation clocks
+    val frameDelayNanos: Long = DefaultFrameDelay
+) : MonotonicFrameClock {
+    private val lock = Any()
+    private val awaiters = mutableListOf<Awaiter<*>>()
+    private var posted = false
+
+    private class Awaiter<R>(
+        private val onFrame: (Long) -> R,
+        private val continuation: CancellableContinuation<R>
+    ) {
+        fun runFrame(frameTimeNanos: Long): () -> Unit {
+            val result = runCatching { onFrame(frameTimeNanos) }
+            return { continuation.resumeWith(result) }
+        }
+    }
+
+    override suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R =
+        suspendCancellableCoroutine { co ->
+            synchronized(lock) {
+                awaiters.add(Awaiter(onFrame, co))
+                maybeLaunchTickRunner()
+            }
+        }
+
+    private fun maybeLaunchTickRunner() {
+        if (!posted) {
+            posted = true
+            coroutineScope.launch {
+                delay(frameDelayMillis)
+                synchronized(lock) {
+                    posted = false
+                    val toRun = awaiters.toList()
+                    awaiters.clear()
+                    val frameTime = delayController.currentTime * 1_000_000
+                    // In case of awaiters on an immediate dispatcher, run all frame callbacks
+                    // before resuming any associated continuations with the results.
+                    toRun.map { it.runFrame(frameTime) }.forEach { it() }
+                }
+            }
+        }
+    }
+}
+
+/**
+ * The frame delay time for the [TestMonotonicFrameClock] in milliseconds.
+ */
+@ExperimentalCoroutinesApi
+val TestMonotonicFrameClock.frameDelayMillis: Long
+    get() = frameDelayNanos / 1_000_000
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ViewApplier.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
index ae08758..69a78b5 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
@@ -17,12 +17,7 @@
 package androidx.compose.runtime.mock
 
 import androidx.compose.runtime.AbstractApplier
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeCompilerApi
-import androidx.compose.runtime.Composer
 import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.Stable
-import androidx.compose.runtime.currentComposer
 
 @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
 @OptIn(ExperimentalComposeApi::class)
@@ -61,25 +56,3 @@
         onEndChangesCalled++
     }
 }
-
-@Stable
-class MockComposeScope
-
-// TODO(lmr): we should really remove this from our tests
-@Suppress("UNCHECKED_CAST", "ComposableNaming")
-@OptIn(ComposeCompilerApi::class)
-@Composable
-fun <P1> MockComposeScope.memoize(
-    key: Int,
-    p1: P1,
-    block: @Composable (p1: P1) -> Unit
-) {
-    currentComposer.startGroup(key)
-    if (!currentComposer.changed(p1)) {
-        currentComposer.skipToGroupEnd()
-    } else {
-        val realFn = block as Function3<P1, Composer<*>, Int, Unit>
-        realFn(p1, currentComposer, 0)
-    }
-    currentComposer.endGroup()
-}
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt
index 39cdb19..ec940bc 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt
@@ -20,11 +20,10 @@
 import androidx.compose.runtime.emit
 import androidx.compose.runtime.key
 
-@Suppress("ComposableNaming")
 @Composable
-fun <T : Any> MockComposeScope.repeat(
+fun <T : Any> Repeated(
     of: Iterable<T>,
-    block: @Composable MockComposeScope.(value: T) -> Unit
+    block: @Composable (value: T) -> Unit
 ) {
     for (value in of) {
         key(value) {
@@ -33,9 +32,8 @@
     }
 }
 
-@Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.linear(content: @Composable MockComposeScope.() -> Unit) {
+fun Linear(content: @Composable () -> Unit) {
     emit<View, ViewApplier>(
         ctor = { View().also { it.name = "linear" } },
         update = { }
@@ -44,29 +42,26 @@
     }
 }
 
-@Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.text(value: String) {
+fun Text(value: String) {
     emit<View, ViewApplier>(
         ctor = { View().also { it.name = "text" } },
         update = { set(value) { text = it } }
     )
 }
 
-@Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.edit(value: String) {
+fun Edit(value: String) {
     emit<View, ViewApplier>(
         ctor = { View().also { it.name = "edit" } },
         update = { set(value) { this.value = it } }
     )
 }
 
-@Suppress("ComposableNaming")
 @Composable
-fun MockComposeScope.selectBox(
+fun SelectBox(
     selected: Boolean,
-    content: @Composable MockComposeScope.() -> Unit
+    content: @Composable () -> Unit
 ) {
     if (selected) {
         emit<View, ViewApplier>(
diff --git a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.kt b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.kt
index a6f913d..87326b1 100644
--- a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.kt
+++ b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.kt
@@ -34,7 +34,7 @@
 import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.dispatch.MonotonicFrameClock
 import androidx.compose.runtime.snapshots.Snapshot
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.test.TestMonotonicFrameClock
 import androidx.compose.ui.test.frameDelayMillis
@@ -134,7 +134,7 @@
         }
 
         composition = activity.setContent(recomposer) { testCase!!.Content() }
-        view = findAndroidOwner(activity)!!.view
+        view = findViewRootForTest(activity)!!.view
         @OptIn(ExperimentalComposeApi::class)
         Snapshot.notifyObjectsInitialized()
         simulationState = SimulationState.EmitContentDone
@@ -251,6 +251,9 @@
 
         composition?.dispose()
 
+        // Dispatcher will clean up the cancelled coroutines when it advances to them
+        testCoroutineDispatcher.advanceUntilIdle()
+
         // Clear the view
         val rootView = activity.findViewById(android.R.id.content) as ViewGroup
         rootView.removeAllViews()
@@ -305,18 +308,18 @@
     RecomposeDone
 }
 
-private fun findAndroidOwner(activity: Activity): AndroidOwner? {
-    return findAndroidOwner(activity.findViewById(android.R.id.content) as ViewGroup)
+private fun findViewRootForTest(activity: Activity): ViewRootForTest? {
+    return findViewRootForTest(activity.findViewById(android.R.id.content) as ViewGroup)
 }
 
-private fun findAndroidOwner(view: View): AndroidOwner? {
-    if (view is AndroidOwner) {
+private fun findViewRootForTest(view: View): ViewRootForTest? {
+    if (view is ViewRootForTest) {
         return view
     }
 
     if (view is ViewGroup) {
         for (i in 0 until view.childCount) {
-            val composeView = findAndroidOwner(view.getChildAt(i))
+            val composeView = findViewRootForTest(view.getChildAt(i))
             if (composeView != null) {
                 return composeView
             }
diff --git a/compose/ui/ui-geometry/api/current.txt b/compose/ui/ui-geometry/api/current.txt
index 750bfb4..ef2658a 100644
--- a/compose/ui/ui-geometry/api/current.txt
+++ b/compose/ui/ui-geometry/api/current.txt
@@ -31,6 +31,9 @@
     method @androidx.compose.runtime.Stable public static long lerp-LCIZJP8(long start, long stop, float fraction);
   }
 
+  public final class GeometryUtilsKt {
+  }
+
   public final class MutableRect {
     ctor public MutableRect(float left, float top, float right, float bottom);
     method public boolean contains-k-4lQ0M(long offset);
@@ -99,7 +102,11 @@
 
   public final class OffsetKt {
     method @androidx.compose.runtime.Stable public static inline long Offset(float x, float y);
+    method public static boolean isFinite-k-4lQ0M(long);
+    method public static boolean isSpecified-k-4lQ0M(long);
+    method public static inline boolean isUnspecified-k-4lQ0M(long);
     method @androidx.compose.runtime.Stable public static long lerp-tX6QBWo(long start, long stop, float fraction);
+    method public static inline long takeOrElse-Yy5JL0A(long, kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Offset> block);
   }
 
   @androidx.compose.runtime.Immutable public final class Rect {
@@ -263,7 +270,10 @@
 
   public final class SizeKt {
     method @androidx.compose.runtime.Stable public static long Size(float width, float height);
+    method public static inline boolean isSpecified-uvyYCjk(long);
+    method public static inline boolean isUnspecified-uvyYCjk(long);
     method @androidx.compose.runtime.Stable public static long lerp-3tf5JpU(long start, long stop, float fraction);
+    method public static inline long takeOrElse-GR1djXE(long, kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Size> block);
     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 750bfb4..ef2658a 100644
--- a/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
@@ -31,6 +31,9 @@
     method @androidx.compose.runtime.Stable public static long lerp-LCIZJP8(long start, long stop, float fraction);
   }
 
+  public final class GeometryUtilsKt {
+  }
+
   public final class MutableRect {
     ctor public MutableRect(float left, float top, float right, float bottom);
     method public boolean contains-k-4lQ0M(long offset);
@@ -99,7 +102,11 @@
 
   public final class OffsetKt {
     method @androidx.compose.runtime.Stable public static inline long Offset(float x, float y);
+    method public static boolean isFinite-k-4lQ0M(long);
+    method public static boolean isSpecified-k-4lQ0M(long);
+    method public static inline boolean isUnspecified-k-4lQ0M(long);
     method @androidx.compose.runtime.Stable public static long lerp-tX6QBWo(long start, long stop, float fraction);
+    method public static inline long takeOrElse-Yy5JL0A(long, kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Offset> block);
   }
 
   @androidx.compose.runtime.Immutable public final class Rect {
@@ -263,7 +270,10 @@
 
   public final class SizeKt {
     method @androidx.compose.runtime.Stable public static long Size(float width, float height);
+    method public static inline boolean isSpecified-uvyYCjk(long);
+    method public static inline boolean isUnspecified-uvyYCjk(long);
     method @androidx.compose.runtime.Stable public static long lerp-3tf5JpU(long start, long stop, float fraction);
+    method public static inline long takeOrElse-GR1djXE(long, kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Size> block);
     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 750bfb4..ef2658a 100644
--- a/compose/ui/ui-geometry/api/restricted_current.txt
+++ b/compose/ui/ui-geometry/api/restricted_current.txt
@@ -31,6 +31,9 @@
     method @androidx.compose.runtime.Stable public static long lerp-LCIZJP8(long start, long stop, float fraction);
   }
 
+  public final class GeometryUtilsKt {
+  }
+
   public final class MutableRect {
     ctor public MutableRect(float left, float top, float right, float bottom);
     method public boolean contains-k-4lQ0M(long offset);
@@ -99,7 +102,11 @@
 
   public final class OffsetKt {
     method @androidx.compose.runtime.Stable public static inline long Offset(float x, float y);
+    method public static boolean isFinite-k-4lQ0M(long);
+    method public static boolean isSpecified-k-4lQ0M(long);
+    method public static inline boolean isUnspecified-k-4lQ0M(long);
     method @androidx.compose.runtime.Stable public static long lerp-tX6QBWo(long start, long stop, float fraction);
+    method public static inline long takeOrElse-Yy5JL0A(long, kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Offset> block);
   }
 
   @androidx.compose.runtime.Immutable public final class Rect {
@@ -263,7 +270,10 @@
 
   public final class SizeKt {
     method @androidx.compose.runtime.Stable public static long Size(float width, float height);
+    method public static inline boolean isSpecified-uvyYCjk(long);
+    method public static inline boolean isUnspecified-uvyYCjk(long);
     method @androidx.compose.runtime.Stable public static long lerp-3tf5JpU(long start, long stop, float fraction);
+    method public static inline long takeOrElse-GR1djXE(long, kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Size> block);
     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/CornerRadius.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/CornerRadius.kt
index 44afb95..ef44e85 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/CornerRadius.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/CornerRadius.kt
@@ -20,7 +20,6 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.util.lerp
 import androidx.compose.ui.util.packFloats
-import androidx.compose.ui.util.toStringAsFixed
 import androidx.compose.ui.util.unpackFloat1
 import androidx.compose.ui.util.unpackFloat2
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/GeometryUtils.kt
similarity index 72%
copy from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
copy to compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/GeometryUtils.kt
index f9cb2fe..956b0f0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/GeometryUtils.kt
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.focus
+package androidx.compose.ui.geometry
 
-@RequiresOptIn("The Focus API is experimental and is likely to change in the future.")
-annotation class ExperimentalFocus
\ No newline at end of file
+import androidx.compose.ui.util.format
+
+// File of internal utility methods used for the geometry library
+internal fun Float.toStringAsFixed(digits: Int): String = "%.${digits}f".format(this)
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt
index a8ae2c9d..b00c587 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.geometry
 
 import androidx.compose.runtime.Stable
-import androidx.compose.ui.util.toStringAsFixed
 import kotlin.math.max
 import kotlin.math.min
 
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
index ab1beaa..bef6a9f 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
@@ -20,7 +20,6 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.util.lerp
 import androidx.compose.ui.util.packFloats
-import androidx.compose.ui.util.toStringAsFixed
 import androidx.compose.ui.util.unpackFloat1
 import androidx.compose.ui.util.unpackFloat2
 import kotlin.math.sqrt
@@ -64,11 +63,23 @@
 
     @Stable
     val x: Float
-        get() = unpackFloat1(packedValue)
+        get() {
+            // Explicitly compare against packed values to avoid auto-boxing of Size.Unspecified
+            check(this.packedValue != Size.Unspecified.packedValue) {
+                "Offset is unspecified"
+            }
+            return unpackFloat1(packedValue)
+        }
 
     @Stable
     val y: Float
-        get() = unpackFloat2(packedValue)
+        get() {
+            // Explicitly compare against packed values to avoid auto-boxing of Size.Unspecified
+            check(this.packedValue != Size.Unspecified.packedValue) {
+                "Offset is unspecified"
+            }
+            return unpackFloat2(packedValue)
+        }
 
     @Stable
     operator fun component1(): Float = x
@@ -221,4 +232,29 @@
         lerp(start.x, stop.x, fraction),
         lerp(start.y, stop.y, fraction)
     )
-}
\ No newline at end of file
+}
+
+/**
+ * True if both x and y values of the [Offset] are finite
+ */
+@Stable
+val Offset.isFinite: Boolean get() = x.isFinite() && y.isFinite()
+
+/**
+ * `false` when this is [Offset.Unspecified].
+ */
+@Stable
+val Offset.isSpecified: Boolean get() = packedValue != Offset.Unspecified.packedValue
+
+/**
+ * `true` when this is [Offset.Unspecified].
+ */
+@Stable
+inline val Offset.isUnspecified: Boolean get() = packedValue == Offset.Unspecified.packedValue
+
+/**
+ * If this [Offset] [isSpecified] then this is returned, otherwise [block] is executed
+ * and its result is returned.
+ */
+inline fun Offset.takeOrElse(block: () -> Offset): Offset =
+    if (isSpecified) this else block()
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt
index cd0a540..6c976ed 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt
@@ -19,7 +19,6 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.util.lerp
-import androidx.compose.ui.util.toStringAsFixed
 import kotlin.math.absoluteValue
 import kotlin.math.max
 import kotlin.math.min
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt
index 962191c..9e1484c 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.runtime.Immutable
 import androidx.compose.ui.util.lerp
-import androidx.compose.ui.util.toStringAsFixed
 import kotlin.math.absoluteValue
 import kotlin.math.max
 import kotlin.math.min
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 2df0db1..39a4b24 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
@@ -20,7 +20,6 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.util.lerp
 import androidx.compose.ui.util.packFloats
-import androidx.compose.ui.util.toStringAsFixed
 import androidx.compose.ui.util.unpackFloat1
 import androidx.compose.ui.util.unpackFloat2
 import kotlin.math.absoluteValue
@@ -139,6 +138,27 @@
 }
 
 /**
+ * `false` when this is [Size.Unspecified].
+ */
+@Stable
+inline val Size.isSpecified: Boolean
+    get() = packedValue != Size.Unspecified.packedValue
+
+/**
+ * `true` when this is [Size.Unspecified].
+ */
+@Stable
+inline val Size.isUnspecified: Boolean
+    get() = packedValue == Size.Unspecified.packedValue
+
+/**
+ * If this [Size] [isSpecified] then this is returned, otherwise [block] is executed
+ * and its result is returned.
+ */
+inline fun Size.takeOrElse(block: () -> Size): Size =
+    if (isSpecified) this else block()
+
+/**
  * Linearly interpolate between two sizes
  *
  * The [fraction] argument represents position on the timeline, with 0.0 meaning
diff --git a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/OffsetTest.kt b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/OffsetTest.kt
index 82eb717..5f252f00 100644
--- a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/OffsetTest.kt
+++ b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/OffsetTest.kt
@@ -17,6 +17,8 @@
 package androidx.compose.ui.geometry
 
 import org.junit.Assert
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -44,4 +46,74 @@
         Assert.assertEquals(100f, copy.x)
         Assert.assertEquals(300f, copy.y)
     }
+
+    @Test
+    fun testUnspecifiedWidthQueryThrows() {
+        try {
+            Offset.Unspecified.x
+            Assert.fail("Offset.Unspecified.x is not allowed")
+        } catch (t: Throwable) {
+            // no-op
+        }
+    }
+
+    @Test
+    fun testUnspecifiedHeightQueryThrows() {
+        try {
+            Offset.Unspecified.y
+            Assert.fail("Offset.Unspecified.y is not allowed")
+        } catch (t: Throwable) {
+            // no-op
+        }
+    }
+
+    @Test
+    fun testUnspecifiedCopyThrows() {
+        try {
+            Offset.Unspecified.copy(x = 100f)
+            Offset.Unspecified.copy(y = 70f)
+            Assert.fail("Offset.Unspecified.copy is not allowed")
+        } catch (t: Throwable) {
+            // no-op
+        }
+    }
+
+    @Test
+    fun testUnspecifiedComponentAssignmentThrows() {
+        try {
+            val (_, _) = Offset.Unspecified
+            Assert.fail("Size.Unspecified component assignment is not allowed")
+        } catch (t: Throwable) {
+            // no-op
+        }
+    }
+
+    @Test
+    fun testIsSpecified() {
+        val offset = Offset(10f, 20f)
+        assertTrue(offset.isSpecified)
+        assertFalse(offset.isUnspecified)
+    }
+
+    @Test
+    fun testIsUnspecified() {
+        assertTrue(Offset.Unspecified.isUnspecified)
+        assertFalse(Offset.Unspecified.isSpecified)
+    }
+
+    @Test
+    fun testUnspecifiedEquals() {
+        // Verify that verifying equality here does not crash
+        assertTrue(Offset.Unspecified == Offset.Unspecified)
+    }
+
+    @Test
+    fun testTakeOrElseTrue() {
+        assertTrue(Offset(1f, 1f).takeOrElse { Offset.Unspecified }.isSpecified)
+    }
+
+    @Test
+    fun testTakeOrElseFalse() {
+        assertTrue(Offset.Unspecified.takeOrElse { Offset(1f, 1f) }.isSpecified)
+    }
 }
\ No newline at end of file
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 35592f3..ada612e 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
@@ -139,4 +139,26 @@
         val size2 = Size(300f, 500f)
         Assert.assertEquals(Size(200f, 350f), lerp(size1, size2, 0.5f))
     }
+
+    @Test
+    fun testIsSpecified() {
+        Assert.assertFalse(Size.Unspecified.isSpecified)
+        Assert.assertTrue(Size(1f, 1f).isSpecified)
+    }
+
+    @Test
+    fun testIsUnspecified() {
+        Assert.assertTrue(Size.Unspecified.isUnspecified)
+        Assert.assertFalse(Size(1f, 1f).isUnspecified)
+    }
+
+    @Test
+    fun testTakeOrElseTrue() {
+        Assert.assertTrue(Size(1f, 1f).takeOrElse { Size.Unspecified }.isSpecified)
+    }
+
+    @Test
+    fun testTakeOrElseFalse() {
+        Assert.assertTrue(Size.Unspecified.takeOrElse { Size(1f, 1f) }.isSpecified)
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/api/current.txt b/compose/ui/ui-graphics/api/current.txt
index 2b56325..0a18e14 100644
--- a/compose/ui/ui-graphics/api/current.txt
+++ b/compose/ui/ui-graphics/api/current.txt
@@ -31,6 +31,7 @@
     method public androidx.compose.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.compose.ui.graphics.FilterQuality getFilterQuality();
     method public android.graphics.PathEffect? getNativePathEffect();
+    method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.compose.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.compose.ui.graphics.StrokeJoin getStrokeJoin();
@@ -45,6 +46,7 @@
     method public void setColorFilter(androidx.compose.ui.graphics.ColorFilter? value);
     method public void setFilterQuality(androidx.compose.ui.graphics.FilterQuality value);
     method public void setNativePathEffect(android.graphics.PathEffect? value);
+    method public void setPathEffect(androidx.compose.ui.graphics.PathEffect? value);
     method public void setShader(android.graphics.Shader? value);
     method public void setStrokeCap(androidx.compose.ui.graphics.StrokeCap value);
     method public void setStrokeJoin(androidx.compose.ui.graphics.StrokeJoin value);
@@ -58,6 +60,7 @@
     property public androidx.compose.ui.graphics.FilterQuality filterQuality;
     property public boolean isAntiAlias;
     property public android.graphics.PathEffect? nativePathEffect;
+    property public androidx.compose.ui.graphics.PathEffect? pathEffect;
     property public android.graphics.Shader? shader;
     property public androidx.compose.ui.graphics.StrokeCap strokeCap;
     property public androidx.compose.ui.graphics.StrokeJoin strokeJoin;
@@ -104,6 +107,11 @@
     property public boolean isEmpty;
   }
 
+  public final class AndroidPathEffectKt {
+    method public static android.graphics.PathEffect asAndroidPathEffect(androidx.compose.ui.graphics.PathEffect);
+    method public static androidx.compose.ui.graphics.PathEffect toComposePathEffect(android.graphics.PathEffect);
+  }
+
   public final class AndroidPathKt {
     method public static androidx.compose.ui.graphics.Path Path();
     method public static inline android.graphics.Path asAndroidPath(androidx.compose.ui.graphics.Path);
@@ -165,20 +173,36 @@
   }
 
   @androidx.compose.runtime.Immutable public abstract sealed class Brush {
-    method public abstract void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
+    method @Deprecated public void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
+    method public abstract void applyTo-TJof4Gw(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+    field public static final androidx.compose.ui.graphics.Brush.Companion Companion;
+  }
+
+  public static final class Brush.Companion {
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush horizontalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional float startX, optional float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush horizontalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional float startX, optional float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush linearGradient-7_sGemo(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long start, optional long end, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush linearGradient-K4jYFb0(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long start, optional long end, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush radialGradient-YU3LRu0(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long center, optional float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush radialGradient-g04MWJE(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long center, optional float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush sweepGradient-PvDSl28(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long center);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush sweepGradient-acbAMd8(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long center);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush verticalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional float startY, optional float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush verticalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional float startY, optional float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
   }
 
   public final class BrushKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-PvDSl28(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, long center);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-acbAMd8(java.util.List<androidx.compose.ui.graphics.Color> colors, long center);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method public static androidx.compose.ui.graphics.ShaderBrush ShaderBrush(android.graphics.Shader shader);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-PvDSl28(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, long center);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-acbAMd8(java.util.List<androidx.compose.ui.graphics.Color> colors, long center);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
   }
 
   public interface Canvas {
@@ -311,8 +335,9 @@
     method public static inline boolean isUnspecified-8_81llA(long);
     method @androidx.compose.runtime.Stable public static long lerp-m18UwgE(long start, long stop, @FloatRange(from=0.0, to=1.0) float fraction);
     method @androidx.compose.runtime.Stable public static float luminance-8_81llA(long);
+    method public static inline long takeOrElse-iYUlWp8(long, kotlin.jvm.functions.Function0<androidx.compose.ui.graphics.Color> block);
     method @ColorInt @androidx.compose.runtime.Stable public static int toArgb-8_81llA(long);
-    method public static inline long useOrElse-iYUlWp8(long, kotlin.jvm.functions.Function0<androidx.compose.ui.graphics.Color> block);
+    method @Deprecated public static inline long useOrElse-iYUlWp8(long, kotlin.jvm.functions.Function0<androidx.compose.ui.graphics.Color> block);
   }
 
   public final class DegreesKt {
@@ -358,6 +383,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class LinearGradient extends androidx.compose.ui.graphics.ShaderBrush {
+    method public android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public final inline class Matrix {
@@ -390,13 +416,17 @@
   }
 
   public abstract sealed class Outline {
+    method public abstract androidx.compose.ui.geometry.Rect getBounds();
+    property public abstract androidx.compose.ui.geometry.Rect bounds;
   }
 
   public static final class Outline.Generic extends androidx.compose.ui.graphics.Outline {
     ctor public Outline.Generic(androidx.compose.ui.graphics.Path path);
     method public androidx.compose.ui.graphics.Path component1();
     method public androidx.compose.ui.graphics.Outline.Generic copy(androidx.compose.ui.graphics.Path path);
+    method public androidx.compose.ui.geometry.Rect getBounds();
     method public androidx.compose.ui.graphics.Path getPath();
+    property public androidx.compose.ui.geometry.Rect bounds;
     property public final androidx.compose.ui.graphics.Path path;
   }
 
@@ -404,7 +434,9 @@
     ctor public Outline.Rectangle(androidx.compose.ui.geometry.Rect rect);
     method public androidx.compose.ui.geometry.Rect component1();
     method @androidx.compose.runtime.Immutable public androidx.compose.ui.graphics.Outline.Rectangle copy(androidx.compose.ui.geometry.Rect rect);
+    method public androidx.compose.ui.geometry.Rect getBounds();
     method public androidx.compose.ui.geometry.Rect getRect();
+    property public androidx.compose.ui.geometry.Rect bounds;
     property public final androidx.compose.ui.geometry.Rect rect;
   }
 
@@ -412,7 +444,9 @@
     ctor public Outline.Rounded(androidx.compose.ui.geometry.RoundRect roundRect);
     method public androidx.compose.ui.geometry.RoundRect component1();
     method @androidx.compose.runtime.Immutable public androidx.compose.ui.graphics.Outline.Rounded copy(androidx.compose.ui.geometry.RoundRect roundRect);
+    method public androidx.compose.ui.geometry.Rect getBounds();
     method public androidx.compose.ui.geometry.RoundRect getRoundRect();
+    property public androidx.compose.ui.geometry.Rect bounds;
     property public final androidx.compose.ui.geometry.RoundRect roundRect;
   }
 
@@ -430,7 +464,8 @@
     method public long getColor-0d7_KjU();
     method public androidx.compose.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.compose.ui.graphics.FilterQuality getFilterQuality();
-    method public android.graphics.PathEffect? getNativePathEffect();
+    method @Deprecated public android.graphics.PathEffect? getNativePathEffect();
+    method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.compose.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.compose.ui.graphics.StrokeJoin getStrokeJoin();
@@ -444,7 +479,8 @@
     method public void setColor-8_81llA(long p);
     method public void setColorFilter(androidx.compose.ui.graphics.ColorFilter? p);
     method public void setFilterQuality(androidx.compose.ui.graphics.FilterQuality p);
-    method public void setNativePathEffect(android.graphics.PathEffect? p);
+    method @Deprecated public void setNativePathEffect(android.graphics.PathEffect? p);
+    method public void setPathEffect(androidx.compose.ui.graphics.PathEffect? p);
     method public void setShader(android.graphics.Shader? p);
     method public void setStrokeCap(androidx.compose.ui.graphics.StrokeCap p);
     method public void setStrokeJoin(androidx.compose.ui.graphics.StrokeJoin p);
@@ -457,7 +493,8 @@
     property public abstract androidx.compose.ui.graphics.ColorFilter? colorFilter;
     property public abstract androidx.compose.ui.graphics.FilterQuality filterQuality;
     property public abstract boolean isAntiAlias;
-    property public abstract android.graphics.PathEffect? nativePathEffect;
+    property @Deprecated public abstract android.graphics.PathEffect? nativePathEffect;
+    property public abstract androidx.compose.ui.graphics.PathEffect? pathEffect;
     property public abstract android.graphics.Shader? shader;
     property public abstract androidx.compose.ui.graphics.StrokeCap strokeCap;
     property public abstract androidx.compose.ui.graphics.StrokeJoin strokeJoin;
@@ -511,6 +548,17 @@
     method public androidx.compose.ui.graphics.Path combine(androidx.compose.ui.graphics.PathOperation operation, androidx.compose.ui.graphics.Path path1, androidx.compose.ui.graphics.Path path2);
   }
 
+  public interface PathEffect {
+    field public static final androidx.compose.ui.graphics.PathEffect.Companion Companion;
+  }
+
+  public static final class PathEffect.Companion {
+    method public androidx.compose.ui.graphics.PathEffect chainPathEffect(androidx.compose.ui.graphics.PathEffect outer, androidx.compose.ui.graphics.PathEffect inner);
+    method public androidx.compose.ui.graphics.PathEffect cornerPathEffect(float radius);
+    method public androidx.compose.ui.graphics.PathEffect dashPathEffect(float[] intervals, optional float phase);
+    method public androidx.compose.ui.graphics.PathEffect stampedPathEffect(androidx.compose.ui.graphics.Path shape, float advance, float phase, androidx.compose.ui.graphics.StampedPathEffectStyle style);
+  }
+
   public enum PathFillType {
     enum_constant public static final androidx.compose.ui.graphics.PathFillType EvenOdd;
     enum_constant public static final androidx.compose.ui.graphics.PathFillType NonZero;
@@ -553,6 +601,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class RadialGradient extends androidx.compose.ui.graphics.ShaderBrush {
+    method public android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public final class RectHelperKt {
@@ -565,11 +614,10 @@
     method public static androidx.compose.ui.graphics.Shape getRectangleShape();
   }
 
-  @androidx.compose.runtime.Immutable public class ShaderBrush extends androidx.compose.ui.graphics.Brush {
-    ctor public ShaderBrush(android.graphics.Shader shader);
-    method public final void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
-    method public final android.graphics.Shader getShader();
-    property public final android.graphics.Shader shader;
+  @androidx.compose.runtime.Immutable public abstract class ShaderBrush extends androidx.compose.ui.graphics.Brush {
+    ctor public ShaderBrush();
+    method public final void applyTo-TJof4Gw(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+    method public abstract android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public final class ShaderKt {
@@ -607,11 +655,17 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SolidColor extends androidx.compose.ui.graphics.Brush {
-    method public void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
+    method public void applyTo-TJof4Gw(long size, androidx.compose.ui.graphics.Paint p, float alpha);
     method public long getValue-0d7_KjU();
     property public final long value;
   }
 
+  public enum StampedPathEffectStyle {
+    enum_constant public static final androidx.compose.ui.graphics.StampedPathEffectStyle Morph;
+    enum_constant public static final androidx.compose.ui.graphics.StampedPathEffectStyle Rotate;
+    enum_constant public static final androidx.compose.ui.graphics.StampedPathEffectStyle Translate;
+  }
+
   public enum StrokeCap {
     enum_constant public static final androidx.compose.ui.graphics.StrokeCap Butt;
     enum_constant public static final androidx.compose.ui.graphics.StrokeCap Round;
@@ -625,6 +679,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SweepGradient extends androidx.compose.ui.graphics.ShaderBrush {
+    method public android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public enum TileMode {
@@ -860,14 +915,14 @@
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, float radius, long center, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, long topLeft, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize, long dstOffset, long dstSize, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-1-s4MmQ(long color, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-QXZmVdc(long color, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-UXw4dv4(androidx.compose.ui.graphics.Brush brush, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-IdEHoqk(long color, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath(androidx.compose.ui.graphics.Path path, androidx.compose.ui.graphics.Brush brush, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath-tilSWAQ(androidx.compose.ui.graphics.Path path, long color, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints-8s8raUw(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints-Aqy9O-k(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-IdEHoqk(long color, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRoundRect-fNghmuc(long color, long topLeft, long size, long cornerRadius, androidx.compose.ui.graphics.drawscope.DrawStyle style, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
@@ -906,14 +961,14 @@
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, optional float radius, optional long center, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, optional long topLeft, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-1-s4MmQ(long color, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-QXZmVdc(long color, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-UXw4dv4(androidx.compose.ui.graphics.Brush brush, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-IdEHoqk(long color, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath(androidx.compose.ui.graphics.Path path, androidx.compose.ui.graphics.Brush brush, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath-tilSWAQ(androidx.compose.ui.graphics.Path path, long color, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints-8s8raUw(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints-Aqy9O-k(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-IdEHoqk(long color, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRoundRect-fNghmuc(long color, optional long topLeft, optional long size, optional long cornerRadius, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
@@ -982,23 +1037,23 @@
   }
 
   public final class Stroke extends androidx.compose.ui.graphics.drawscope.DrawStyle {
-    ctor public Stroke(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, android.graphics.PathEffect? pathEffect);
+    ctor public Stroke(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, androidx.compose.ui.graphics.PathEffect? pathEffect);
     ctor public Stroke();
     method public float component1();
     method public float component2();
     method public androidx.compose.ui.graphics.StrokeCap component3();
     method public androidx.compose.ui.graphics.StrokeJoin component4();
-    method public android.graphics.PathEffect? component5();
-    method public androidx.compose.ui.graphics.drawscope.Stroke copy(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, android.graphics.PathEffect? pathEffect);
+    method public androidx.compose.ui.graphics.PathEffect? component5();
+    method public androidx.compose.ui.graphics.drawscope.Stroke copy(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, androidx.compose.ui.graphics.PathEffect? pathEffect);
     method public androidx.compose.ui.graphics.StrokeCap getCap();
     method public androidx.compose.ui.graphics.StrokeJoin getJoin();
     method public float getMiter();
-    method public android.graphics.PathEffect? getPathEffect();
+    method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
     method public float getWidth();
     property public final androidx.compose.ui.graphics.StrokeCap cap;
     property public final androidx.compose.ui.graphics.StrokeJoin join;
     property public final float miter;
-    property public final android.graphics.PathEffect? pathEffect;
+    property public final androidx.compose.ui.graphics.PathEffect? pathEffect;
     property public final float width;
     field public static final androidx.compose.ui.graphics.drawscope.Stroke.Companion Companion;
     field public static final float DefaultMiter = 4.0f;
diff --git a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
index 2b56325..0a18e14 100644
--- a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
@@ -31,6 +31,7 @@
     method public androidx.compose.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.compose.ui.graphics.FilterQuality getFilterQuality();
     method public android.graphics.PathEffect? getNativePathEffect();
+    method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.compose.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.compose.ui.graphics.StrokeJoin getStrokeJoin();
@@ -45,6 +46,7 @@
     method public void setColorFilter(androidx.compose.ui.graphics.ColorFilter? value);
     method public void setFilterQuality(androidx.compose.ui.graphics.FilterQuality value);
     method public void setNativePathEffect(android.graphics.PathEffect? value);
+    method public void setPathEffect(androidx.compose.ui.graphics.PathEffect? value);
     method public void setShader(android.graphics.Shader? value);
     method public void setStrokeCap(androidx.compose.ui.graphics.StrokeCap value);
     method public void setStrokeJoin(androidx.compose.ui.graphics.StrokeJoin value);
@@ -58,6 +60,7 @@
     property public androidx.compose.ui.graphics.FilterQuality filterQuality;
     property public boolean isAntiAlias;
     property public android.graphics.PathEffect? nativePathEffect;
+    property public androidx.compose.ui.graphics.PathEffect? pathEffect;
     property public android.graphics.Shader? shader;
     property public androidx.compose.ui.graphics.StrokeCap strokeCap;
     property public androidx.compose.ui.graphics.StrokeJoin strokeJoin;
@@ -104,6 +107,11 @@
     property public boolean isEmpty;
   }
 
+  public final class AndroidPathEffectKt {
+    method public static android.graphics.PathEffect asAndroidPathEffect(androidx.compose.ui.graphics.PathEffect);
+    method public static androidx.compose.ui.graphics.PathEffect toComposePathEffect(android.graphics.PathEffect);
+  }
+
   public final class AndroidPathKt {
     method public static androidx.compose.ui.graphics.Path Path();
     method public static inline android.graphics.Path asAndroidPath(androidx.compose.ui.graphics.Path);
@@ -165,20 +173,36 @@
   }
 
   @androidx.compose.runtime.Immutable public abstract sealed class Brush {
-    method public abstract void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
+    method @Deprecated public void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
+    method public abstract void applyTo-TJof4Gw(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+    field public static final androidx.compose.ui.graphics.Brush.Companion Companion;
+  }
+
+  public static final class Brush.Companion {
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush horizontalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional float startX, optional float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush horizontalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional float startX, optional float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush linearGradient-7_sGemo(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long start, optional long end, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush linearGradient-K4jYFb0(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long start, optional long end, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush radialGradient-YU3LRu0(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long center, optional float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush radialGradient-g04MWJE(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long center, optional float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush sweepGradient-PvDSl28(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long center);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush sweepGradient-acbAMd8(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long center);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush verticalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional float startY, optional float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush verticalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional float startY, optional float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
   }
 
   public final class BrushKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-PvDSl28(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, long center);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-acbAMd8(java.util.List<androidx.compose.ui.graphics.Color> colors, long center);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method public static androidx.compose.ui.graphics.ShaderBrush ShaderBrush(android.graphics.Shader shader);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-PvDSl28(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, long center);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-acbAMd8(java.util.List<androidx.compose.ui.graphics.Color> colors, long center);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
   }
 
   public interface Canvas {
@@ -311,8 +335,9 @@
     method public static inline boolean isUnspecified-8_81llA(long);
     method @androidx.compose.runtime.Stable public static long lerp-m18UwgE(long start, long stop, @FloatRange(from=0.0, to=1.0) float fraction);
     method @androidx.compose.runtime.Stable public static float luminance-8_81llA(long);
+    method public static inline long takeOrElse-iYUlWp8(long, kotlin.jvm.functions.Function0<androidx.compose.ui.graphics.Color> block);
     method @ColorInt @androidx.compose.runtime.Stable public static int toArgb-8_81llA(long);
-    method public static inline long useOrElse-iYUlWp8(long, kotlin.jvm.functions.Function0<androidx.compose.ui.graphics.Color> block);
+    method @Deprecated public static inline long useOrElse-iYUlWp8(long, kotlin.jvm.functions.Function0<androidx.compose.ui.graphics.Color> block);
   }
 
   public final class DegreesKt {
@@ -358,6 +383,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class LinearGradient extends androidx.compose.ui.graphics.ShaderBrush {
+    method public android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public final inline class Matrix {
@@ -390,13 +416,17 @@
   }
 
   public abstract sealed class Outline {
+    method public abstract androidx.compose.ui.geometry.Rect getBounds();
+    property public abstract androidx.compose.ui.geometry.Rect bounds;
   }
 
   public static final class Outline.Generic extends androidx.compose.ui.graphics.Outline {
     ctor public Outline.Generic(androidx.compose.ui.graphics.Path path);
     method public androidx.compose.ui.graphics.Path component1();
     method public androidx.compose.ui.graphics.Outline.Generic copy(androidx.compose.ui.graphics.Path path);
+    method public androidx.compose.ui.geometry.Rect getBounds();
     method public androidx.compose.ui.graphics.Path getPath();
+    property public androidx.compose.ui.geometry.Rect bounds;
     property public final androidx.compose.ui.graphics.Path path;
   }
 
@@ -404,7 +434,9 @@
     ctor public Outline.Rectangle(androidx.compose.ui.geometry.Rect rect);
     method public androidx.compose.ui.geometry.Rect component1();
     method @androidx.compose.runtime.Immutable public androidx.compose.ui.graphics.Outline.Rectangle copy(androidx.compose.ui.geometry.Rect rect);
+    method public androidx.compose.ui.geometry.Rect getBounds();
     method public androidx.compose.ui.geometry.Rect getRect();
+    property public androidx.compose.ui.geometry.Rect bounds;
     property public final androidx.compose.ui.geometry.Rect rect;
   }
 
@@ -412,7 +444,9 @@
     ctor public Outline.Rounded(androidx.compose.ui.geometry.RoundRect roundRect);
     method public androidx.compose.ui.geometry.RoundRect component1();
     method @androidx.compose.runtime.Immutable public androidx.compose.ui.graphics.Outline.Rounded copy(androidx.compose.ui.geometry.RoundRect roundRect);
+    method public androidx.compose.ui.geometry.Rect getBounds();
     method public androidx.compose.ui.geometry.RoundRect getRoundRect();
+    property public androidx.compose.ui.geometry.Rect bounds;
     property public final androidx.compose.ui.geometry.RoundRect roundRect;
   }
 
@@ -430,7 +464,8 @@
     method public long getColor-0d7_KjU();
     method public androidx.compose.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.compose.ui.graphics.FilterQuality getFilterQuality();
-    method public android.graphics.PathEffect? getNativePathEffect();
+    method @Deprecated public android.graphics.PathEffect? getNativePathEffect();
+    method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.compose.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.compose.ui.graphics.StrokeJoin getStrokeJoin();
@@ -444,7 +479,8 @@
     method public void setColor-8_81llA(long p);
     method public void setColorFilter(androidx.compose.ui.graphics.ColorFilter? p);
     method public void setFilterQuality(androidx.compose.ui.graphics.FilterQuality p);
-    method public void setNativePathEffect(android.graphics.PathEffect? p);
+    method @Deprecated public void setNativePathEffect(android.graphics.PathEffect? p);
+    method public void setPathEffect(androidx.compose.ui.graphics.PathEffect? p);
     method public void setShader(android.graphics.Shader? p);
     method public void setStrokeCap(androidx.compose.ui.graphics.StrokeCap p);
     method public void setStrokeJoin(androidx.compose.ui.graphics.StrokeJoin p);
@@ -457,7 +493,8 @@
     property public abstract androidx.compose.ui.graphics.ColorFilter? colorFilter;
     property public abstract androidx.compose.ui.graphics.FilterQuality filterQuality;
     property public abstract boolean isAntiAlias;
-    property public abstract android.graphics.PathEffect? nativePathEffect;
+    property @Deprecated public abstract android.graphics.PathEffect? nativePathEffect;
+    property public abstract androidx.compose.ui.graphics.PathEffect? pathEffect;
     property public abstract android.graphics.Shader? shader;
     property public abstract androidx.compose.ui.graphics.StrokeCap strokeCap;
     property public abstract androidx.compose.ui.graphics.StrokeJoin strokeJoin;
@@ -511,6 +548,17 @@
     method public androidx.compose.ui.graphics.Path combine(androidx.compose.ui.graphics.PathOperation operation, androidx.compose.ui.graphics.Path path1, androidx.compose.ui.graphics.Path path2);
   }
 
+  public interface PathEffect {
+    field public static final androidx.compose.ui.graphics.PathEffect.Companion Companion;
+  }
+
+  public static final class PathEffect.Companion {
+    method public androidx.compose.ui.graphics.PathEffect chainPathEffect(androidx.compose.ui.graphics.PathEffect outer, androidx.compose.ui.graphics.PathEffect inner);
+    method public androidx.compose.ui.graphics.PathEffect cornerPathEffect(float radius);
+    method public androidx.compose.ui.graphics.PathEffect dashPathEffect(float[] intervals, optional float phase);
+    method public androidx.compose.ui.graphics.PathEffect stampedPathEffect(androidx.compose.ui.graphics.Path shape, float advance, float phase, androidx.compose.ui.graphics.StampedPathEffectStyle style);
+  }
+
   public enum PathFillType {
     enum_constant public static final androidx.compose.ui.graphics.PathFillType EvenOdd;
     enum_constant public static final androidx.compose.ui.graphics.PathFillType NonZero;
@@ -553,6 +601,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class RadialGradient extends androidx.compose.ui.graphics.ShaderBrush {
+    method public android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public final class RectHelperKt {
@@ -565,11 +614,10 @@
     method public static androidx.compose.ui.graphics.Shape getRectangleShape();
   }
 
-  @androidx.compose.runtime.Immutable public class ShaderBrush extends androidx.compose.ui.graphics.Brush {
-    ctor public ShaderBrush(android.graphics.Shader shader);
-    method public final void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
-    method public final android.graphics.Shader getShader();
-    property public final android.graphics.Shader shader;
+  @androidx.compose.runtime.Immutable public abstract class ShaderBrush extends androidx.compose.ui.graphics.Brush {
+    ctor public ShaderBrush();
+    method public final void applyTo-TJof4Gw(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+    method public abstract android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public final class ShaderKt {
@@ -607,11 +655,17 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SolidColor extends androidx.compose.ui.graphics.Brush {
-    method public void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
+    method public void applyTo-TJof4Gw(long size, androidx.compose.ui.graphics.Paint p, float alpha);
     method public long getValue-0d7_KjU();
     property public final long value;
   }
 
+  public enum StampedPathEffectStyle {
+    enum_constant public static final androidx.compose.ui.graphics.StampedPathEffectStyle Morph;
+    enum_constant public static final androidx.compose.ui.graphics.StampedPathEffectStyle Rotate;
+    enum_constant public static final androidx.compose.ui.graphics.StampedPathEffectStyle Translate;
+  }
+
   public enum StrokeCap {
     enum_constant public static final androidx.compose.ui.graphics.StrokeCap Butt;
     enum_constant public static final androidx.compose.ui.graphics.StrokeCap Round;
@@ -625,6 +679,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SweepGradient extends androidx.compose.ui.graphics.ShaderBrush {
+    method public android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public enum TileMode {
@@ -860,14 +915,14 @@
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, float radius, long center, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, long topLeft, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize, long dstOffset, long dstSize, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-1-s4MmQ(long color, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-QXZmVdc(long color, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-UXw4dv4(androidx.compose.ui.graphics.Brush brush, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-IdEHoqk(long color, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath(androidx.compose.ui.graphics.Path path, androidx.compose.ui.graphics.Brush brush, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath-tilSWAQ(androidx.compose.ui.graphics.Path path, long color, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints-8s8raUw(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints-Aqy9O-k(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-IdEHoqk(long color, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRoundRect-fNghmuc(long color, long topLeft, long size, long cornerRadius, androidx.compose.ui.graphics.drawscope.DrawStyle style, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
@@ -906,14 +961,14 @@
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, optional float radius, optional long center, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, optional long topLeft, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-1-s4MmQ(long color, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-QXZmVdc(long color, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-UXw4dv4(androidx.compose.ui.graphics.Brush brush, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-IdEHoqk(long color, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath(androidx.compose.ui.graphics.Path path, androidx.compose.ui.graphics.Brush brush, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath-tilSWAQ(androidx.compose.ui.graphics.Path path, long color, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints-8s8raUw(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints-Aqy9O-k(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-IdEHoqk(long color, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRoundRect-fNghmuc(long color, optional long topLeft, optional long size, optional long cornerRadius, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
@@ -982,23 +1037,23 @@
   }
 
   public final class Stroke extends androidx.compose.ui.graphics.drawscope.DrawStyle {
-    ctor public Stroke(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, android.graphics.PathEffect? pathEffect);
+    ctor public Stroke(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, androidx.compose.ui.graphics.PathEffect? pathEffect);
     ctor public Stroke();
     method public float component1();
     method public float component2();
     method public androidx.compose.ui.graphics.StrokeCap component3();
     method public androidx.compose.ui.graphics.StrokeJoin component4();
-    method public android.graphics.PathEffect? component5();
-    method public androidx.compose.ui.graphics.drawscope.Stroke copy(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, android.graphics.PathEffect? pathEffect);
+    method public androidx.compose.ui.graphics.PathEffect? component5();
+    method public androidx.compose.ui.graphics.drawscope.Stroke copy(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, androidx.compose.ui.graphics.PathEffect? pathEffect);
     method public androidx.compose.ui.graphics.StrokeCap getCap();
     method public androidx.compose.ui.graphics.StrokeJoin getJoin();
     method public float getMiter();
-    method public android.graphics.PathEffect? getPathEffect();
+    method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
     method public float getWidth();
     property public final androidx.compose.ui.graphics.StrokeCap cap;
     property public final androidx.compose.ui.graphics.StrokeJoin join;
     property public final float miter;
-    property public final android.graphics.PathEffect? pathEffect;
+    property public final androidx.compose.ui.graphics.PathEffect? pathEffect;
     property public final float width;
     field public static final androidx.compose.ui.graphics.drawscope.Stroke.Companion Companion;
     field public static final float DefaultMiter = 4.0f;
diff --git a/compose/ui/ui-graphics/api/restricted_current.txt b/compose/ui/ui-graphics/api/restricted_current.txt
index f6b1064..5abec76 100644
--- a/compose/ui/ui-graphics/api/restricted_current.txt
+++ b/compose/ui/ui-graphics/api/restricted_current.txt
@@ -61,6 +61,7 @@
     method public androidx.compose.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.compose.ui.graphics.FilterQuality getFilterQuality();
     method public android.graphics.PathEffect? getNativePathEffect();
+    method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.compose.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.compose.ui.graphics.StrokeJoin getStrokeJoin();
@@ -75,6 +76,7 @@
     method public void setColorFilter(androidx.compose.ui.graphics.ColorFilter? value);
     method public void setFilterQuality(androidx.compose.ui.graphics.FilterQuality value);
     method public void setNativePathEffect(android.graphics.PathEffect? value);
+    method public void setPathEffect(androidx.compose.ui.graphics.PathEffect? value);
     method public void setShader(android.graphics.Shader? value);
     method public void setStrokeCap(androidx.compose.ui.graphics.StrokeCap value);
     method public void setStrokeJoin(androidx.compose.ui.graphics.StrokeJoin value);
@@ -88,6 +90,7 @@
     property public androidx.compose.ui.graphics.FilterQuality filterQuality;
     property public boolean isAntiAlias;
     property public android.graphics.PathEffect? nativePathEffect;
+    property public androidx.compose.ui.graphics.PathEffect? pathEffect;
     property public android.graphics.Shader? shader;
     property public androidx.compose.ui.graphics.StrokeCap strokeCap;
     property public androidx.compose.ui.graphics.StrokeJoin strokeJoin;
@@ -134,6 +137,11 @@
     property public boolean isEmpty;
   }
 
+  public final class AndroidPathEffectKt {
+    method public static android.graphics.PathEffect asAndroidPathEffect(androidx.compose.ui.graphics.PathEffect);
+    method public static androidx.compose.ui.graphics.PathEffect toComposePathEffect(android.graphics.PathEffect);
+  }
+
   public final class AndroidPathKt {
     method public static androidx.compose.ui.graphics.Path Path();
     method public static inline android.graphics.Path asAndroidPath(androidx.compose.ui.graphics.Path);
@@ -195,20 +203,36 @@
   }
 
   @androidx.compose.runtime.Immutable public abstract sealed class Brush {
-    method public abstract void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
+    method @Deprecated public void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
+    method public abstract void applyTo-TJof4Gw(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+    field public static final androidx.compose.ui.graphics.Brush.Companion Companion;
+  }
+
+  public static final class Brush.Companion {
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush horizontalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional float startX, optional float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush horizontalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional float startX, optional float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush linearGradient-7_sGemo(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long start, optional long end, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush linearGradient-K4jYFb0(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long start, optional long end, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush radialGradient-YU3LRu0(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long center, optional float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush radialGradient-g04MWJE(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long center, optional float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush sweepGradient-PvDSl28(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long center);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush sweepGradient-acbAMd8(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long center);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush verticalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional float startY, optional float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush verticalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional float startY, optional float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
   }
 
   public final class BrushKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-PvDSl28(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, long center);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-acbAMd8(java.util.List<androidx.compose.ui.graphics.Color> colors, long center);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient HorizontalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float endX, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient LinearGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startX, float startY, float endX, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.RadialGradient RadialGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float centerX, float centerY, float radius, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method public static androidx.compose.ui.graphics.ShaderBrush ShaderBrush(android.graphics.Shader shader);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-PvDSl28(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, long center);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.SweepGradient SweepGradient-acbAMd8(java.util.List<androidx.compose.ui.graphics.Color> colors, long center);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.LinearGradient VerticalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, float startY, float endY, optional androidx.compose.ui.graphics.TileMode tileMode);
   }
 
   public interface Canvas {
@@ -342,8 +366,9 @@
     method public static inline boolean isUnspecified-8_81llA(long);
     method @androidx.compose.runtime.Stable public static long lerp-m18UwgE(long start, long stop, @FloatRange(from=0.0, to=1.0) float fraction);
     method @androidx.compose.runtime.Stable public static float luminance-8_81llA(long);
+    method public static inline long takeOrElse-iYUlWp8(long, kotlin.jvm.functions.Function0<androidx.compose.ui.graphics.Color> block);
     method @ColorInt @androidx.compose.runtime.Stable public static int toArgb-8_81llA(long);
-    method public static inline long useOrElse-iYUlWp8(long, kotlin.jvm.functions.Function0<androidx.compose.ui.graphics.Color> block);
+    method @Deprecated public static inline long useOrElse-iYUlWp8(long, kotlin.jvm.functions.Function0<androidx.compose.ui.graphics.Color> block);
   }
 
   public final class DegreesKt {
@@ -390,6 +415,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class LinearGradient extends androidx.compose.ui.graphics.ShaderBrush {
+    method public android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public final inline class Matrix {
@@ -422,13 +448,17 @@
   }
 
   public abstract sealed class Outline {
+    method public abstract androidx.compose.ui.geometry.Rect getBounds();
+    property public abstract androidx.compose.ui.geometry.Rect bounds;
   }
 
   public static final class Outline.Generic extends androidx.compose.ui.graphics.Outline {
     ctor public Outline.Generic(androidx.compose.ui.graphics.Path path);
     method public androidx.compose.ui.graphics.Path component1();
     method public androidx.compose.ui.graphics.Outline.Generic copy(androidx.compose.ui.graphics.Path path);
+    method public androidx.compose.ui.geometry.Rect getBounds();
     method public androidx.compose.ui.graphics.Path getPath();
+    property public androidx.compose.ui.geometry.Rect bounds;
     property public final androidx.compose.ui.graphics.Path path;
   }
 
@@ -436,7 +466,9 @@
     ctor public Outline.Rectangle(androidx.compose.ui.geometry.Rect rect);
     method public androidx.compose.ui.geometry.Rect component1();
     method @androidx.compose.runtime.Immutable public androidx.compose.ui.graphics.Outline.Rectangle copy(androidx.compose.ui.geometry.Rect rect);
+    method public androidx.compose.ui.geometry.Rect getBounds();
     method public androidx.compose.ui.geometry.Rect getRect();
+    property public androidx.compose.ui.geometry.Rect bounds;
     property public final androidx.compose.ui.geometry.Rect rect;
   }
 
@@ -444,7 +476,9 @@
     ctor public Outline.Rounded(androidx.compose.ui.geometry.RoundRect roundRect);
     method public androidx.compose.ui.geometry.RoundRect component1();
     method @androidx.compose.runtime.Immutable public androidx.compose.ui.graphics.Outline.Rounded copy(androidx.compose.ui.geometry.RoundRect roundRect);
+    method public androidx.compose.ui.geometry.Rect getBounds();
     method public androidx.compose.ui.geometry.RoundRect getRoundRect();
+    property public androidx.compose.ui.geometry.Rect bounds;
     property public final androidx.compose.ui.geometry.RoundRect roundRect;
   }
 
@@ -462,7 +496,8 @@
     method public long getColor-0d7_KjU();
     method public androidx.compose.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.compose.ui.graphics.FilterQuality getFilterQuality();
-    method public android.graphics.PathEffect? getNativePathEffect();
+    method @Deprecated public android.graphics.PathEffect? getNativePathEffect();
+    method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.compose.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.compose.ui.graphics.StrokeJoin getStrokeJoin();
@@ -476,7 +511,8 @@
     method public void setColor-8_81llA(long p);
     method public void setColorFilter(androidx.compose.ui.graphics.ColorFilter? p);
     method public void setFilterQuality(androidx.compose.ui.graphics.FilterQuality p);
-    method public void setNativePathEffect(android.graphics.PathEffect? p);
+    method @Deprecated public void setNativePathEffect(android.graphics.PathEffect? p);
+    method public void setPathEffect(androidx.compose.ui.graphics.PathEffect? p);
     method public void setShader(android.graphics.Shader? p);
     method public void setStrokeCap(androidx.compose.ui.graphics.StrokeCap p);
     method public void setStrokeJoin(androidx.compose.ui.graphics.StrokeJoin p);
@@ -489,7 +525,8 @@
     property public abstract androidx.compose.ui.graphics.ColorFilter? colorFilter;
     property public abstract androidx.compose.ui.graphics.FilterQuality filterQuality;
     property public abstract boolean isAntiAlias;
-    property public abstract android.graphics.PathEffect? nativePathEffect;
+    property @Deprecated public abstract android.graphics.PathEffect? nativePathEffect;
+    property public abstract androidx.compose.ui.graphics.PathEffect? pathEffect;
     property public abstract android.graphics.Shader? shader;
     property public abstract androidx.compose.ui.graphics.StrokeCap strokeCap;
     property public abstract androidx.compose.ui.graphics.StrokeJoin strokeJoin;
@@ -543,6 +580,17 @@
     method public androidx.compose.ui.graphics.Path combine(androidx.compose.ui.graphics.PathOperation operation, androidx.compose.ui.graphics.Path path1, androidx.compose.ui.graphics.Path path2);
   }
 
+  public interface PathEffect {
+    field public static final androidx.compose.ui.graphics.PathEffect.Companion Companion;
+  }
+
+  public static final class PathEffect.Companion {
+    method public androidx.compose.ui.graphics.PathEffect chainPathEffect(androidx.compose.ui.graphics.PathEffect outer, androidx.compose.ui.graphics.PathEffect inner);
+    method public androidx.compose.ui.graphics.PathEffect cornerPathEffect(float radius);
+    method public androidx.compose.ui.graphics.PathEffect dashPathEffect(float[] intervals, optional float phase);
+    method public androidx.compose.ui.graphics.PathEffect stampedPathEffect(androidx.compose.ui.graphics.Path shape, float advance, float phase, androidx.compose.ui.graphics.StampedPathEffectStyle style);
+  }
+
   public enum PathFillType {
     enum_constant public static final androidx.compose.ui.graphics.PathFillType EvenOdd;
     enum_constant public static final androidx.compose.ui.graphics.PathFillType NonZero;
@@ -585,6 +633,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class RadialGradient extends androidx.compose.ui.graphics.ShaderBrush {
+    method public android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public final class RectHelperKt {
@@ -597,11 +646,10 @@
     method public static androidx.compose.ui.graphics.Shape getRectangleShape();
   }
 
-  @androidx.compose.runtime.Immutable public class ShaderBrush extends androidx.compose.ui.graphics.Brush {
-    ctor public ShaderBrush(android.graphics.Shader shader);
-    method public final void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
-    method public final android.graphics.Shader getShader();
-    property public final android.graphics.Shader shader;
+  @androidx.compose.runtime.Immutable public abstract class ShaderBrush extends androidx.compose.ui.graphics.Brush {
+    ctor public ShaderBrush();
+    method public final void applyTo-TJof4Gw(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+    method public abstract android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public final class ShaderKt {
@@ -639,11 +687,17 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SolidColor extends androidx.compose.ui.graphics.Brush {
-    method public void applyTo(androidx.compose.ui.graphics.Paint p, float alpha);
+    method public void applyTo-TJof4Gw(long size, androidx.compose.ui.graphics.Paint p, float alpha);
     method public long getValue-0d7_KjU();
     property public final long value;
   }
 
+  public enum StampedPathEffectStyle {
+    enum_constant public static final androidx.compose.ui.graphics.StampedPathEffectStyle Morph;
+    enum_constant public static final androidx.compose.ui.graphics.StampedPathEffectStyle Rotate;
+    enum_constant public static final androidx.compose.ui.graphics.StampedPathEffectStyle Translate;
+  }
+
   public enum StrokeCap {
     enum_constant public static final androidx.compose.ui.graphics.StrokeCap Butt;
     enum_constant public static final androidx.compose.ui.graphics.StrokeCap Round;
@@ -657,6 +711,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SweepGradient extends androidx.compose.ui.graphics.ShaderBrush {
+    method public android.graphics.Shader createShader-uvyYCjk(long size);
   }
 
   public enum TileMode {
@@ -892,14 +947,14 @@
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, float radius, long center, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, long topLeft, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize, long dstOffset, long dstSize, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-1-s4MmQ(long color, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-QXZmVdc(long color, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-UXw4dv4(androidx.compose.ui.graphics.Brush brush, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-IdEHoqk(long color, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath(androidx.compose.ui.graphics.Path path, androidx.compose.ui.graphics.Brush brush, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath-tilSWAQ(androidx.compose.ui.graphics.Path path, long color, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints-8s8raUw(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints-Aqy9O-k(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-IdEHoqk(long color, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRoundRect-fNghmuc(long color, long topLeft, long size, long cornerRadius, androidx.compose.ui.graphics.drawscope.DrawStyle style, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
@@ -962,14 +1017,14 @@
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, optional float radius, optional long center, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, optional long topLeft, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-1-s4MmQ(long color, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-QXZmVdc(long color, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawLine-UXw4dv4(androidx.compose.ui.graphics.Brush brush, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-IdEHoqk(long color, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath(androidx.compose.ui.graphics.Path path, androidx.compose.ui.graphics.Brush brush, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawPath-tilSWAQ(androidx.compose.ui.graphics.Path path, long color, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawPoints-8s8raUw(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, androidx.compose.ui.graphics.Brush brush, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawPoints-Aqy9O-k(java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.PointMode pointMode, long color, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRect-IdEHoqk(long color, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawRoundRect-fNghmuc(long color, optional long topLeft, optional long size, optional long cornerRadius, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
@@ -1038,23 +1093,23 @@
   }
 
   public final class Stroke extends androidx.compose.ui.graphics.drawscope.DrawStyle {
-    ctor public Stroke(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, android.graphics.PathEffect? pathEffect);
+    ctor public Stroke(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, androidx.compose.ui.graphics.PathEffect? pathEffect);
     ctor public Stroke();
     method public float component1();
     method public float component2();
     method public androidx.compose.ui.graphics.StrokeCap component3();
     method public androidx.compose.ui.graphics.StrokeJoin component4();
-    method public android.graphics.PathEffect? component5();
-    method public androidx.compose.ui.graphics.drawscope.Stroke copy(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, android.graphics.PathEffect? pathEffect);
+    method public androidx.compose.ui.graphics.PathEffect? component5();
+    method public androidx.compose.ui.graphics.drawscope.Stroke copy(float width, float miter, androidx.compose.ui.graphics.StrokeCap cap, androidx.compose.ui.graphics.StrokeJoin join, androidx.compose.ui.graphics.PathEffect? pathEffect);
     method public androidx.compose.ui.graphics.StrokeCap getCap();
     method public androidx.compose.ui.graphics.StrokeJoin getJoin();
     method public float getMiter();
-    method public android.graphics.PathEffect? getPathEffect();
+    method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
     method public float getWidth();
     property public final androidx.compose.ui.graphics.StrokeCap cap;
     property public final androidx.compose.ui.graphics.StrokeJoin join;
     property public final float miter;
-    property public final android.graphics.PathEffect? pathEffect;
+    property public final androidx.compose.ui.graphics.PathEffect? pathEffect;
     property public final float width;
     field public static final androidx.compose.ui.graphics.drawscope.Stroke.Companion Companion;
     field public static final float DefaultMiter = 4.0f;
diff --git a/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/BrushSamples.kt b/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/BrushSamples.kt
new file mode 100644
index 0000000..4784e83
--- /dev/null
+++ b/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/BrushSamples.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.graphics.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+
+@Sampled
+@Composable
+fun GradientBrushSample() {
+    Column(modifier = Modifier.fillMaxSize().wrapContentSize()) {
+
+        // Create a linear gradient that shows red in the top left corner
+        // and blue in the bottom right corner
+        val linear = Brush.linearGradient(listOf(Color.Red, Color.Blue))
+
+        Box(modifier = Modifier.size(120.dp).background(linear))
+
+        Spacer(modifier = Modifier.size(20.dp))
+
+        // Create a radial gradient centered about the drawing area that is green on
+        // the outer
+        // edge of the circle and magenta towards the center of the circle
+        val radial = Brush.radialGradient(listOf(Color.Green, Color.Magenta))
+        Box(modifier = Modifier.size(120.dp).background(radial))
+
+        Spacer(modifier = Modifier.size(20.dp))
+
+        // Create a radial gradient centered about the drawing area that is green on
+        // the outer
+        // edge of the circle and magenta towards the center of the circle
+        val sweep = Brush.sweepGradient(listOf(Color.Cyan, Color.Magenta))
+        Box(modifier = Modifier.size(120.dp).background(sweep))
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/PathEffectSample.kt b/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/PathEffectSample.kt
new file mode 100644
index 0000000..09c5dcc
--- /dev/null
+++ b/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/PathEffectSample.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.graphics.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.PathEffect
+import androidx.compose.ui.graphics.StampedPathEffectStyle
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.unit.dp
+
+@Sampled
+@Composable
+fun StampedPathEffectSample() {
+    val size = 20f
+    val square = Path().apply {
+        lineTo(size, 0f)
+        lineTo(size, size)
+        lineTo(0f, size)
+        close()
+    }
+    Column(modifier = Modifier.fillMaxHeight().wrapContentSize(Alignment.Center)) {
+        val canvasModifier = Modifier.size(80.dp).align(Alignment.CenterHorizontally)
+
+        // StampedPathEffectStyle.Morph will modify the lines of the square to be curved to fit
+        // the curvature of the circle itself. Each stamped square will be rendered as an arc
+        // that is fully contained by the bounds of the circle itself
+        Canvas(modifier = canvasModifier) {
+            drawCircle(color = Color.Blue)
+            drawCircle(
+                color = Color.Red,
+                style = Stroke(
+                    pathEffect = PathEffect.stampedPathEffect(
+                        shape = square,
+                        style = StampedPathEffectStyle.Morph,
+                        phase = 0f,
+                        advance = 30f
+                    )
+                )
+            )
+        }
+
+        Spacer(modifier = Modifier.size(10.dp))
+
+        // StampedPathEffectStyle.Rotate will draw the square repeatedly around the circle
+        // such that each stamped square is centered on the circumference of the circle and is
+        // rotated along the curvature of the circle itself
+        Canvas(modifier = canvasModifier) {
+            drawCircle(color = Color.Blue)
+            drawCircle(
+                color = Color.Red,
+                style = Stroke(
+                    pathEffect = PathEffect.stampedPathEffect(
+                        shape = square,
+                        style = StampedPathEffectStyle.Rotate,
+                        phase = 0f,
+                        advance = 30f
+                    )
+                )
+            )
+        }
+
+        Spacer(modifier = Modifier.size(10.dp))
+
+        // StampedPathEffectStyle.Translate will draw the square repeatedly around the circle
+        // with the top left of each stamped square on the circumference of the circle
+        Canvas(modifier = canvasModifier) {
+            drawCircle(color = Color.Blue)
+            drawCircle(
+                color = Color.Red,
+                style = Stroke(
+                    pathEffect = PathEffect.stampedPathEffect(
+                        shape = square,
+                        style = StampedPathEffectStyle.Translate,
+                        phase = 0f,
+                        advance = 30f
+                    )
+                )
+            )
+        }
+    }
+}
\ 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 ecec048..c5bb9d6 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
@@ -17,8 +17,8 @@
 package androidx.compose.ui.graphics
 
 import android.content.Context
+import android.graphics.Bitmap
 import android.graphics.Canvas
-import android.graphics.Color
 import android.os.Build
 import android.util.AttributeSet
 import android.view.Gravity
@@ -61,14 +61,14 @@
         activityTestRule.runOnUiThread {
             val group = EnableDisableZViewGroup(drawLatch, activity)
             groupView = group
-            group.setBackgroundColor(Color.WHITE)
+            group.setBackgroundColor(android.graphics.Color.WHITE)
             group.layoutParams = ViewGroup.LayoutParams(12, 12)
             val child = View(activity)
             val childLayoutParams = FrameLayout.LayoutParams(10, 10)
             childLayoutParams.gravity = Gravity.TOP or Gravity.LEFT
             child.layoutParams = childLayoutParams
             child.elevation = 4f
-            child.setBackgroundColor(Color.WHITE)
+            child.setBackgroundColor(android.graphics.Color.WHITE)
             group.addView(child)
             activity.setContentView(group)
         }
@@ -78,9 +78,9 @@
         // the drawn content can get onto the screen before we capture the bitmap.
         activityTestRule.runOnUiThread { }
         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))
+        assertEquals(android.graphics.Color.WHITE, bitmap.getPixel(0, 0))
+        assertEquals(android.graphics.Color.WHITE, bitmap.getPixel(9, 9))
+        assertNotEquals(android.graphics.Color.WHITE, bitmap.getPixel(10, 10))
     }
 
     @Test
@@ -266,6 +266,215 @@
         assertEquals(bg, pixelMap[75, 76])
     }
 
+    @Test
+    fun testCornerPathEffect() {
+        val width = 80
+        val height = 80
+        val radius = 20f
+        val imageBitmap = ImageBitmap(width, height)
+        imageBitmap.asAndroidBitmap().eraseColor(android.graphics.Color.WHITE)
+        val canvas = Canvas(imageBitmap)
+        canvas.drawRect(
+            0f,
+            0f,
+            width.toFloat(),
+            height.toFloat(),
+            Paint().apply {
+                color = Color.Blue
+                pathEffect = PathEffect.cornerPathEffect(radius)
+            }
+        )
+
+        val androidBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+        androidBitmap.eraseColor(android.graphics.Color.WHITE)
+        val androidCanvas = android.graphics.Canvas(androidBitmap)
+        androidCanvas.drawRect(
+            0f,
+            0f,
+            width.toFloat(),
+            height.toFloat(),
+            android.graphics.Paint().apply {
+                isAntiAlias = true
+                color = android.graphics.Color.BLUE
+                pathEffect = android.graphics.CornerPathEffect(radius)
+            }
+        )
+
+        val composePixels = imageBitmap.toPixelMap()
+        for (i in 0 until 80) {
+            for (j in 0 until 80) {
+                assertEquals(
+                    "invalid color at i: " + i + ", " + j,
+                    composePixels[i, j].toArgb(),
+                    androidBitmap.getPixel(i, j)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testDashPathEffect() {
+        val width = 80
+        val height = 80
+        val imageBitmap = ImageBitmap(width, height)
+        imageBitmap.asAndroidBitmap().eraseColor(android.graphics.Color.WHITE)
+        val canvas = Canvas(imageBitmap)
+        canvas.drawRect(
+            0f,
+            0f,
+            width.toFloat(),
+            height.toFloat(),
+            Paint().apply {
+                color = Color.Blue
+                pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 5f), 8f)
+            }
+        )
+
+        val androidBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+        androidBitmap.eraseColor(android.graphics.Color.WHITE)
+        val androidCanvas = android.graphics.Canvas(androidBitmap)
+        androidCanvas.drawRect(
+            0f,
+            0f,
+            width.toFloat(),
+            height.toFloat(),
+            android.graphics.Paint().apply {
+                isAntiAlias = true
+                color = android.graphics.Color.BLUE
+                pathEffect = android.graphics.DashPathEffect(floatArrayOf(10f, 5f), 8f)
+            }
+        )
+
+        val composePixels = imageBitmap.toPixelMap()
+        for (i in 0 until 80) {
+            for (j in 0 until 80) {
+                assertEquals(
+                    "invalid color at i: " + i + ", " + j,
+                    composePixels[i, j].toArgb(),
+                    androidBitmap.getPixel(i, j)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testChainPathEffect() {
+        val width = 80
+        val height = 80
+        val imageBitmap = ImageBitmap(width, height)
+        imageBitmap.asAndroidBitmap().eraseColor(android.graphics.Color.WHITE)
+        val canvas = Canvas(imageBitmap)
+        canvas.drawRect(
+            0f,
+            0f,
+            width.toFloat(),
+            height.toFloat(),
+            Paint().apply {
+                color = Color.Blue
+                pathEffect =
+                    PathEffect.chainPathEffect(
+                        PathEffect.dashPathEffect(floatArrayOf(10f, 5f), 8f),
+                        PathEffect.cornerPathEffect(20f)
+                    )
+            }
+        )
+
+        val androidBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+        androidBitmap.eraseColor(android.graphics.Color.WHITE)
+        val androidCanvas = android.graphics.Canvas(androidBitmap)
+        androidCanvas.drawRect(
+            0f,
+            0f,
+            width.toFloat(),
+            height.toFloat(),
+            android.graphics.Paint().apply {
+                isAntiAlias = true
+                color = android.graphics.Color.BLUE
+                pathEffect =
+                    android.graphics.ComposePathEffect(
+                        android.graphics.DashPathEffect(floatArrayOf(10f, 5f), 8f),
+                        android.graphics.CornerPathEffect(20f)
+                    )
+            }
+        )
+
+        val composePixels = imageBitmap.toPixelMap()
+        for (i in 0 until 80) {
+            for (j in 0 until 80) {
+                assertEquals(
+                    "invalid color at i: " + i + ", " + j,
+                    composePixels[i, j].toArgb(),
+                    androidBitmap.getPixel(i, j)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testPathDashPathEffect() {
+        val width = 80
+        val height = 80
+        val imageBitmap = ImageBitmap(width, height)
+        imageBitmap.asAndroidBitmap().eraseColor(android.graphics.Color.WHITE)
+        val canvas = Canvas(imageBitmap)
+        canvas.drawRect(
+            0f,
+            0f,
+            width.toFloat(),
+            height.toFloat(),
+            Paint().apply {
+                color = Color.Blue
+                pathEffect =
+                    PathEffect.stampedPathEffect(
+                        Path().apply {
+                            lineTo(0f, 5f)
+                            lineTo(5f, 5f)
+                            close()
+                        },
+                        5f,
+                        2f,
+                        StampedPathEffectStyle.Rotate
+                    )
+            }
+        )
+
+        val androidBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+        androidBitmap.eraseColor(android.graphics.Color.WHITE)
+        val androidCanvas = android.graphics.Canvas(androidBitmap)
+        androidCanvas.drawRect(
+            0f,
+            0f,
+            width.toFloat(),
+            height.toFloat(),
+            android.graphics.Paint().apply {
+                isAntiAlias = true
+                color = android.graphics.Color.BLUE
+                pathEffect =
+                    android.graphics.PathDashPathEffect(
+                        android.graphics.Path().apply {
+                            lineTo(0f, 5f)
+                            lineTo(5f, 5f)
+                            close()
+                        },
+                        5f,
+                        2f,
+                        android.graphics.PathDashPathEffect.Style.ROTATE
+                    )
+            }
+        )
+
+        val composePixels = imageBitmap.toPixelMap()
+        for (i in 0 until 80) {
+            for (j in 0 until 80) {
+                assertEquals(
+                    "invalid color at i: " + i + ", " + j,
+                    composePixels[i, j].toArgb(),
+                    androidBitmap.getPixel(i, j)
+                )
+            }
+        }
+    }
+
     class EnableDisableZViewGroup @JvmOverloads constructor(
         val drawLatch: CountDownLatch,
         context: Context,
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/OutlineTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/OutlineTest.kt
new file mode 100644
index 0000000..d14fdaa
--- /dev/null
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/OutlineTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.graphics
+
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.RoundRect
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class OutlineTest {
+
+    @Test
+    fun testRectOutlineBounds() {
+        val outlineRect = Outline.Rectangle(Rect(1f, 2f, 3f, 4f))
+        assertEquals(Rect(1f, 2f, 3f, 4f), outlineRect.bounds)
+    }
+
+    @Test
+    fun testRoundRectOutlineBounds() {
+        val roundRectOutline = Outline.Rounded(
+            RoundRect(5f, 10f, 15f, 20f, CornerRadius(7f))
+        )
+        assertEquals(Rect(5f, 10f, 15f, 20f), roundRectOutline.bounds)
+    }
+
+    @Test
+    fun testPathOutlineBounds() {
+        val pathOutline = Outline.Generic(
+            Path().apply {
+                moveTo(5f, 15f)
+                lineTo(100f, 200f)
+                lineTo(0f, 200f)
+                close()
+            }
+        )
+        assertEquals(Rect(0f, 15f, 100f, 200f), pathOutline.bounds)
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt
index ec1b135..dd24cefd 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt
@@ -38,15 +38,14 @@
         val imageBitmap = ImageBitmap(100, 100)
         imageBitmap.drawInto {
             drawRect(
-                brush = LinearGradient(
+                brush = Brush.linearGradient(
                     0.0f to Color.Red,
                     0.5f to Color.Red,
                     0.5f to Color.Blue,
                     1.0f to Color.Blue,
-                    startX = 0.0f,
-                    startY = 0.0f,
-                    endX = 0.0f,
-                    endY = 100f
+                    start = Offset.Zero,
+                    end = Offset(0.0f, 100f),
+                    tileMode = TileMode.Clamp
                 )
             )
         }
@@ -68,14 +67,14 @@
 
         imageBitmap.drawInto {
             drawCircle(
-                brush = RadialGradient(
+                brush = Brush.radialGradient(
                     0.0f to Color.Red,
                     0.5f to Color.Red,
                     0.5f to Color.Blue,
                     1.0f to Color.Blue,
-                    centerX = 50f,
-                    centerY = 50f,
-                    radius = 50f
+                    center = Offset(50f, 50f),
+                    radius = 50f,
+                    tileMode = TileMode.Clamp
                 )
             )
         }
@@ -100,12 +99,12 @@
         val center = Offset(50f, 50f)
         imageBitmap.drawInto {
             drawRect(
-                brush = SweepGradient(
+                brush = Brush.sweepGradient(
                     0.0f to Color.Red,
                     0.5f to Color.Red,
                     0.5f to Color.Blue,
                     1.0f to Color.Blue,
-                    center = center,
+                    center = center
                 )
             )
         }
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeTest.kt
index b78b5f0..55730af 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeTest.kt
@@ -19,15 +19,20 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.ClipOp
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.LinearGradientShader
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.PointMode
+import androidx.compose.ui.graphics.RadialGradientShader
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.SweepGradientShader
+import androidx.compose.ui.graphics.TileMode
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.graphics.toPixelMap
 import androidx.compose.ui.unit.Density
@@ -972,6 +977,545 @@
         }
     }
 
+    @Test
+    fun testLinearGradient() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(Brush.linearGradient(listOf(Color.Red, Color.Green, Color.Blue)))
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = LinearGradientShader(
+                        Offset.Zero,
+                        Offset(100f, 100f),
+                        listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testLinearGradientBottomEnd() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.linearGradient(
+                        listOf(Color.Red, Color.Green, Color.Blue),
+                        end = Offset(0f, Float.POSITIVE_INFINITY)
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = LinearGradientShader(
+                        Offset.Zero,
+                        Offset(0f, 100f),
+                        listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testLinearGradientRightEnd() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.linearGradient(
+                        listOf(Color.Red, Color.Green, Color.Blue),
+                        end = Offset(Float.POSITIVE_INFINITY, 0f)
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = LinearGradientShader(
+                        Offset.Zero,
+                        Offset(100f, 0f),
+                        listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testLinearGradientBottomStart() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.linearGradient(
+                        listOf(Color.Red, Color.Green, Color.Blue),
+                        start = Offset(0f, Float.POSITIVE_INFINITY)
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = LinearGradientShader(
+                        Offset(0f, 100f),
+                        Offset(100f, 100f),
+                        listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testLinearGradientRightStart() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.linearGradient(
+                        listOf(Color.Red, Color.Green, Color.Blue),
+                        start = Offset(Float.POSITIVE_INFINITY, 0f)
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = LinearGradientShader(
+                        Offset(100f, 0f),
+                        Offset(100f, 100f),
+                        listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testLinearGradientWithStops() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.linearGradient(
+                        0.0f to Color.Red,
+                        0.1f to Color.Green,
+                        0.8f to Color.Blue,
+                        start = Offset(10.0f, 10f),
+                        tileMode = TileMode.Repeated
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = LinearGradientShader(
+                        Offset(10f, 10f),
+                        Offset(100f, 100f),
+                        colors = listOf(Color.Red, Color.Green, Color.Blue),
+                        colorStops = listOf(0.0f, 0.1f, 0.8f),
+                        tileMode = TileMode.Repeated
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testHorizontalGradient() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(Brush.horizontalGradient(listOf(Color.Red, Color.Green, Color.Blue)))
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = LinearGradientShader(
+                        Offset.Zero,
+                        Offset(100f, 0f),
+                        listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testHorizontalGradientWithStops() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.horizontalGradient(
+                        0.0f to Color.Red,
+                        0.1f to Color.Green,
+                        0.8f to Color.Blue,
+                        startX = 10f,
+                        tileMode = TileMode.Repeated
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = LinearGradientShader(
+                        Offset(10f, 0f),
+                        Offset(100f, 0f),
+                        colors = listOf(Color.Red, Color.Green, Color.Blue),
+                        colorStops = listOf(0.0f, 0.1f, 0.8f),
+                        tileMode = TileMode.Repeated
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testVerticalGradient() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(Brush.verticalGradient(listOf(Color.Red, Color.Green, Color.Blue)))
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = LinearGradientShader(
+                        Offset.Zero,
+                        Offset(0f, 100f),
+                        listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testVerticalGradientWithStops() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.verticalGradient(
+                        0.0f to Color.Red,
+                        0.1f to Color.Green,
+                        0.8f to Color.Blue,
+                        startY = 10f,
+                        tileMode = TileMode.Repeated
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = LinearGradientShader(
+                        Offset(0f, 10f),
+                        Offset(0f, 100f),
+                        colors = listOf(Color.Red, Color.Green, Color.Blue),
+                        colorStops = listOf(0.0f, 0.1f, 0.8f),
+                        tileMode = TileMode.Repeated
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testRadialGradient() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.radialGradient(listOf(Color.Red, Color.Green, Color.Blue))
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = RadialGradientShader(
+                        Offset(50f, 50f),
+                        50f,
+                        colors = listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testRadialGradientOutsideDrawingBounds() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                val offsetRadialGradient = Brush.radialGradient(
+                    listOf(Color.Red, Color.Blue),
+                    center = Offset(150f, 150f),
+                    radius = 50f
+                )
+                drawRect(offsetRadialGradient)
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = RadialGradientShader(
+                        Offset(150f, 150f),
+                        radius = 50f,
+                        colors = listOf(Color.Red, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testRadialGradientBottomRight() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                val offsetRadialGradient = Brush.radialGradient(
+                    listOf(Color.Red, Color.Blue),
+                    center = Offset.Infinite
+                )
+                drawRect(offsetRadialGradient)
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = RadialGradientShader(
+                        Offset(100f, 100f),
+                        radius = 50f,
+                        colors = listOf(Color.Red, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testRadialGradientRight() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                val offsetRadialGradient = Brush.radialGradient(
+                    listOf(Color.Red, Color.Blue),
+                    center = Offset(Float.POSITIVE_INFINITY, 0f)
+                )
+                drawRect(offsetRadialGradient)
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = RadialGradientShader(
+                        Offset(100f, 0f),
+                        radius = 50f,
+                        colors = listOf(Color.Red, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testRadialGradientBottom() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                val offsetRadialGradient = Brush.radialGradient(
+                    listOf(Color.Red, Color.Blue),
+                    center = Offset(0f, Float.POSITIVE_INFINITY)
+                )
+                drawRect(offsetRadialGradient)
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = RadialGradientShader(
+                        Offset(0f, 100f),
+                        radius = 50f,
+                        colors = listOf(Color.Red, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testRadialGradientWithStops() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.radialGradient(
+                        0.0f to Color.Red,
+                        0.1f to Color.Green,
+                        0.8f to Color.Blue,
+                        radius = 10f,
+                        tileMode = TileMode.Mirror
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = RadialGradientShader(
+                        Offset(50f, 50f),
+                        10f,
+                        colors = listOf(Color.Red, Color.Green, Color.Blue),
+                        colorStops = listOf(0.0f, 0.1f, 0.8f),
+                        tileMode = TileMode.Mirror
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testSweepGradient() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.sweepGradient(listOf(Color.Red, Color.Green, Color.Blue))
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = SweepGradientShader(
+                        Offset(50f, 50f),
+                        colors = listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testSweepGradientBottomRight() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.sweepGradient(
+                        listOf(Color.Red, Color.Green, Color.Blue),
+                        center = Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = SweepGradientShader(
+                        Offset(100f, 100f),
+                        colors = listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testSweepGradientBottom() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.sweepGradient(
+                        listOf(Color.Red, Color.Green, Color.Blue),
+                        center = Offset(0f, Float.POSITIVE_INFINITY)
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = SweepGradientShader(
+                        Offset(0f, 100f),
+                        colors = listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testSweepGradientRight() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.sweepGradient(
+                        listOf(Color.Red, Color.Green, Color.Blue),
+                        center = Offset(Float.POSITIVE_INFINITY, 0f)
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = SweepGradientShader(
+                        Offset(100f, 0f),
+                        colors = listOf(Color.Red, Color.Green, Color.Blue)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
+    @Test
+    fun testSweepGradientWithStops() {
+        testDrawScopeAndCanvasAreEquivalent(
+            100,
+            100,
+            {
+                drawRect(
+                    Brush.sweepGradient(
+                        0.0f to Color.Red,
+                        0.1f to Color.Green,
+                        0.8f to Color.Blue
+                    )
+                )
+            },
+            { canvas ->
+                val paint = Paint().apply {
+                    shader = SweepGradientShader(
+                        Offset(50f, 50f),
+                        colors = listOf(Color.Red, Color.Green, Color.Blue),
+                        colorStops = listOf(0.0f, 0.1f, 0.8f)
+                    )
+                }
+                canvas.drawRect(0f, 0f, 100f, 100f, paint)
+            }
+        )
+    }
+
     private inline fun testDrawTransformDefault(block: WrappedDrawTransform.() -> Unit) {
         val width = 100
         val height = 150
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPaint.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPaint.kt
index 2eca0e3..30b33a1 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPaint.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPaint.kt
@@ -108,7 +108,17 @@
             internalPaint.setNativeColorFilter(value)
         }
 
-    override var nativePathEffect: NativePathEffect? = null
+    override var nativePathEffect: NativePathEffect?
+        get() = pathEffect?.asAndroidPathEffect()
+        set(value) {
+            pathEffect = if (value == null) {
+                null
+            } else {
+                AndroidPathEffect(value)
+            }
+        }
+
+    override var pathEffect: PathEffect? = null
         set(value) {
             internalPaint.setNativePathEffect(value)
             field = value
@@ -235,6 +245,6 @@
     this.shader = value
 }
 
-internal fun NativePaint.setNativePathEffect(value: NativePathEffect?) {
-    this.pathEffect = value
+internal fun NativePaint.setNativePathEffect(value: PathEffect?) {
+    this.pathEffect = (value as AndroidPathEffect).nativePathEffect
 }
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPathEffect.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPathEffect.kt
new file mode 100644
index 0000000..4755271
--- /dev/null
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPathEffect.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.graphics
+
+import android.graphics.PathDashPathEffect
+
+/**
+ * Obtain a reference to the Android PathEffect type
+ */
+internal class AndroidPathEffect(val nativePathEffect: android.graphics.PathEffect) : PathEffect
+
+fun PathEffect.asAndroidPathEffect(): android.graphics.PathEffect =
+    (this as AndroidPathEffect).nativePathEffect
+
+fun android.graphics.PathEffect.toComposePathEffect(): PathEffect = AndroidPathEffect(this)
+
+internal actual fun actualCornerPathEffect(radius: Float): PathEffect =
+    AndroidPathEffect(android.graphics.CornerPathEffect(radius))
+
+internal actual fun actualDashPathEffect(intervals: FloatArray, phase: Float): PathEffect =
+    AndroidPathEffect(android.graphics.DashPathEffect(intervals, phase))
+
+internal actual fun actualChainPathEffect(outer: PathEffect, inner: PathEffect): PathEffect =
+    AndroidPathEffect(
+        android.graphics.ComposePathEffect(
+            (outer as AndroidPathEffect).nativePathEffect,
+            (inner as AndroidPathEffect).nativePathEffect
+        )
+    )
+
+internal actual fun actualStampedPathEffect(
+    shape: Path,
+    advance: Float,
+    phase: Float,
+    style: StampedPathEffectStyle
+): PathEffect =
+    AndroidPathEffect(
+        PathDashPathEffect(
+            shape.asAndroidPath(),
+            advance,
+            phase,
+            style.toAndroidPathDashPathEffectStyle()
+        )
+    )
+
+internal fun StampedPathEffectStyle.toAndroidPathDashPathEffectStyle() =
+    when (this) {
+        StampedPathEffectStyle.Morph -> PathDashPathEffect.Style.MORPH
+        StampedPathEffectStyle.Rotate -> PathDashPathEffect.Style.ROTATE
+        StampedPathEffectStyle.Translate -> PathDashPathEffect.Style.TRANSLATE
+    }
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt
index dfb9be7..b3597b8 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt
@@ -19,16 +19,387 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.util.nativeClass
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.isFinite
+import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.geometry.isUnspecified
+import androidx.compose.ui.unit.center
 
 @Immutable
 sealed class Brush {
-    abstract fun applyTo(p: Paint, alpha: Float)
+    @Deprecated(
+        "Use applyTo(size, paint, alpha) instead",
+        ReplaceWith("applyTo(size, p, alpha", "androidx.compose.ui.graphics")
+    )
+    open fun applyTo(p: Paint, alpha: Float) = applyTo(Size.Zero, p, alpha)
+
+    abstract fun applyTo(size: Size, p: Paint, alpha: Float)
+
+    companion object {
+
+        /**
+         * Creates a linear gradient with the provided colors along the given start and end
+         * coordinates. The colors are dispersed at the provided offset defined in the [ColorStop]
+         *
+         * ```
+         *  Brush.linearGradient(
+         *      0.0f to Color.Red,
+         *      0.3f to Color.Green,
+         *      1.0f to Color.Blue,
+         *      start = Offset(0.0f, 50.0f),
+         *      end = Offset(0.0f, 100.0f)
+         * )
+         * ```
+         *
+         * @see androidx.compose.ui.graphics.samples.GradientBrushSample
+         *
+         * @param colorStops Colors and their offset in the gradient area
+         * @param start Starting position of the linear gradient. This can be set to
+         * [Offset.Infinite] to position at the far right and bottom of the drawing area
+         * @param end Ending position of the linear gradient. This can be set to
+         * [Offset.Infinite] to position at the far right and bottom of the drawing area
+         * @param tileMode Determines the behavior for how the shader is to fill a region outside
+         * its bounds. Defaults to [TileMode.Clamp] to repeat the edge pixels
+         */
+        @Stable
+        fun linearGradient(
+            vararg colorStops: ColorStop,
+            start: Offset = Offset.Zero,
+            end: Offset = Offset.Infinite,
+            tileMode: TileMode = TileMode.Clamp
+        ): Brush = LinearGradient(
+            colors = List<Color>(colorStops.size) { i -> colorStops[i].second },
+            stops = List<Float>(colorStops.size) { i -> colorStops[i].first },
+            start = start,
+            end = end,
+            tileMode = tileMode
+        )
+
+        /**
+         * Creates a linear gradient with the provided colors along the given start and end coordinates.
+         * The colors are
+         *
+         * ```
+         *  Brush.linearGradient(
+         *      listOf(Color.Red, Color.Green, Color.Blue),
+         *      start = Offset(0.0f, 50.0f)
+         *      end = Offset(0.0f, 100.0f)
+         * )
+         * ```
+         *
+         * @sample androidx.compose.ui.graphics.samples.GradientBrushSample
+         *
+         * @param colors Colors to be rendered as part of the gradient
+         * @param start Starting position of the linear gradient. This can be set to
+         * [Offset.Infinite] to position at the far right and bottom of the drawing area
+         * @param end Ending position of the linear gradient. This can be set to
+         * [Offset.Infinite] to position at the far right and bottom of the drawing area
+         * @param tileMode Determines the behavior for how the shader is to fill a region outside
+         * its bounds. Defaults to [TileMode.Clamp] to repeat the edge pixels
+         */
+        @Stable
+        fun linearGradient(
+            colors: List<Color>,
+            start: Offset = Offset.Zero,
+            end: Offset = Offset.Infinite,
+            tileMode: TileMode = TileMode.Clamp
+        ): Brush = LinearGradient(
+            colors = colors,
+            stops = null,
+            start = start,
+            end = end,
+            tileMode = tileMode
+        )
+
+        /**
+         * Creates a horizontal gradient with the given colors evenly dispersed within the gradient
+         *
+         * Ex:
+         * ```
+         *  Brush.horizontalGradient(
+         *      listOf(Color.Red, Color.Green, Color.Blue),
+         *      startX = 10.0f,
+         *      endX = 20.0f
+         * )
+         * ```
+         *
+         * @sample androidx.compose.ui.graphics.samples.GradientBrushSample
+         *
+         * @param colors colors Colors to be rendered as part of the gradient
+         * @param startX Starting x position of the horizontal gradient. Defaults to 0 which
+         * represents the left of the drawing area
+         * @param endX Ending x position of the horizontal gradient.
+         * Defaults to [Float.POSITIVE_INFINITY] which indicates the right of the specified
+         * drawing area
+         * @param tileMode Determines the behavior for how the shader is to fill a region outside
+         * its bounds. Defaults to [TileMode.Clamp] to repeat the edge pixels
+         */
+        @Stable
+        fun horizontalGradient(
+            colors: List<Color>,
+            startX: Float = 0.0f,
+            endX: Float = Float.POSITIVE_INFINITY,
+            tileMode: TileMode = TileMode.Clamp
+        ): Brush = linearGradient(colors, Offset(startX, 0.0f), Offset(endX, 0.0f), tileMode)
+
+        /**
+         * Creates a horizontal gradient with the given colors dispersed at the provided offset
+         * defined in the [ColorStop]
+         *
+         * Ex:
+         * ```
+         *  Brush.horizontalGradient(
+         *      0.0f to Color.Red,
+         *      0.3f to Color.Green,
+         *      1.0f to Color.Blue,
+         *      startX = 0.0f,
+         *      endX = 100.0f
+         * )
+         * ```
+         *
+         * @sample androidx.compose.ui.graphics.samples.GradientBrushSample
+         *
+         * @param colorStops Colors and offsets to determine how the colors are dispersed throughout
+         * the vertical gradient
+         * @param startX Starting x position of the horizontal gradient. Defaults to 0 which
+         * represents the left of the drawing area
+         * @param endX Ending x position of the horizontal gradient.
+         * Defaults to [Float.POSITIVE_INFINITY] which indicates the right of the specified
+         * drawing area
+         * @param tileMode Determines the behavior for how the shader is to fill a region outside
+         * its bounds. Defaults to [TileMode.Clamp] to repeat the edge pixels
+         */
+        @Stable
+        fun horizontalGradient(
+            vararg colorStops: ColorStop,
+            startX: Float = 0.0f,
+            endX: Float = Float.POSITIVE_INFINITY,
+            tileMode: TileMode = TileMode.Clamp
+        ): Brush = linearGradient(
+            *colorStops,
+            start = Offset(startX, 0.0f),
+            end = Offset(endX, 0.0f),
+            tileMode = tileMode
+        )
+
+        /**
+         * Creates a vertical gradient with the given colors evenly dispersed within the gradient
+         * Ex:
+         * ```
+         *  Brush.verticalGradient(
+         *      listOf(Color.Red, Color.Green, Color.Blue),
+         *      startY = 0.0f,
+         *      endY = 100.0f
+         * )
+         *
+         * ```
+         *
+         * @sample androidx.compose.ui.graphics.samples.GradientBrushSample
+         *
+         * @param colors colors Colors to be rendered as part of the gradient
+         * @param startY Starting y position of the vertical gradient. Defaults to 0 which
+         * represents the top of the drawing area
+         * @param endY Ending y position of the vertical gradient.
+         * Defaults to [Float.POSITIVE_INFINITY] which indicates the bottom of the specified
+         * drawing area
+         * @param tileMode Determines the behavior for how the shader is to fill a region outside
+         * its bounds. Defaults to [TileMode.Clamp] to repeat the edge pixels
+         */
+        @Stable
+        fun verticalGradient(
+            colors: List<Color>,
+            startY: Float = 0.0f,
+            endY: Float = Float.POSITIVE_INFINITY,
+            tileMode: TileMode = TileMode.Clamp
+        ): Brush = linearGradient(colors, Offset(0.0f, startY), Offset(0.0f, endY), tileMode)
+
+        /**
+         * Creates a vertical gradient with the given colors at the provided offset defined
+         * in the [ColorStop]
+         *
+         * Ex:
+         * ```
+         *  Brush.verticalGradient(
+         *      0.1f to Color.Red,
+         *      0.3f to Color.Green,
+         *      0.5f to Color.Blue,
+         *      startY = 0.0f,
+         *      endY = 100.0f
+         * )
+         * ```
+         *
+         * @sample androidx.compose.ui.graphics.samples.GradientBrushSample
+         *
+         * @param colorStops Colors and offsets to determine how the colors are dispersed throughout
+         * the vertical gradient
+         * @param startY Starting y position of the vertical gradient. Defaults to 0 which
+         * represents the top of the drawing area
+         * @param endY Ending y position of the vertical gradient.
+         * Defaults to [Float.POSITIVE_INFINITY] which indicates the bottom of the specified
+         * drawing area
+         * @param tileMode Determines the behavior for how the shader is to fill a region outside
+         * its bounds. Defaults to [TileMode.Clamp] to repeat the edge pixels
+         */
+        @Stable
+        fun verticalGradient(
+            vararg colorStops: ColorStop,
+            startY: Float = 0f,
+            endY: Float = Float.POSITIVE_INFINITY,
+            tileMode: TileMode = TileMode.Clamp
+        ): Brush = linearGradient(
+            *colorStops,
+            start = Offset(0.0f, startY),
+            end = Offset(0.0f, endY),
+            tileMode = tileMode
+        )
+
+        /**
+         * Creates a radial gradient with the given colors at the provided offset
+         * defined in the [ColorStop]
+         * ```
+         * Brush.radialGradient(
+         *      0.0f to Color.Red,
+         *      0.3f to Color.Green,
+         *      1.0f to Color.Blue,
+         *      center = Offset(side1 / 2.0f, side2 / 2.0f),
+         *      radius = side1 / 2.0f,
+         *      tileMode = TileMode.Repeated
+         * )
+         * ```
+         *
+         * @sample androidx.compose.ui.graphics.samples.GradientBrushSample
+         *
+         * @param colorStops Colors and offsets to determine how the colors are dispersed throughout
+         * the radial gradient
+         * @param center Center position of the radial gradient circle. If this is set to
+         * [Offset.Unspecified] then the center of the drawing area is used as the center for
+         * the radial gradient. [Float.POSITIVE_INFINITY] can be used for either [Offset.x] or
+         * [Offset.y] to indicate the far right or far bottom of the drawing area respectively.
+         * @param radius Radius for the radial gradient. Defaults to positive infinity to indicate
+         * the largest radius that can fit within the bounds of the drawing area
+         * @param tileMode Determines the behavior for how the shader is to fill a region outside
+         * its bounds. Defaults to [TileMode.Clamp] to repeat the edge pixels
+         */
+        @Stable
+        fun radialGradient(
+            vararg colorStops: ColorStop,
+            center: Offset = Offset.Unspecified,
+            radius: Float = Float.POSITIVE_INFINITY,
+            tileMode: TileMode = TileMode.Clamp
+        ): Brush = RadialGradient(
+            colors = List<Color>(colorStops.size) { i -> colorStops[i].second },
+            stops = List<Float>(colorStops.size) { i -> colorStops[i].first },
+            center = center,
+            radius = radius,
+            tileMode = tileMode
+        )
+
+        /**
+         * Creates a radial gradient with the given colors evenly dispersed within the gradient
+         * ```
+         * Brush.radialGradient(
+         *      listOf(Color.Red, Color.Green, Color.Blue),
+         *      centerX = side1 / 2.0f,
+         *      centerY = side2 / 2.0f,
+         *      radius = side1 / 2.0f,
+         *      tileMode = TileMode.Repeated
+         * )
+         * ```
+         *
+         * @sample androidx.compose.ui.graphics.samples.GradientBrushSample
+         *
+         * @param colors Colors to be rendered as part of the gradient
+         * @param center Center position of the radial gradient circle. If this is set to
+         * [Offset.Unspecified] then the center of the drawing area is used as the center for
+         * the radial gradient. [Float.POSITIVE_INFINITY] can be used for either [Offset.x] or
+         * [Offset.y] to indicate the far right or far bottom of the drawing area respectively.
+         * @param radius Radius for the radial gradient. Defaults to positive infinity to indicate
+         * the largest radius that can fit within the bounds of the drawing area
+         * @param tileMode Determines the behavior for how the shader is to fill a region outside
+         * its bounds. Defaults to [TileMode.Clamp] to repeat the edge pixels
+         */
+        @Stable
+        fun radialGradient(
+            colors: List<Color>,
+            center: Offset = Offset.Unspecified,
+            radius: Float = Float.POSITIVE_INFINITY,
+            tileMode: TileMode = TileMode.Clamp
+        ): Brush = RadialGradient(
+            colors = colors,
+            stops = null,
+            center = center,
+            radius = radius,
+            tileMode = tileMode
+        )
+
+        /**
+         * Creates a sweep gradient with the given colors dispersed around the center with
+         * offsets defined in each [ColorStop]. The sweep begins relative to 3 o'clock and continues
+         * clockwise until it reaches the starting position again.
+         *
+         * Ex:
+         * ```
+         *  Brush.sweepGradient(
+         *      0.0f to Color.Red,
+         *      0.3f to Color.Green,
+         *      1.0f to Color.Blue,
+         *      center = Offset(0.0f, 100.0f)
+         * )
+         * ```
+         *
+         * @sample androidx.compose.ui.graphics.samples.GradientBrushSample
+         *
+         * @param colorStops Colors and offsets to determine how the colors are dispersed throughout
+         * the sweep gradient
+         * @param center Center position of the sweep gradient circle. If this is set to
+         * [Offset.Unspecified] then the center of the drawing area is used as the center for
+         * the sweep gradient
+         */
+        @Stable
+        fun sweepGradient(
+            vararg colorStops: ColorStop,
+            center: Offset = Offset.Unspecified
+        ): Brush = SweepGradient(
+            colors = List<Color>(colorStops.size) { i -> colorStops[i].second },
+            stops = List<Float>(colorStops.size) { i -> colorStops[i].first },
+            center = center
+        )
+
+        /**
+         * Creates a sweep gradient with the given colors dispersed evenly around the center.
+         * The sweep begins relative to 3 o'clock and continues clockwise until it reaches the
+         * starting position again.
+         *
+         * Ex:
+         * ```
+         *  Brush.sweepGradient(
+         *      listOf(Color.Red, Color.Green, Color.Blue),
+         *      center = Offset(10.0f, 20.0f)
+         * )
+         * ```
+         *
+         * @sample androidx.compose.ui.graphics.samples.GradientBrushSample
+         *
+         * @param colors List of colors to fill the sweep gradient
+         * @param center Center position of the sweep gradient circle. If this is set to
+         * [Offset.Unspecified] then the center of the drawing area is used as the center for
+         * the sweep gradient
+         */
+        @Stable
+        fun sweepGradient(
+            colors: List<Color>,
+            center: Offset = Offset.Unspecified
+        ): Brush = SweepGradient(
+            colors = colors,
+            stops = null,
+            center = center
+        )
+    }
 }
 
 @Immutable
 class SolidColor(val value: Color) : Brush() {
-    override fun applyTo(p: Paint, alpha: Float) {
+    override fun applyTo(size: Size, p: Paint, alpha: Float) {
         p.alpha = DefaultAlpha
         p.color = if (alpha != DefaultAlpha) {
             value.copy(alpha = value.alpha * alpha)
@@ -40,10 +411,7 @@
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (nativeClass() != other?.nativeClass()) return false
-
-        other as SolidColor
-
+        if (other !is SolidColor) return false
         if (value != other.value) return false
 
         return true
@@ -74,6 +442,16 @@
  * )
  * ```
  */
+@Deprecated(
+    "Use Brush.linearGradient instead",
+    ReplaceWith(
+        "Brush.linearGradient(colors = colors," +
+            "start = Offset(startX, startY), " +
+            "end = Offset(endX, endY), " +
+            "tileMode = tileMode)",
+        "androidx.compose.ui.ui.graphics"
+    )
+)
 @Stable
 fun LinearGradient(
     colors: List<Color>,
@@ -85,10 +463,8 @@
 ) = LinearGradient(
     colors,
     null,
-    startX,
-    startY,
-    endX,
-    endY,
+    Offset(startX, startY),
+    Offset(endX, endY),
     tileMode
 )
 
@@ -108,6 +484,16 @@
  * )
  * ```
  */
+@Deprecated(
+    "Use Brush.linearGradient instead",
+    ReplaceWith(
+        "Brush.linearGradient(colorStops, " +
+            "start = Offset(startX, startY), " +
+            "end = Offset(endX, endY), " +
+            "tileMode = tileMode)",
+        "androidx.compose.ui.ui.graphics"
+    )
+)
 @Stable
 fun LinearGradient(
     vararg colorStops: ColorStop,
@@ -119,10 +505,8 @@
 ) = LinearGradient(
     List<Color>(colorStops.size) { i -> colorStops[i].second },
     List<Float>(colorStops.size) { i -> colorStops[i].first },
-    startX,
-    startY,
-    endX,
-    endY,
+    Offset(startX, startY),
+    Offset(endX, endY),
     tileMode
 )
 
@@ -140,6 +524,18 @@
  * )
  * ```
  */
+@Deprecated(
+    "Use Brush.radialGradient instead",
+    ReplaceWith(
+        "Brush.radialGradient(" +
+            "colorStops, " +
+            "center = Offset(centerX, centerY)," +
+            "radius = radius, " +
+            "tileMode = tileMode" +
+            ")",
+        "androidx.compose.ui.ui.graphics"
+    )
+)
 @Stable
 fun RadialGradient(
     vararg colorStops: ColorStop,
@@ -150,8 +546,7 @@
 ) = RadialGradient(
     List<Color>(colorStops.size) { i -> colorStops[i].second },
     List<Float>(colorStops.size) { i -> colorStops[i].first },
-    centerX,
-    centerY,
+    Offset(centerX, centerY),
     radius,
     tileMode
 )
@@ -168,6 +563,13 @@
  * )
  * ```
  */
+@Deprecated(
+    "Use Brush.radialGradient instead",
+    ReplaceWith(
+        "Brush.radialGradient(colors, Offset(centerX, centerY), radius, tileMode)",
+        "androidx.compose.ui.ui.graphics"
+    )
+)
 @Stable
 fun RadialGradient(
     colors: List<Color>,
@@ -175,7 +577,7 @@
     centerY: Float,
     radius: Float,
     tileMode: TileMode = TileMode.Clamp
-) = RadialGradient(colors, null, centerX, centerY, radius, tileMode)
+) = RadialGradient(colors, null, Offset(centerX, centerY), radius, tileMode)
 
 /**
  * Creates a vertical gradient with the given colors evenly dispersed within the gradient
@@ -189,6 +591,13 @@
  *
  * ```
  */
+@Deprecated(
+    "Use Brush.verticalGradient instead",
+    ReplaceWith(
+        "Brush.verticalGradient(colors, startY, endY, tileMode)",
+        "androidx.compose.ui.ui.graphics"
+    )
+)
 @Stable
 fun VerticalGradient(
     colors: List<Color>,
@@ -198,10 +607,8 @@
 ) = LinearGradient(
     colors,
     null,
-    startX = 0.0f,
-    startY = startY,
-    endX = 0.0f,
-    endY = endY,
+    start = Offset(0.0f, startY),
+    end = Offset(0.0f, endY),
     tileMode = tileMode
 )
 
@@ -218,6 +625,13 @@
  * )
  * ```
  */
+@Deprecated(
+    "Use Brush.verticalGradient instead",
+    ReplaceWith(
+        "Brush.verticalGradient(colorStops, startY = startY, endY = endY, tileMode = tileMode)",
+        "androidx.compose.ui.ui.graphics"
+    )
+)
 @Stable
 fun VerticalGradient(
     vararg colorStops: ColorStop,
@@ -227,10 +641,8 @@
 ) = LinearGradient(
     List<Color>(colorStops.size) { i -> colorStops[i].second },
     List<Float>(colorStops.size) { i -> colorStops[i].first },
-    startX = 0.0f,
-    startY = startY,
-    endX = 0.0f,
-    endY = endY,
+    start = Offset(0.0f, startY),
+    end = Offset(0.0f, endY),
     tileMode = tileMode
 )
 
@@ -246,6 +658,13 @@
  * )
  * ```
  */
+@Deprecated(
+    "Use Brush.horizontalGradient instead",
+    ReplaceWith(
+        "Brush.horizontalGradient(colors, startX, endX, tileMode)",
+        "androidx.compose.ui.ui.graphics"
+    )
+)
 @Stable
 fun HorizontalGradient(
     colors: List<Color>,
@@ -255,10 +674,8 @@
 ) = LinearGradient(
     colors,
     null,
-    startX = startX,
-    startY = 0.0f,
-    endX = endX,
-    endY = 0.0f,
+    start = Offset(startX, 0.0f),
+    end = Offset(endX, 0.0f),
     tileMode = tileMode
 )
 
@@ -276,6 +693,14 @@
  * )
  * ```
  */
+@Deprecated(
+    "Use Brush.horizontalGradient instead",
+    ReplaceWith(
+        "Brush.horizontalGradient(" +
+            "colorStops, startX = startX, endX = endX, tileMode = tileMode)",
+        "androidx.compose.ui.ui.graphics"
+    )
+)
 @Stable
 fun HorizontalGradient(
     vararg colorStops: ColorStop,
@@ -285,10 +710,8 @@
 ) = LinearGradient(
     List<Color>(colorStops.size) { i -> colorStops[i].second },
     List<Float>(colorStops.size) { i -> colorStops[i].first },
-    startX = startX,
-    startY = 0.0f,
-    endX = endX,
-    endY = 0.0f,
+    start = Offset(startX, 0.0f),
+    end = Offset(endX, 0.0f),
     tileMode = tileMode
 )
 
@@ -307,6 +730,13 @@
  * )
  * ```
  */
+@Deprecated(
+    "Use Brush.sweepGradient instead",
+    ReplaceWith(
+        "Brush.sweepGradient(colorStops, center = center)",
+        "androidx.compose.ui.ui.graphics"
+    )
+)
 @Stable
 fun SweepGradient(
     vararg colorStops: ColorStop,
@@ -314,7 +744,7 @@
 ) = SweepGradient(
     center,
     List<Color>(colorStops.size) { i -> colorStops[i].second },
-    List<Float>(colorStops.size) { i -> colorStops[i].first },
+    List<Float>(colorStops.size) { i -> colorStops[i].first }
 )
 
 /**
@@ -330,6 +760,10 @@
  * )
  * ```
  */
+@Deprecated(
+    "Use Brush.sweepGradient instead",
+    ReplaceWith("Brush.sweepGradient(colors, center)", "androidx.compose.ui.ui.graphics")
+)
 @Stable
 fun SweepGradient(
     colors: List<Color>,
@@ -343,32 +777,33 @@
 class LinearGradient internal constructor(
     private val colors: List<Color>,
     private val stops: List<Float>? = null,
-    private val startX: Float,
-    private val startY: Float,
-    private val endX: Float,
-    private val endY: Float,
+    private val start: Offset,
+    private val end: Offset,
     private val tileMode: TileMode = TileMode.Clamp
-) : ShaderBrush(
-    LinearGradientShader(
-        Offset(startX, startY),
-        Offset(endX, endY),
-        colors,
-        stops,
-        tileMode
-    )
-) {
+) : ShaderBrush() {
+
+    override fun createShader(size: Size): Shader {
+        val startX = if (start.x == Float.POSITIVE_INFINITY) size.width else start.x
+        val startY = if (start.y == Float.POSITIVE_INFINITY) size.height else start.y
+        val endX = if (end.x == Float.POSITIVE_INFINITY) size.width else end.x
+        val endY = if (end.y == Float.POSITIVE_INFINITY) size.height else end.y
+        return LinearGradientShader(
+            colors = colors,
+            colorStops = stops,
+            from = Offset(startX, startY),
+            to = Offset(endX, endY),
+            tileMode = tileMode
+        )
+    }
+
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (nativeClass() != other?.nativeClass()) return false
-
-        other as LinearGradient
+        if (other !is LinearGradient) return false
 
         if (colors != other.colors) return false
         if (stops != other.stops) return false
-        if (startX != other.startX) return false
-        if (startY != other.startY) return false
-        if (endX != other.endX) return false
-        if (endY != other.endY) return false
+        if (start != other.start) return false
+        if (end != other.end) return false
         if (tileMode != other.tileMode) return false
 
         return true
@@ -377,21 +812,19 @@
     override fun hashCode(): Int {
         var result = colors.hashCode()
         result = 31 * result + (stops?.hashCode() ?: 0)
-        result = 31 * result + startX.hashCode()
-        result = 31 * result + startY.hashCode()
-        result = 31 * result + endX.hashCode()
-        result = 31 * result + endY.hashCode()
+        result = 31 * result + start.hashCode()
+        result = 31 * result + end.hashCode()
         result = 31 * result + tileMode.hashCode()
         return result
     }
 
     override fun toString(): String {
+        val startValue = if (start.isFinite) "start=$start, " else ""
+        val endValue = if (end.isFinite) "end=$end, " else ""
         return "LinearGradient(colors=$colors, " +
             "stops=$stops, " +
-            "startX=$startX, " +
-            "startY=$startY, " +
-            "endX=$endX, " +
-            "endY=$endY, " +
+            startValue +
+            endValue +
             "tileMode=$tileMode)"
     }
 }
@@ -403,29 +836,39 @@
 class RadialGradient internal constructor(
     private val colors: List<Color>,
     private val stops: List<Float>? = null,
-    private val centerX: Float,
-    private val centerY: Float,
+    private val center: Offset,
     private val radius: Float,
     private val tileMode: TileMode = TileMode.Clamp
-) : ShaderBrush(
-    RadialGradientShader(
-        Offset(centerX, centerY),
-        radius,
-        colors,
-        stops,
-        tileMode
-    )
-) {
+) : ShaderBrush() {
+
+    override fun createShader(size: Size): Shader {
+        val centerX: Float
+        val centerY: Float
+        if (center.isUnspecified) {
+            val drawCenter = size.center()
+            centerX = drawCenter.x
+            centerY = drawCenter.y
+        } else {
+            centerX = if (center.x == Float.POSITIVE_INFINITY) size.width else center.x
+            centerY = if (center.y == Float.POSITIVE_INFINITY) size.height else center.y
+        }
+
+        return RadialGradientShader(
+            colors = colors,
+            colorStops = stops,
+            center = Offset(centerX, centerY),
+            radius = if (radius == Float.POSITIVE_INFINITY) size.minDimension / 2 else radius,
+            tileMode = tileMode
+        )
+    }
+
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (nativeClass() != other?.nativeClass()) return false
-
-        other as RadialGradient
+        if (other !is RadialGradient) return false
 
         if (colors != other.colors) return false
         if (stops != other.stops) return false
-        if (centerX != other.centerX) return false
-        if (centerY != other.centerY) return false
+        if (center != other.center) return false
         if (radius != other.radius) return false
         if (tileMode != other.tileMode) return false
 
@@ -435,20 +878,20 @@
     override fun hashCode(): Int {
         var result = colors.hashCode()
         result = 31 * result + (stops?.hashCode() ?: 0)
-        result = 31 * result + centerX.hashCode()
-        result = 31 * result + centerY.hashCode()
+        result = 31 * result + center.hashCode()
         result = 31 * result + radius.hashCode()
         result = 31 * result + tileMode.hashCode()
         return result
     }
 
     override fun toString(): String {
+        val centerValue = if (center.isSpecified) "center=$center, " else ""
+        val radiusValue = if (radius.isFinite()) "radius=$radius, " else ""
         return "RadialGradient(" +
             "colors=$colors, " +
             "stops=$stops, " +
-            "centerX=$centerX, " +
-            "centerY=$centerY, " +
-            "radius=$radius, " +
+            centerValue +
+            radiusValue +
             "tileMode=$tileMode)"
     }
 }
@@ -460,19 +903,26 @@
 class SweepGradient internal constructor(
     private val center: Offset,
     private val colors: List<Color>,
-    private val stops: List<Float>? = null,
-) : ShaderBrush(
-    SweepGradientShader(
-        center,
-        colors,
-        stops
-    )
-) {
+    private val stops: List<Float>? = null
+) : ShaderBrush() {
+
+    override fun createShader(size: Size): Shader =
+        SweepGradientShader(
+            if (center.isUnspecified) {
+                size.center()
+            } else {
+                Offset(
+                    if (center.x == Float.POSITIVE_INFINITY) size.width else center.x,
+                    if (center.y == Float.POSITIVE_INFINITY) size.height else center.y
+                )
+            },
+            colors,
+            stops
+        )
+
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (nativeClass() != other?.nativeClass()) return false
-
-        other as SweepGradient
+        if (other !is SweepGradient) return false
 
         if (center != other.center) return false
         if (colors != other.colors) return false
@@ -489,18 +939,45 @@
     }
 
     override fun toString(): String {
-        return "SweepGradient(center=$center, colors=$colors, stops=$stops)"
+        val centerValue = if (center.isSpecified) "center=$center, " else ""
+        return "SweepGradient(" +
+            centerValue +
+            "colors=$colors, stops=$stops)"
     }
 }
 
 /**
+ * Convenience method to create a ShaderBrush that always returns the same shader instance
+ * regardless of size
+ */
+fun ShaderBrush(shader: Shader) = object : ShaderBrush() {
+
+    /**
+     * Create a shader based on the given size that represents the current drawing area
+     */
+    override fun createShader(size: Size): Shader = shader
+}
+
+/**
  * Brush implementation that wraps and applies a the provided shader to a [Paint]
+ * The shader can be lazily created based on a given size, or provided directly as a parameter
  */
 @Immutable
-open class ShaderBrush(val shader: Shader) : Brush() {
-    final override fun applyTo(p: Paint, alpha: Float) {
+abstract class ShaderBrush() : Brush() {
+
+    private var internalShader: Shader? = null
+    private var createdSize = Size.Unspecified
+
+    abstract fun createShader(size: Size): Shader
+
+    final override fun applyTo(size: Size, p: Paint, alpha: Float) {
+        var shader = internalShader
+        if (shader == null || createdSize != size) {
+            shader = createShader(size).also { internalShader = it }
+            createdSize = size
+        }
         if (p.color != Color.Black) p.color = Color.Black
         if (p.shader != shader) p.shader = shader
         if (p.alpha != alpha) p.alpha = alpha
     }
-}
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
index 53249d4..d5e194c 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
@@ -594,4 +594,11 @@
  * If this [Color] [isSpecified] then this is returned, otherwise [block] is executed and its result
  * is returned.
  */
+@Deprecated("Use takeOrElse", ReplaceWith("takeOrElse(block)"))
 inline fun Color.useOrElse(block: () -> Color): Color = if (isSpecified) this else block()
+
+/**
+ * If this [Color] [isSpecified] then this is returned, otherwise [block] is executed and its result
+ * is returned.
+ */
+inline fun Color.takeOrElse(block: () -> Color): Color = if (isSpecified) this else block()
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Outline.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Outline.kt
index 9af24c1..8695e97 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Outline.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Outline.kt
@@ -22,6 +22,7 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.RoundRect
 import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.boundingRect
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.drawscope.DrawStyle
 import androidx.compose.ui.graphics.drawscope.Fill
@@ -38,7 +39,11 @@
      * Rectangular area.
      */
     @Immutable
-    data class Rectangle(val rect: Rect) : Outline()
+    data class Rectangle(val rect: Rect) : Outline() {
+
+        override val bounds: Rect
+            get() = rect
+    }
     /**
      * Rectangular area with rounded corners.
      */
@@ -60,13 +65,24 @@
                 null
             }
         }
+
+        override val bounds: Rect
+            get() = roundRect.boundingRect
     }
     /**
      * An area defined as a path.
      *
      * Note that only convex paths can be used for drawing the shadow. See [Path.isConvex].
      */
-    data class Generic(val path: Path) : Outline()
+    data class Generic(val path: Path) : Outline() {
+        override val bounds: Rect
+            get() = path.getBounds()
+    }
+
+    /**
+     * Return the bounds of the outline
+     */
+    abstract val bounds: Rect
 }
 
 /**
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Paint.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Paint.kt
index bdbd687..32a3905 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Paint.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Paint.kt
@@ -28,111 +28,126 @@
 interface Paint {
     fun asFrameworkPaint(): NativePaint
 
+    /**
+     * Configures the alpha value between 0f to 1f representing fully transparent to fully
+     * opaque for the color drawn with this Paint
+     */
     var alpha: Float
 
-    // Whether to apply anti-aliasing to lines and images drawn on the
-    // canvas.
-    //
-    // Defaults to true.
+    /**
+     * Whether to apply anti-aliasing to lines and images drawn on the
+     * canvas.
+     * Defaults to true.
+     */
     var isAntiAlias: Boolean
 
-    // The color to use when stroking or filling a shape.
-    //
-    // Defaults to opaque black.
-    //
-    // See also:
-    //
-    //  * [style], which controls whether to stroke or fill (or both).
-    //  * [colorFilter], which overrides [color].
-    //  * [shader], which overrides [color] with more elaborate effects.
-    //
-    // This color is not used when compositing. To colorize a layer, use
-    // [colorFilter].
+    /**
+     * The color to use when stroking or filling a shape.
+     * Defaults to opaque black.
+     * See also:
+     * [style], which controls whether to stroke or fill (or both).
+     * [colorFilter], which overrides [color].
+     * [shader], which overrides [color] with more elaborate effects.
+     * This color is not used when compositing. To colorize a layer, use [colorFilter].
+     */
     var color: Color
 
-    // A blend mode to apply when a shape is drawn or a layer is composited.
-    //
-    // The source colors are from the shape being drawn (e.g. from
-    // [Canvas.drawPath]) or layer being composited (the graphics that were drawn
-    // between the [Canvas.saveLayer] and [Canvas.restore] calls), after applying
-    // the [colorFilter], if any.
-    //
-    // The destination colors are from the background onto which the shape or
-    // layer is being composited.
-    //
-    // Defaults to [BlendMode.srcOver].
-    //
-    // See also:
-    //
-    //  * [Canvas.saveLayer], which uses its [Paint]'s [blendMode] to composite
-    //    the layer when [restore] is called.
-    //  * [BlendMode], which discusses the user of [saveLayer] with [blendMode].
+    /**
+     * A blend mode to apply when a shape is drawn or a layer is composited.
+     * The source colors are from the shape being drawn (e.g. from
+     * [Canvas.drawPath]) or layer being composited (the graphics that were drawn
+     * between the [Canvas.saveLayer] and [Canvas.restore] calls), after applying
+     * the [colorFilter], if any.
+     * The destination colors are from the background onto which the shape or
+     * layer is being composited.
+     * Defaults to [BlendMode.SrcOver].
+     * See also:
+     * [Canvas.saveLayer], which uses its [Paint]'s [blendMode] to composite
+     * the layer when [Canvas.restore] is called.
+     * [BlendMode], which discusses the user of [Canvas.saveLayer] with [blendMode].
+     */
     var blendMode: BlendMode
 
-    // Whether to paint inside shapes, the edges of shapes, or both.
-    //
-    // Defaults to [PaintingStyle.fill].
+    /**
+     * Whether to paint inside shapes, the edges of shapes, or both.
+     * Defaults to [PaintingStyle.Fill].
+     */
     var style: PaintingStyle
 
-    // How wide to make edges drawn when [style] is set to
-    // [PaintingStyle.stroke]. The width is given in logical pixels measured in
-    // the direction orthogonal to the direction of the path.
-    //
-    // Defaults to 0.0, which correspond to a hairline width.
+    /**
+     * How wide to make edges drawn when [style] is set to
+     * [PaintingStyle.Stroke]. The width is given in logical pixels measured in
+     * the direction orthogonal to the direction of the path.
+     * Defaults to 0.0, which correspond to a hairline width.
+     */
     var strokeWidth: Float
 
-    // The kind of finish to place on the end of lines drawn when
-    // [style] is set to [PaintingStyle.stroke].
-    //
-    // Defaults to [StrokeCap.butt], i.e. no caps.
+    /**
+     * The kind of finish to place on the end of lines drawn when
+     * [style] is set to [PaintingStyle.Stroke].
+     * Defaults to [StrokeCap.Butt], i.e. no caps.
+     */
     var strokeCap: StrokeCap
 
-    // The kind of finish to place on the joins between segments.
-    //
-    // This applies to paths drawn when [style] is set to [PaintingStyle.stroke],
-    // It does not apply to points drawn as lines with [Canvas.drawPoints].
-    //
-    // Defaults to [StrokeJoin.miter], i.e. sharp corners. See also
-    // [strokeMiterLimit] to control when miters are replaced by bevels.
+    /**
+     * The kind of finish to place on the joins between segments.
+     * This applies to paths drawn when [style] is set to [PaintingStyle.Stroke],
+     * It does not apply to points drawn as lines with [Canvas.drawPoints].
+     * Defaults to [StrokeJoin.Miter], i.e. sharp corners. See also
+     * [strokeMiterLimit] to control when miters are replaced by bevels.
+     */
     var strokeJoin: StrokeJoin
 
-    // The limit for miters to be drawn on segments when the join is set to
-    // [StrokeJoin.miter] and the [style] is set to [PaintingStyle.stroke]. If
-    // this limit is exceeded, then a [StrokeJoin.bevel] join will be drawn
-    // instead. This may cause some 'popping' of the corners of a path if the
-    // angle between line segments is animated.
-    //
-    // This limit is expressed as a limit on the length of the miter.
-    //
-    // Defaults to 4.0.  Using zero as a limit will cause a [StrokeJoin.bevel]
-    // join to be used all the time.
+    /**
+     * The limit for miters to be drawn on segments when the join is set to
+     * [StrokeJoin.Miter] and the [style] is set to [PaintingStyle.Stroke]. If
+     * this limit is exceeded, then a [StrokeJoin.Bevel] join will be drawn
+     * instead. This may cause some 'popping' of the corners of a path if the
+     * angle between line segments is animated.
+     * This limit is expressed as a limit on the length of the miter.
+     * Defaults to 4.0.  Using zero as a limit will cause a [StrokeJoin.Bevel]
+     * join to be used all the time.
+     */
     var strokeMiterLimit: Float
 
-    // Controls the performance vs quality trade-off to use when applying
-    // when drawing images, as with [Canvas.drawImageRect]
-    //
-    // Defaults to [FilterQuality.none].
+    /**
+     * Controls the performance vs quality trade-off to use when applying
+     * when drawing images, as with [Canvas.drawImageRect]
+     * Defaults to [FilterQuality.None].
+     */
     var filterQuality: FilterQuality
 
-    // The shader to use when stroking or filling a shape.
-    //
-    // When this is null, the [color] is used instead.
-    //
-    // See also:
-    //
-    //  * [Gradient], a shader that paints a color gradient.
-    //  * [ImageShader], a shader that tiles an [Image].
-    //  * [colorFilter], which overrides [shader].
-    //  * [color], which is used if [shader] and [colorFilter] are null.
+    /**
+     * The shader to use when stroking or filling a shape.
+     *
+     * When this is null, the [color] is used instead.
+     *
+     * See also:
+     * [LinearGradientShader], [RadialGradientShader], or [SweepGradientShader] shaders that
+     * paint a color gradient.
+     * [ImageShader], a shader that tiles an [ImageBitmap].
+     * [colorFilter], which overrides [shader].
+     * [color], which is used if [shader] and [colorFilter] are null.
+     */
     var shader: Shader?
 
-    // A color filter to apply when a shape is drawn or when a layer is
-    // composited.
-    //
-    // See [ColorFilter] for details.
-    //
-    // When a shape is being drawn, [colorFilter] overrides [color] and [shader].
+    /**
+     *  A color filter to apply when a shape is drawn or when a layer is
+     *  composited.
+     *  See [ColorFilter] for details.
+     *  When a shape is being drawn, [colorFilter] overrides [color] and [shader].
+     */
     var colorFilter: ColorFilter?
 
+    @Suppress("DEPRECATION")
+    @Deprecated(
+        "Use pathEffect instead",
+        ReplaceWith("pathEffect", "androidx.compose.ui.graphics.Paint")
+    )
     var nativePathEffect: NativePathEffect?
+
+    /**
+     * Specifies the [PathEffect] applied to the geometry of the shape that is drawn
+     */
+    var pathEffect: PathEffect?
 }
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
index 91e53db..ccf90c0 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
@@ -22,6 +22,10 @@
 
 expect fun Path(): Path
 
+@Deprecated(
+    "Use PathEffect instead",
+    ReplaceWith("PathEffect", "androidx.compose.ui.graphics.PathEffect")
+)
 expect class NativePathEffect
 
 /* expect class */ interface Path {
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PathEffect.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PathEffect.kt
new file mode 100644
index 0000000..fb12a5d
--- /dev/null
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PathEffect.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.graphics
+
+/**
+ * Effect applied to the geometry of a drawing primitive. For example, this can be used
+ * to draw shapes as a dashed or shaped pattern, or apply a treatment around line segment
+ * intersections.
+ */
+interface PathEffect {
+    companion object {
+
+        /**
+         * Replaces sharp angles between line segments into rounded angles of the specified radius
+         *
+         * @param radius Rounded corner radius to apply for each angle of the drawn shape
+         */
+        fun cornerPathEffect(radius: Float): PathEffect = actualCornerPathEffect(radius)
+
+        /**
+         * Draws a shape as a series of dashes with the given intervals and offset into the specified
+         * interval array. The intervals must contain an even number of entries (>=2). The even indices
+         * specify "on" intervals and the odd indices represent "off" intervals. The phase parameter
+         * is the pixel offset into the intervals array (mod the sum of all of the intervals).
+         *
+         * For example: if `intervals[] = {10, 20}`, and phase = 25, this will set up a dashed
+         * path like so: 5 pixels off 10 pixels on 20 pixels off 10 pixels on 20 pixels off
+         *
+         * The phase parameter is
+         * an offset into the intervals array. The intervals array
+         * controls the length of the dashes. This is only applied for stroked shapes
+         * (ex. [PaintingStyle.Stroke] and is ignored for filled in shapes (ex. [PaintingStyle.Fill]
+         *
+         * @param intervals Array of "on" and "off" distances for the dashed line segments
+         * @param phase Pixel offset into the intervals array
+         */
+        fun dashPathEffect(intervals: FloatArray, phase: Float = 0f): PathEffect =
+            actualDashPathEffect(intervals, phase)
+
+        /**
+         * Create a PathEffect that applies the inner effect to the path, and then applies the outer
+         * effect to the result of the inner effect. (e.g. outer(inner(path)).
+         */
+        fun chainPathEffect(outer: PathEffect, inner: PathEffect): PathEffect =
+            actualChainPathEffect(outer, inner)
+
+        /**
+         * Dash the drawn path by stamping it with the specified shape represented as a [Path].
+         * This is only applied to stroke shapes and will be ignored with filled shapes.
+         * The stroke width used with this [PathEffect] is ignored as well.
+         *
+         * @param shape Path to stamp along
+         * @param advance Spacing between each stamped shape
+         * @param phase Amount to offset before the first shape is stamped
+         * @param style How to transform the shape at each position as it is stamped
+         */
+        fun stampedPathEffect(
+            shape: Path,
+            advance: Float,
+            phase: Float,
+            style: StampedPathEffectStyle
+        ): PathEffect = actualStampedPathEffect(shape, advance, phase, style)
+    }
+}
+
+internal expect fun actualCornerPathEffect(radius: Float): PathEffect
+
+internal expect fun actualDashPathEffect(intervals: FloatArray, phase: Float): PathEffect
+
+internal expect fun actualChainPathEffect(outer: PathEffect, inner: PathEffect): PathEffect
+
+internal expect fun actualStampedPathEffect(
+    shape: Path,
+    advance: Float,
+    phase: Float,
+    style: StampedPathEffectStyle
+): PathEffect
+
+/**
+ * Strategy for transforming each point of the shape along the drawn path
+ *
+ * @sample androidx.compose.ui.graphics.samples.StampedPathEffectSample
+ */
+enum class StampedPathEffectStyle {
+
+    /**
+     * Translate the path shape into the specified location aligning the top left of the path with
+     * the drawn geometry. This does not modify the path itself.
+     *
+     * For example, a circle drawn with a square path and [Translate] will draw the square path
+     * repeatedly with the top left corner of each stamped square along the curvature of the circle.
+     */
+    Translate,
+
+    /**
+     * Rotates the path shape its center along the curvature of the drawn geometry. This does not
+     * modify the path itself.
+     *
+     * For example, a circle drawn with a square path and [Rotate] will draw the square path
+     * repeatedly with the center of each stamped square along the curvature of the circle as well
+     * as each square being rotated along the circumference.
+     */
+    Rotate,
+
+    /**
+     * Modifies the points within the path such that they fit within the drawn geometry. This will
+     * turn straight lines into curves.
+     *
+     * For example, a circle drawn with a square path and [Morph] will modify the straight lines
+     * of the square paths to be curves such that each stamped square is rendered as an arc around
+     * the curvature of the circle.
+     */
+    Morph
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt
index 7fb547f..cf3527d 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt
@@ -103,7 +103,7 @@
 fun SweepGradientShader(
     center: Offset,
     colors: List<Color>,
-    colorStops: List<Float>? = null,
+    colorStops: List<Float>? = null
 ): Shader = ActualSweepGradientShader(center, colors, colorStops)
 
 internal expect fun ActualSweepGradientShader(
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt
index a73b4a4..45867d0 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt
@@ -27,10 +27,10 @@
 import androidx.compose.ui.graphics.ColorFilter
 import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Matrix
-import androidx.compose.ui.graphics.NativePathEffect
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.PaintingStyle
 import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.PathEffect
 import androidx.compose.ui.graphics.PointMode
 import androidx.compose.ui.graphics.StrokeCap
 import androidx.compose.ui.graphics.StrokeJoin
@@ -102,7 +102,7 @@
         end: Offset,
         strokeWidth: Float,
         cap: StrokeCap,
-        pathEffect: NativePathEffect?,
+        pathEffect: PathEffect?,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float,
         colorFilter: ColorFilter?,
         blendMode: BlendMode
@@ -131,7 +131,7 @@
         end: Offset,
         strokeWidth: Float,
         cap: StrokeCap,
-        pathEffect: NativePathEffect?,
+        pathEffect: PathEffect?,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float,
         colorFilter: ColorFilter?,
         blendMode: BlendMode
@@ -432,7 +432,7 @@
         color: Color,
         strokeWidth: Float,
         cap: StrokeCap,
-        pathEffect: NativePathEffect?,
+        pathEffect: PathEffect?,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float,
         colorFilter: ColorFilter?,
         blendMode: BlendMode
@@ -461,7 +461,7 @@
         brush: Brush,
         strokeWidth: Float,
         cap: StrokeCap,
-        pathEffect: NativePathEffect?,
+        pathEffect: PathEffect?,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float,
         colorFilter: ColorFilter?,
         blendMode: BlendMode
@@ -554,15 +554,11 @@
             is Stroke ->
                 obtainStrokePaint()
                     .apply {
-                        with(drawStyle) {
-                            if (strokeWidth != width) strokeWidth = width
-                            if (strokeCap != cap) strokeCap = cap
-                            if (strokeMiterLimit != miter) strokeMiterLimit = miter
-                            if (strokeJoin != join) strokeJoin = join
-
-                            // TODO b/154550525 add PathEffect to Paint if necessary
-                            nativePathEffect = pathEffect
-                        }
+                        if (strokeWidth != drawStyle.width) strokeWidth = drawStyle.width
+                        if (strokeCap != drawStyle.cap) strokeCap = drawStyle.cap
+                        if (strokeMiterLimit != drawStyle.miter) strokeMiterLimit = drawStyle.miter
+                        if (strokeJoin != drawStyle.join) strokeJoin = drawStyle.join
+                        if (pathEffect != drawStyle.pathEffect) pathEffect = drawStyle.pathEffect
                     }
         }
 
@@ -578,7 +574,7 @@
         blendMode: BlendMode
     ): Paint = selectPaint(style).apply {
         if (brush != null) {
-            brush.applyTo(this, alpha)
+            brush.applyTo(size, this, alpha)
         } else if (this.alpha != alpha) {
             this.alpha = alpha
         }
@@ -612,7 +608,7 @@
         miter: Float,
         cap: StrokeCap,
         join: StrokeJoin,
-        pathEffect: NativePathEffect?,
+        pathEffect: PathEffect?,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float,
         colorFilter: ColorFilter?,
         blendMode: BlendMode
@@ -629,7 +625,7 @@
             if (this.strokeMiterLimit != miter) this.strokeMiterLimit = miter
             if (this.strokeCap != cap) this.strokeCap = cap
             if (this.strokeJoin != join) this.strokeJoin = join
-            this.nativePathEffect = pathEffect
+            if (this.pathEffect != pathEffect) this.pathEffect = pathEffect
         }
 
     private fun configureStrokePaint(
@@ -638,13 +634,13 @@
         miter: Float,
         cap: StrokeCap,
         join: StrokeJoin,
-        pathEffect: NativePathEffect?,
+        pathEffect: PathEffect?,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float,
         colorFilter: ColorFilter?,
         blendMode: BlendMode
     ) = obtainStrokePaint().apply {
         if (brush != null) {
-            brush.applyTo(this, alpha)
+            brush.applyTo(size, this, alpha)
         } else if (this.alpha != alpha) {
             this.alpha = alpha
         }
@@ -654,7 +650,7 @@
         if (this.strokeMiterLimit != miter) this.strokeMiterLimit = miter
         if (this.strokeCap != cap) this.strokeCap = cap
         if (this.strokeJoin != join) this.strokeJoin = join
-        this.nativePathEffect = pathEffect
+        if (this.pathEffect != pathEffect) this.pathEffect = pathEffect
     }
 
     /**
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt
index 16f8b4f..26eeacd 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt
@@ -26,9 +26,9 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
 import androidx.compose.ui.graphics.ImageBitmap
-import androidx.compose.ui.graphics.NativePathEffect
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.PathEffect
 import androidx.compose.ui.graphics.PointMode
 import androidx.compose.ui.graphics.StrokeCap
 import androidx.compose.ui.graphics.StrokeJoin
@@ -327,7 +327,7 @@
         end: Offset,
         strokeWidth: Float = Stroke.HairlineWidth,
         cap: StrokeCap = Stroke.DefaultCap,
-        pathEffect: NativePathEffect? = null,
+        pathEffect: PathEffect? = null,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float = 1.0f,
         colorFilter: ColorFilter? = null,
         blendMode: BlendMode = DefaultBlendMode
@@ -354,7 +354,7 @@
         end: Offset,
         strokeWidth: Float = Stroke.HairlineWidth,
         cap: StrokeCap = Stroke.DefaultCap,
-        pathEffect: NativePathEffect? = null,
+        pathEffect: PathEffect? = null,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float = 1.0f,
         colorFilter: ColorFilter? = null,
         blendMode: BlendMode = DefaultBlendMode
@@ -745,7 +745,7 @@
         color: Color,
         strokeWidth: Float = Stroke.HairlineWidth,
         cap: StrokeCap = StrokeCap.Butt,
-        pathEffect: NativePathEffect? = null,
+        pathEffect: PathEffect? = null,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float = 1.0f,
         colorFilter: ColorFilter? = null,
         blendMode: BlendMode = DefaultBlendMode
@@ -773,7 +773,7 @@
         brush: Brush,
         strokeWidth: Float = Stroke.HairlineWidth,
         cap: StrokeCap = StrokeCap.Butt,
-        pathEffect: NativePathEffect? = null,
+        pathEffect: PathEffect? = null,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float = 1.0f,
         colorFilter: ColorFilter? = null,
         blendMode: BlendMode = DefaultBlendMode
@@ -837,7 +837,7 @@
     /**
      * Effect to apply to the stroke, null indicates a solid stroke line is to be drawn
      */
-    val pathEffect: NativePathEffect? = null
+    val pathEffect: PathEffect? = null
 ) : DrawStyle() {
     companion object {
 
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
index 2f32ba0..996cd0d 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
@@ -36,7 +36,6 @@
 import androidx.compose.ui.graphics.vector.PathNode.RelativeReflectiveQuadTo
 import androidx.compose.ui.graphics.vector.PathNode.RelativeVerticalTo
 import androidx.compose.ui.graphics.vector.PathNode.VerticalTo
-import androidx.compose.ui.util.toRadians
 import kotlin.math.PI
 import kotlin.math.abs
 import kotlin.math.atan2
@@ -639,4 +638,7 @@
         var endPosition: Int = 0,
         var endWithNegativeOrDot: Boolean = false
     )
-}
+
+    private fun Float.toRadians(): Float = this / 180f * PI.toFloat()
+    private fun Double.toRadians(): Double = this / 180 * PI
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPaint.kt b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPaint.kt
index a22418d..38cc267 100644
--- a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPaint.kt
+++ b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPaint.kt
@@ -114,9 +114,19 @@
             field = value
         }
 
-    override var nativePathEffect: NativePathEffect? = null
+    override var nativePathEffect: NativePathEffect?
+        get() = pathEffect?.asDesktopPathEffect()
         set(value) {
-            skija.pathEffect = value
+            pathEffect = if (value == null) {
+                null
+            } else {
+                DesktopPathEffect(value)
+            }
+        }
+
+    override var pathEffect: PathEffect? = null
+        set(value) {
+            skija.pathEffect = (value as DesktopPathEffect).asDesktopPathEffect()
             field = value
         }
 
diff --git a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPathEffect.kt b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPathEffect.kt
new file mode 100644
index 0000000..a443873
--- /dev/null
+++ b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPathEffect.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.graphics
+
+import org.jetbrains.skija.PathEffect as SkijaPathEffect
+
+internal class DesktopPathEffect(val nativePathEffect: SkijaPathEffect) : PathEffect
+
+/**
+ * Obtain a reference to the desktop PathEffect type
+ */
+fun PathEffect.asDesktopPathEffect(): SkijaPathEffect =
+    (this as DesktopPathEffect).nativePathEffect
+
+internal actual fun actualCornerPathEffect(radius: Float): PathEffect =
+    DesktopPathEffect(SkijaPathEffect.makeCorner(radius))
+
+internal actual fun actualDashPathEffect(
+    intervals: FloatArray,
+    phase: Float
+): PathEffect = DesktopPathEffect(SkijaPathEffect.makeDash(intervals, phase))
+
+internal actual fun actualChainPathEffect(outer: PathEffect, inner: PathEffect): PathEffect =
+    DesktopPathEffect(outer.asDesktopPathEffect().makeCompose(inner.asDesktopPathEffect()))
+
+internal actual fun actualStampedPathEffect(
+    shape: Path,
+    advance: Float,
+    phase: Float,
+    style: StampedPathEffectStyle
+): PathEffect =
+    DesktopPathEffect(
+        SkijaPathEffect.makePath1D(
+            shape.asDesktopPath(),
+            advance,
+            phase,
+            style.toSkijaStampedPathEffectStyle()
+        )
+    )
+
+internal fun StampedPathEffectStyle.toSkijaStampedPathEffectStyle(): SkijaPathEffect.Style =
+    when (this) {
+        StampedPathEffectStyle.Morph -> SkijaPathEffect.Style.MORPH
+        StampedPathEffectStyle.Rotate -> SkijaPathEffect.Style.ROTATE
+        StampedPathEffectStyle.Translate -> SkijaPathEffect.Style.TRANSLATE
+    }
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/test/java/androidx/compose/ui/graphics/ColorTest.kt b/compose/ui/ui-graphics/src/test/java/androidx/compose/ui/graphics/ColorTest.kt
index e496801..da18ac6 100644
--- a/compose/ui/ui-graphics/src/test/java/androidx/compose/ui/graphics/ColorTest.kt
+++ b/compose/ui/ui-graphics/src/test/java/androidx/compose/ui/graphics/ColorTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.ui.graphics.colorspace.ColorSpaces
 import androidx.compose.ui.util.lerp
-import androidx.compose.ui.util.toHexString
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotEquals
@@ -285,4 +284,9 @@
         assertEquals(0.4f, blue, epsilon)
         assertEquals(0.6f, alpha, epsilon)
     }
+
+    companion object {
+        @OptIn(kotlin.ExperimentalUnsignedTypes::class)
+        fun Int.toHexString() = "0x${toUInt().toString(16).padStart(8, '0')}"
+    }
 }
diff --git a/compose/ui/ui-test-font/build.gradle b/compose/ui/ui-test-font/build.gradle
index 16d1675..995fd29 100644
--- a/compose/ui/ui-test-font/build.gradle
+++ b/compose/ui/ui-test-font/build.gradle
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+
+import androidx.build.AndroidXUiPlugin
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
@@ -25,6 +27,19 @@
     id("AndroidXUiPlugin")
 }
 
+AndroidXUiPlugin.applyAndConfigureKotlinPlugin(project)
+
+if(AndroidXUiPlugin.isMultiplatformEnabled(project)) {
+    kotlin {
+        android()
+        jvm("desktop")
+
+        sourceSets {
+            desktopMain.dependsOn jvmMain
+        }
+    }
+}
+
 androidx {
     name = "Compose Test Font resources"
     publish = Publish.NONE
diff --git a/compose/ui/ui-test-font/src/main/AndroidManifest.xml b/compose/ui/ui-test-font/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from compose/ui/ui-test-font/src/main/AndroidManifest.xml
rename to compose/ui/ui-test-font/src/androidMain/AndroidManifest.xml
diff --git a/compose/ui/ui-test-font/src/main/res/font/invalid_font.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/invalid_font.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/invalid_font.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/invalid_font.ttf
diff --git a/compose/ui/ui-test-font/src/main/res/font/kern_font.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/kern_font.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/kern_font.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/kern_font.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/sample_font.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/sample_font.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/sample_font.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/sample_font.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/sample_font2.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/sample_font2.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/sample_font2.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/sample_font2.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_100_italic.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_100_italic.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_100_italic.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_100_italic.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_100_regular.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_100_regular.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_100_regular.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_100_regular.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_200_italic.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_200_italic.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_200_italic.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_200_italic.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_200_regular.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_200_regular.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_200_regular.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_200_regular.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_300_italic.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_300_italic.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_300_italic.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_300_italic.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_300_regular.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_300_regular.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_300_regular.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_300_regular.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_400_italic.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_400_italic.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_400_italic.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_400_italic.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_400_regular.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_400_regular.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_400_regular.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_400_regular.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_500_italic.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_500_italic.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_500_italic.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_500_italic.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_500_regular.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_500_regular.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_500_regular.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_500_regular.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_600_italic.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_600_italic.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_600_italic.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_600_italic.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_600_regular.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_600_regular.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_600_regular.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_600_regular.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_700_italic.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_700_italic.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_700_italic.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_700_italic.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_700_regular.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_700_regular.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_700_regular.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_700_regular.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_800_italic.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_800_italic.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_800_italic.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_800_italic.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_800_regular.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_800_regular.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_800_regular.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_800_regular.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_900_italic.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_900_italic.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_900_italic.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_900_italic.ttf
Binary files differ
diff --git a/compose/ui/ui-test-font/src/main/res/font/test_900_regular.ttf b/compose/ui/ui-test-font/src/commonMain/resources/font/test_900_regular.ttf
similarity index 100%
rename from compose/ui/ui-test-font/src/main/res/font/test_900_regular.ttf
rename to compose/ui/ui-test-font/src/commonMain/resources/font/test_900_regular.ttf
Binary files differ
diff --git a/compose/ui/ui-test-junit4/api/current.txt b/compose/ui/ui-test-junit4/api/current.txt
index e6bda1c2..f4d018a 100644
--- a/compose/ui/ui-test-junit4/api/current.txt
+++ b/compose/ui/ui-test-junit4/api/current.txt
@@ -7,7 +7,7 @@
 
   public final class AndroidComposeTestRule<R extends org.junit.rules.TestRule, A extends androidx.activity.ComponentActivity> implements androidx.compose.ui.test.junit4.ComposeTestRule {
     ctor public AndroidComposeTestRule(R activityRule, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider);
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method @androidx.compose.ui.test.ExperimentalTesting public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public R getActivityRule();
     method public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
@@ -15,9 +15,11 @@
     method public long getDisplaySize-YbymL2g();
     method public androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodes(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
     method public androidx.compose.ui.test.SemanticsNodeInteraction onNode(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
+    method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public <T> T! runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
     method public <T> T! runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public void waitForIdle();
     property public final R activityRule;
     property public androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
@@ -54,9 +56,11 @@
     method public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
     method public androidx.compose.ui.unit.Density getDensity();
     method public long getDisplaySize-YbymL2g();
+    method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public <T> T! runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
     method public <T> T! runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public void waitForIdle();
     property public abstract androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
     property public abstract androidx.compose.ui.unit.Density density;
@@ -96,5 +100,8 @@
     ctor public ComposeNotIdleException(String? message, Throwable? cause);
   }
 
+  public final class EspressoLinkKt {
+  }
+
 }
 
diff --git a/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt b/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
index e6bda1c2..f4d018a 100644
--- a/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
@@ -7,7 +7,7 @@
 
   public final class AndroidComposeTestRule<R extends org.junit.rules.TestRule, A extends androidx.activity.ComponentActivity> implements androidx.compose.ui.test.junit4.ComposeTestRule {
     ctor public AndroidComposeTestRule(R activityRule, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider);
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method @androidx.compose.ui.test.ExperimentalTesting public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public R getActivityRule();
     method public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
@@ -15,9 +15,11 @@
     method public long getDisplaySize-YbymL2g();
     method public androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodes(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
     method public androidx.compose.ui.test.SemanticsNodeInteraction onNode(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
+    method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public <T> T! runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
     method public <T> T! runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public void waitForIdle();
     property public final R activityRule;
     property public androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
@@ -54,9 +56,11 @@
     method public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
     method public androidx.compose.ui.unit.Density getDensity();
     method public long getDisplaySize-YbymL2g();
+    method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public <T> T! runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
     method public <T> T! runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public void waitForIdle();
     property public abstract androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
     property public abstract androidx.compose.ui.unit.Density density;
@@ -96,5 +100,8 @@
     ctor public ComposeNotIdleException(String? message, Throwable? cause);
   }
 
+  public final class EspressoLinkKt {
+  }
+
 }
 
diff --git a/compose/ui/ui-test-junit4/api/restricted_current.txt b/compose/ui/ui-test-junit4/api/restricted_current.txt
index e6bda1c2..f4d018a 100644
--- a/compose/ui/ui-test-junit4/api/restricted_current.txt
+++ b/compose/ui/ui-test-junit4/api/restricted_current.txt
@@ -7,7 +7,7 @@
 
   public final class AndroidComposeTestRule<R extends org.junit.rules.TestRule, A extends androidx.activity.ComponentActivity> implements androidx.compose.ui.test.junit4.ComposeTestRule {
     ctor public AndroidComposeTestRule(R activityRule, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider);
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method @androidx.compose.ui.test.ExperimentalTesting public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public R getActivityRule();
     method public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
@@ -15,9 +15,11 @@
     method public long getDisplaySize-YbymL2g();
     method public androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodes(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
     method public androidx.compose.ui.test.SemanticsNodeInteraction onNode(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
+    method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public <T> T! runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
     method public <T> T! runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public void waitForIdle();
     property public final R activityRule;
     property public androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
@@ -54,9 +56,11 @@
     method public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
     method public androidx.compose.ui.unit.Density getDensity();
     method public long getDisplaySize-YbymL2g();
+    method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public <T> T! runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
     method public <T> T! runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+    method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public void waitForIdle();
     property public abstract androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
     property public abstract androidx.compose.ui.unit.Density density;
@@ -96,5 +100,8 @@
     ctor public ComposeNotIdleException(String? message, Throwable? cause);
   }
 
+  public final class EspressoLinkKt {
+  }
+
 }
 
diff --git a/compose/ui/ui-test-junit4/build.gradle b/compose/ui/ui-test-junit4/build.gradle
index 3c79cab..da98a1d 100644
--- a/compose/ui/ui-test-junit4/build.gradle
+++ b/compose/ui/ui-test-junit4/build.gradle
@@ -123,7 +123,6 @@
 android {
     tasks.withType(KotlinCompile).configureEach {
         kotlinOptions {
-            freeCompilerArgs += ["-XXLanguage:-NewInference"]
             useIR = true
         }
     }
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/AndroidOwnerRegistryTest.kt b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/AndroidOwnerRegistryTest.kt
index 2c038c3..f07b0a2 100644
--- a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/AndroidOwnerRegistryTest.kt
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/AndroidOwnerRegistryTest.kt
@@ -20,7 +20,7 @@
 import android.view.View
 import android.view.ViewGroup
 import androidx.activity.ComponentActivity
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.test.junit4.android.AndroidOwnerRegistry
 import androidx.test.ext.junit.rules.ActivityScenarioRule
@@ -44,8 +44,8 @@
 
     private val onRegistrationChangedListener =
         object : AndroidOwnerRegistry.OnRegistrationChangedListener {
-            val recordedChanges = mutableListOf<Pair<AndroidOwner, Boolean>>()
-            override fun onRegistrationChanged(owner: AndroidOwner, registered: Boolean) {
+            val recordedChanges = mutableListOf<Pair<ViewRootForTest, Boolean>>()
+            override fun onRegistrationChanged(owner: ViewRootForTest, registered: Boolean) {
                 recordedChanges.add(Pair(owner, registered))
             }
         }
@@ -67,7 +67,7 @@
         activityRule.scenario.onActivity { activity ->
             // set the composable content and find an owner
             activity.setContent { }
-            val owner = activity.findOwner()
+            val owner = activity.findRootForTest()
 
             // Then it is registered
             assertThat(androidOwnerRegistry.getUnfilteredOwners()).isEqualTo(setOf(owner))
@@ -84,7 +84,7 @@
         activityRule.scenario.onActivity { activity ->
             // set the composable content and find an owner
             activity.setContent { }
-            val owner = activity.findOwner()
+            val owner = activity.findRootForTest()
 
             // And remove it from the hierarchy
             activity.setContentView(View(activity))
@@ -107,7 +107,7 @@
         activityRule.scenario.onActivity { activity ->
             // set the composable content and find an owner
             activity.setContent { }
-            val owner = activity.findOwner()
+            val owner = activity.findRootForTest()
 
             // When we tear down the registry
             androidOwnerRegistry.tearDownRegistry()
@@ -126,16 +126,16 @@
     }
 }
 
-private fun Activity.findOwner(): AndroidOwner {
+private fun Activity.findRootForTest(): ViewRootForTest {
     val viewGroup = findViewById<ViewGroup>(android.R.id.content)
-    return requireNotNull(viewGroup.findOwner())
+    return requireNotNull(viewGroup.findRootForTest())
 }
 
-private fun View.findOwner(): AndroidOwner? {
-    if (this is AndroidOwner) return this
+private fun View.findRootForTest(): ViewRootForTest? {
+    if (this is ViewRootForTest) return this
     if (this is ViewGroup) {
         for (i in 0 until childCount) {
-            val owner = getChildAt(i).findOwner()
+            val owner = getChildAt(i).findRootForTest()
             if (owner != null) {
                 return owner
             }
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt
index 0adcd1b..c04b95a 100644
--- a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt
@@ -114,7 +114,7 @@
     }
 
     /**
-     * Detailed test to verify if [ComposeIdlingResource.isIdle] reports idleness correctly at
+     * Detailed test to verify if [ComposeIdlingResource.isIdleNow] reports idleness correctly at
      * key moments during the animation kick-off process.
      */
     @Test
@@ -138,28 +138,28 @@
 
         val wasIdleAfterRecompose = rule.runOnIdle {
             // Record idleness before kickoff of animation
-            wasIdleBeforeKickOff = composeIdlingResource.isIdle()
+            wasIdleBeforeKickOff = composeIdlingResource.isIdleNow
 
             // Kick off the animation
             animationRunning = true
             animationState.value = AnimationStates.To
 
             // Record idleness after kickoff of animation, but before the snapshot is applied
-            wasIdleBeforeApplySnapshot = composeIdlingResource.isIdle()
+            wasIdleBeforeApplySnapshot = composeIdlingResource.isIdleNow
 
             // Apply the snapshot
             @OptIn(ExperimentalComposeApi::class)
             Snapshot.sendApplyNotifications()
 
             // Record idleness after this snapshot is applied
-            wasIdleAfterApplySnapshot = composeIdlingResource.isIdle()
+            wasIdleAfterApplySnapshot = composeIdlingResource.isIdleNow
 
             // Record idleness after the first recomposition
             @OptIn(ExperimentalCoroutinesApi::class)
             scope.async(start = CoroutineStart.UNDISPATCHED) {
                 // Await a single recomposition
                 withFrameNanos {}
-                composeIdlingResource.isIdle()
+                composeIdlingResource.isIdleNow
             }
         }.let {
             runBlocking {
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/FirstDrawTest.kt b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/FirstDrawTest.kt
index 89851f2..a3e4cf2 100644
--- a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/FirstDrawTest.kt
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/FirstDrawTest.kt
@@ -20,14 +20,10 @@
 import android.widget.FrameLayout
 import androidx.activity.ComponentActivity
 import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.runtime.Recomposer
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.setContent
-import androidx.test.espresso.Espresso.onIdle
+import androidx.compose.ui.platform.ComposeView
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
@@ -40,22 +36,8 @@
     val rule = createAndroidComposeRule<ComponentActivity>()
 
     /**
-     * Tests that the compose tree has been drawn at least once when [onIdle] finishes.
-     */
-    @Test
-    fun waitsForFirstDraw() {
-        var drawn = false
-        rule.setContent {
-            Canvas(Modifier.fillMaxSize()) {
-                drawn = true
-            }
-        }
-        onIdle()
-        assertThat(drawn).isTrue()
-    }
-
-    /**
-     * Tests that the compose tree has been drawn at least once when [onIdle] finishes.
+     * Tests that the compose tree has been drawn at least once when
+     * [ComposeTestRule.setContent] finishes.
      */
     @Test
     fun waitsForFirstDraw_withoutOnIdle() {
@@ -65,13 +47,13 @@
                 drawn = true
             }
         }
-        // onIdle() shouldn't be necessary
+        // rule.waitForIdle() shouldn't be necessary
         assertThat(drawn).isTrue()
     }
 
     /**
-     * Tests that [onIdle] doesn't timeout when the compose tree is completely off-screen and
-     * will hence not be drawn.
+     * Tests that [ComposeTestRule.waitForIdle] doesn't timeout when the compose tree is
+     * completely off-screen and will hence not be drawn.
      */
     @Test
     fun waitsForOutOfBoundsComposeView() {
@@ -79,8 +61,7 @@
 
         rule.activityRule.scenario.onActivity { activity ->
             // Set the compose content in a FrameLayout that is completely placed out of the
-            // screen, and enforce clipToPadding in case clipping will prevent the clipped
-            // content from being drawn.
+            // screen, and set clipToPadding to make sure the content won't be drawn.
 
             val root = object : FrameLayout(activity) {
                 override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
@@ -93,23 +74,23 @@
                 clipToPadding = true
             }
 
-            val outOfBoundsView = FrameLayout(activity).apply {
+            val outOfBoundsView = ComposeView(activity).apply {
                 layoutParams = ViewGroup.MarginLayoutParams(100, 100)
             }
 
             root.addView(outOfBoundsView)
             activity.setContentView(root)
-            outOfBoundsView.setContent(Recomposer.current()) {
+            outOfBoundsView.setContent {
                 // If you see this box when running the test, the test is setup incorrectly
-                Box(Modifier.background(Color.Yellow))
-                Canvas(Modifier) {
+                Canvas(Modifier.fillMaxSize()) {
+                    drawRect(Color.Yellow)
                     drawn = true
                 }
             }
         }
 
         // onIdle shouldn't timeout
-        onIdle()
+        rule.waitForIdle()
         // The compose view was off-screen, so it hasn't drawn yet
         assertThat(drawn).isFalse()
     }
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistryTest.kt b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistryTest.kt
new file mode 100644
index 0000000..cde1ff1
--- /dev/null
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistryTest.kt
@@ -0,0 +1,187 @@
+/*
+ * 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.test.junit4
+
+import androidx.compose.ui.test.IdlingResource
+import androidx.compose.ui.test.InternalTestingApi
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineScope
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/*
+ * This class *could* be moved to the test source set, but that makes it more likely to be
+ * skipped if only connectedCheck is run.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class IdlingResourceRegistryTest {
+
+    private var onIdleCalled = false
+    @OptIn(ExperimentalCoroutinesApi::class)
+    private val scope = TestCoroutineScope()
+    @OptIn(InternalTestingApi::class)
+    private val registry = IdlingResourceRegistry(scope).apply {
+        setOnIdleCallback { onIdleCalled = true }
+    }
+
+    @After
+    fun verifyRegistryStoppedPolling() {
+        scope.cleanupTestCoroutines()
+    }
+
+    @Test
+    @UiThreadTest
+    fun isIdleNow_0_IdlingResources() {
+        assertThat(registry.isIdleNow).isTrue()
+        assertNotPolling()
+    }
+
+    @Test
+    @UiThreadTest
+    fun isIdleNow_1_IdlingResource() {
+        val resource = TestIdlingResource(true)
+        registry.registerIdlingResource(resource)
+        assertThat(registry.isIdleNow).isTrue()
+
+        resource.isIdleNow = false
+        assertThat(registry.isIdleNow).isFalse()
+
+        assertThatPollingStartsAndEnds {
+            resource.isIdleNow = true
+        }
+
+        resource.isIdleNow = false
+        assertThat(registry.isIdleNow).isFalse()
+
+        assertThatPollingStartsAndEnds {
+            resource.isIdleNow = true
+        }
+    }
+
+    @Test
+    @UiThreadTest
+    fun isIdleNow_2_IdlingResources() {
+        val resource1 = TestIdlingResource(true)
+        registry.registerIdlingResource(resource1)
+        val resource2 = TestIdlingResource(true)
+        registry.registerIdlingResource(resource2)
+
+        resource1.isIdleNow = true
+        resource2.isIdleNow = true
+        assertThat(registry.isIdleNow).isTrue()
+
+        resource1.isIdleNow = false
+        resource2.isIdleNow = true
+        assertThat(registry.isIdleNow).isFalse()
+
+        resource1.isIdleNow = true
+        resource2.isIdleNow = false
+        assertThat(registry.isIdleNow).isFalse()
+
+        resource1.isIdleNow = false
+        resource2.isIdleNow = false
+        assertThat(registry.isIdleNow).isFalse()
+
+        assertThatPollingStartsAndEnds {
+            resource1.isIdleNow = true
+            resource2.isIdleNow = true
+        }
+    }
+
+    @Test
+    @UiThreadTest
+    fun isIdleNow_true_doesNotStartPolling() {
+        registry.registerIdlingResource(TestIdlingResource(true))
+        assertThat(registry.isIdleNow).isTrue()
+        assertNotPolling()
+    }
+
+    @Test
+    @UiThreadTest
+    fun isIdleNow_false_doesNotStartPolling() {
+        registry.registerIdlingResource(TestIdlingResource(false))
+        assertThat(registry.isIdleNow).isFalse()
+        assertNotPolling()
+    }
+
+    @Test
+    @UiThreadTest
+    fun isIdleOrStartPolling_emptyRegister_doesNotStartPolling() {
+        assertThat(registry.isIdleNow).isTrue()
+        assertThat(registry.isIdleOrEnsurePolling()).isTrue()
+        assertNotPolling()
+    }
+
+    @Test
+    @UiThreadTest
+    fun isIdleOrStartPolling_idleRegister_doesNotStartPolling() {
+        registry.registerIdlingResource(TestIdlingResource(true))
+        assertThat(registry.isIdleOrEnsurePolling()).isTrue()
+        assertNotPolling()
+    }
+
+    @Test
+    @UiThreadTest
+    fun isIdleOrStartPolling_busyRegister_doesStartPolling() {
+        val resource = TestIdlingResource(false)
+        registry.registerIdlingResource(resource)
+
+        assertThatPollingStartsAndEnds {
+            resource.isIdleNow = true
+        }
+    }
+
+    private fun assertThatPollingStartsAndEnds(makeIdle: () -> Unit) {
+        // Check that we're not polling already ..
+        assertThat(scope.advanceUntilIdle()).isEqualTo(0L)
+        // .. and that we're not idle
+        assertThat(registry.isIdleNow).isFalse()
+
+        // Start the polling
+        onIdleCalled = false
+        assertThat(registry.isIdleOrEnsurePolling()).isFalse()
+
+        // Make the registry idle
+        makeIdle.invoke()
+
+        // Verify that it has polled ..
+        assertThat(scope.advanceUntilIdle()).isGreaterThan(0L)
+        // .. the registry is now idle
+        assertThat(registry.isIdleNow).isTrue()
+        // .. and the onIdle callback was called
+        assertThat(onIdleCalled).isTrue()
+    }
+
+    private fun assertNotPolling() {
+        // Check that no poll job is running ..
+        assertThat(scope.advanceUntilIdle()).isEqualTo(0L)
+        scope.cleanupTestCoroutines()
+        // .. and that the onIdle callback was not called
+        assertThat(onIdleCalled).isFalse()
+    }
+
+    private class TestIdlingResource(initialIdleness: Boolean) : IdlingResource {
+        override var isIdleNow: Boolean = initialIdleness
+    }
+}
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/MultipleComposeRootsTest.kt b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/MultipleComposeRootsTest.kt
index f873303..0fc1531 100644
--- a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/MultipleComposeRootsTest.kt
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/MultipleComposeRootsTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.test.junit4
 
-import android.widget.FrameLayout
 import android.widget.LinearLayout
 import android.widget.TextView
 import androidx.activity.ComponentActivity
@@ -24,10 +23,9 @@
 import androidx.compose.material.Surface
 import androidx.compose.material.TriStateCheckbox
 import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.setContent
+import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.state.ToggleableState
 import androidx.compose.ui.test.assertIsOff
@@ -38,11 +36,11 @@
 import androidx.test.espresso.assertion.ViewAssertions.matches
 import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
 import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import androidx.test.ext.junit.runners.AndroidJUnit4
 
 fun MutableState<ToggleableState>.toggle() {
     value =
@@ -87,16 +85,16 @@
                 .apply { orientation = LinearLayout.VERTICAL }
 
             val textView1 = TextView(activity).apply { text = "Compose 1" }
-            val frameLayout1 = FrameLayout(activity)
+            val composeView1 = ComposeView(activity)
 
             val textView2 = TextView(activity).apply { text = "Compose 2" }
-            val frameLayout2 = FrameLayout(activity)
+            val composeView2 = ComposeView(activity)
 
             activity.setContentView(linearLayout)
             linearLayout.addView(textView1)
-            linearLayout.addView(frameLayout1)
+            linearLayout.addView(composeView1)
             linearLayout.addView(textView2)
-            linearLayout.addView(frameLayout2)
+            linearLayout.addView(composeView2)
 
             fun updateTitle1() {
                 textView1.text = "Compose 1 - ${state1.value}"
@@ -106,7 +104,7 @@
                 textView2.text = "Compose 2 - ${state2.value}"
             }
 
-            frameLayout1.setContent(Recomposer.current()) {
+            composeView1.setContent {
                 MaterialTheme {
                     Surface {
                         TriStateCheckbox(
@@ -123,7 +121,7 @@
                 }
             }
 
-            frameLayout2.setContent(Recomposer.current()) {
+            composeView2.setContent {
                 MaterialTheme {
                     Surface {
                         TriStateCheckbox(
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/SynchronizationMethodsTest.kt b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/SynchronizationMethodsTest.kt
index 2e199ea..3ba39a5 100644
--- a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/SynchronizationMethodsTest.kt
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/SynchronizationMethodsTest.kt
@@ -18,10 +18,8 @@
 
 import androidx.activity.ComponentActivity
 import androidx.compose.testutils.expectError
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.test.onNodeWithTag
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
@@ -51,7 +49,7 @@
 
     @Before
     fun addMockResumedOwner() {
-        androidOwnerRegistry.registerOwner(mockResumedAndroidOwner())
+        androidOwnerRegistry.registerOwner(mockResumedRootForTest())
     }
 
     @Test
@@ -96,7 +94,7 @@
     fun runOnIdle_assert_fails() {
         rule.runOnIdle {
             expectError<IllegalStateException> {
-                rule.onNodeWithTag("dummy").assertExists()
+                rule.onNodeWithTag("placeholder").assertExists()
             }
         }
     }
@@ -119,21 +117,9 @@
         }
     }
 
-    private fun mockResumedAndroidOwner(): AndroidOwner {
-        val lifecycle = mock<Lifecycle>()
-        doReturn(Lifecycle.State.RESUMED).whenever(lifecycle).currentState
-
-        val lifecycleOwner = mock<LifecycleOwner>()
-        doReturn(lifecycle).whenever(lifecycleOwner).lifecycle
-
-        val viewTreeOwners = AndroidOwner.ViewTreeOwners(
-            lifecycleOwner = lifecycleOwner,
-            viewModelStoreOwner = mock(),
-            savedStateRegistryOwner = mock()
-        )
-        val owner = mock<AndroidOwner>()
-        doReturn(viewTreeOwners).whenever(owner).viewTreeOwners
-
+    private fun mockResumedRootForTest(): ViewRootForTest {
+        val owner = mock<ViewRootForTest>()
+        doReturn(true).whenever(owner).isLifecycleInResumedState
         return owner
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/TestAnimationClockTest.kt b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/TestAnimationClockTest.kt
index a6fd566..886546d 100644
--- a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/TestAnimationClockTest.kt
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/TestAnimationClockTest.kt
@@ -87,7 +87,7 @@
             animationState.value = AnimationStates.To
 
             // Changes need to trickle down the animation system, so compose should be non-idle
-            assertThat(composeIdlingResource.isIdle()).isFalse()
+            assertThat(composeIdlingResource.isIdleNow).isFalse()
         }
 
         // Await recomposition
@@ -101,7 +101,7 @@
         // Advance first half of the animation (.5 sec)
         rule.runOnIdle {
             clockTestRule.advanceClock(halfDuration)
-            assertThat(composeIdlingResource.isIdle()).isFalse()
+            assertThat(composeIdlingResource.isIdleNow).isFalse()
         }
 
         // Await next animation frame
@@ -115,7 +115,7 @@
         // Advance second half of the animation (.5 sec)
         rule.runOnIdle {
             clockTestRule.advanceClock(halfDuration)
-            assertThat(composeIdlingResource.isIdle()).isFalse()
+            assertThat(composeIdlingResource.isIdleNow).isFalse()
         }
 
         // Await next animation frame
@@ -150,7 +150,7 @@
             animationState.value = AnimationStates.To
 
             // Changes need to trickle down the animation system, so compose should be non-idle
-            assertThat(composeIdlingResource.isIdle()).isFalse()
+            assertThat(composeIdlingResource.isIdleNow).isFalse()
         }
 
         // Perform a single recomposition by awaiting the same signal as the Recomposer
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/WaitingForOnCommitCallbackTest.kt b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/WaitingForOnCommitCallbackTest.kt
index ab17c2b..644b2b5 100644
--- a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/WaitingForOnCommitCallbackTest.kt
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/WaitingForOnCommitCallbackTest.kt
@@ -21,10 +21,10 @@
 import androidx.compose.runtime.onCommit
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.test.ExperimentalTesting
-import androidx.compose.ui.test.TestUiDispatcher
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
@@ -118,15 +118,12 @@
     }
 
     @Test
-    fun cascadingOnCommits_suspendedWait_defaultDispatcher() = cascadingOnCommits_suspendedWait(
-        EmptyCoroutineContext
-    )
+    fun cascadingOnCommits_suspendedWait_defaultDispatcher() =
+        cascadingOnCommits_suspendedWait(EmptyCoroutineContext)
 
     @Test
-    @OptIn(ExperimentalTesting::class)
-    fun cascadingOnCommits_suspendedWait_mainDispatcher() = cascadingOnCommits_suspendedWait(
-        TestUiDispatcher.Main
-    )
+    fun cascadingOnCommits_suspendedWait_mainDispatcher() =
+        cascadingOnCommits_suspendedWait(Dispatchers.Main)
 
     @OptIn(ExperimentalTesting::class)
     fun cascadingOnCommits_suspendedWait(context: CoroutineContext) = runBlocking(context) {
@@ -182,4 +179,4 @@
             assertThat(values).containsExactly(1, 2, 3, 4, 5, 6).inOrder()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt
index 7fcac78..572d8c2 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt
@@ -23,12 +23,14 @@
 import androidx.compose.runtime.Recomposer
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.test.ExperimentalTesting
+import androidx.compose.ui.test.IdlingResource
 import androidx.compose.ui.test.InternalTestingApi
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.SemanticsNodeInteraction
 import androidx.compose.ui.test.SemanticsNodeInteractionCollection
 import androidx.compose.ui.test.createTestContext
 import androidx.compose.ui.test.junit4.android.ComposeIdlingResource
+import androidx.compose.ui.test.junit4.android.EspressoLink
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.input.textInputServiceFactory
 import androidx.compose.ui.unit.Density
@@ -138,8 +140,13 @@
         activityProvider: (R) -> A
     ) : this(activityRule, activityProvider, false)
 
+    private val idlingResourceRegistry = IdlingResourceRegistry()
+    private val espressoLink = EspressoLink(idlingResourceRegistry)
+
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    internal val composeIdlingResource = ComposeIdlingResource()
+    internal val composeIdlingResource = ComposeIdlingResource().also {
+        registerIdlingResource(it)
+    }
 
     @ExperimentalTesting
     override val clockTestRule: AnimationClockTestRule =
@@ -174,11 +181,13 @@
         }
     }
 
-    override fun apply(base: Statement, description: Description?): Statement {
+    override fun apply(base: Statement, description: Description): Statement {
         @Suppress("NAME_SHADOWING")
         @OptIn(ExperimentalTesting::class)
         return RuleChain
             .outerRule { base, _ -> composeIdlingResource.getStatementFor(base) }
+            .around { base, _ -> idlingResourceRegistry.getStatementFor(base) }
+            .around { base, _ -> espressoLink.getStatementFor(base) }
             .around(clockTestRule)
             .around { base, _ -> AndroidComposeStatement(base) }
             .around(activityRule)
@@ -235,6 +244,14 @@
         return runOnUiThread(action)
     }
 
+    override fun registerIdlingResource(idlingResource: IdlingResource) {
+        idlingResourceRegistry.registerIdlingResource(idlingResource)
+    }
+
+    override fun unregisterIdlingResource(idlingResource: IdlingResource) {
+        idlingResourceRegistry.unregisterIdlingResource(idlingResource)
+    }
+
     inner class AndroidComposeStatement(
         private val base: Statement
     ) : Statement() {
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidTestOwner.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidTestOwner.kt
index b7ad00d..ded27d1 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidTestOwner.kt
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidTestOwner.kt
@@ -18,7 +18,7 @@
 
 import android.annotation.SuppressLint
 import androidx.compose.ui.node.Owner
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.InternalTestingApi
 import androidx.compose.ui.test.TestOwner
@@ -33,7 +33,7 @@
 
     @SuppressLint("DocumentExceptions")
     override fun sendTextInputCommand(node: SemanticsNode, command: List<EditOperation>) {
-        val owner = node.layoutNode.owner as AndroidOwner
+        val owner = node.owner as ViewRootForTest
 
         @Suppress("DEPRECATION")
         runOnUiThread {
@@ -46,7 +46,7 @@
 
     @SuppressLint("DocumentExceptions")
     override fun sendImeAction(node: SemanticsNode, actionSpecified: ImeAction) {
-        val owner = node.layoutNode.owner as AndroidOwner
+        val owner = node.owner as ViewRootForTest
 
         @Suppress("DEPRECATION")
         runOnUiThread {
@@ -66,7 +66,7 @@
         return composeIdlingResource.getOwners()
     }
 
-    private fun AndroidOwner.getTextInputServiceOrDie(): TextInputServiceForTests {
+    private fun ViewRootForTest.getTextInputServiceOrDie(): TextInputServiceForTests {
         return textInputService as? TextInputServiceForTests
             ?: throw IllegalStateException(
                 "Text input service wrapper not set up! Did you use ComposeTestRule?"
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/AndroidOwnerRegistry.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/AndroidOwnerRegistry.kt
index 0138a73..0de743a 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/AndroidOwnerRegistry.kt
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/AndroidOwnerRegistry.kt
@@ -18,9 +18,8 @@
 
 import android.view.View
 import androidx.annotation.VisibleForTesting
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.test.ExperimentalTesting
-import androidx.lifecycle.Lifecycle
 import kotlinx.coroutines.suspendCancellableCoroutine
 import org.junit.runners.model.Statement
 import java.util.Collections
@@ -32,24 +31,25 @@
 import kotlin.time.ExperimentalTime
 
 /**
- * Registry where all [AndroidOwner]s should be registered while they are attached to the window.
- * This registry is used by the testing library to query the owners's state.
+ * Registry where all views implementing [ViewRootForTest] should be registered while they
+ * are attached to the window. This registry is used by the testing library to query the owners's
+ * state.
  */
 internal class AndroidOwnerRegistry {
-    private val owners = Collections.newSetFromMap(WeakHashMap<AndroidOwner, Boolean>())
+    private val owners = Collections.newSetFromMap(WeakHashMap<ViewRootForTest, Boolean>())
     private val registryListeners = mutableSetOf<OnRegistrationChangedListener>()
 
     /**
-     * Returns if the registry is setup to receive registrations from [AndroidOwner]s
+     * Returns if the registry is setup to receive registrations from [ViewRootForTest]s
      */
     val isSetUp: Boolean
-        get() = AndroidOwner.onAndroidOwnerCreatedCallback == ::onAndroidOwnerCreated
+        get() = ViewRootForTest.onViewCreatedCallback == ::onComposeViewCreated
 
     /**
-     * Sets up this registry to be notified of any [AndroidOwner] created
+     * Sets up this registry to be notified of any [ViewRootForTest] created
      */
     private fun setupRegistry() {
-        AndroidOwner.onAndroidOwnerCreatedCallback = ::onAndroidOwnerCreated
+        ViewRootForTest.onViewCreatedCallback = ::onComposeViewCreated
     }
 
     /**
@@ -57,7 +57,7 @@
      */
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     internal fun tearDownRegistry() {
-        AndroidOwner.onAndroidOwnerCreatedCallback = null
+        ViewRootForTest.onViewCreatedCallback = null
         synchronized(owners) {
             getUnfilteredOwners().forEach {
                 unregisterOwner(it)
@@ -67,33 +67,32 @@
         }
     }
 
-    private fun onAndroidOwnerCreated(owner: AndroidOwner) {
+    private fun onComposeViewCreated(owner: ViewRootForTest) {
         owner.view.addOnAttachStateChangeListener(OwnerAttachedListener(owner))
     }
 
     /**
-     * Returns a copy of the set of all registered [AndroidOwner]s, including ones that are
+     * Returns a copy of the set of all registered [ViewRootForTest]s, including ones that are
      * normally not relevant (like those whose lifecycle state is not RESUMED).
      */
-    fun getUnfilteredOwners(): Set<AndroidOwner> {
+    fun getUnfilteredOwners(): Set<ViewRootForTest> {
         return synchronized(owners) { owners.toSet() }
     }
 
     /**
-     * Returns a copy of the set of all registered [AndroidOwner]s that can be interacted with.
+     * Returns a copy of the set of all registered [ViewRootForTest]s that can be interacted with.
      * This method is almost always preferred over [getUnfilteredOwners].
      */
-    fun getOwners(): Set<AndroidOwner> {
+    fun getOwners(): Set<ViewRootForTest> {
         return synchronized(owners) {
             owners.filterTo(mutableSetOf()) {
-                it.viewTreeOwners?.lifecycleOwner
-                    ?.lifecycle?.currentState == Lifecycle.State.RESUMED
+                it.isLifecycleInResumedState
             }
         }
     }
 
     /**
-     * Adds the given [listener], to be notified when an [AndroidOwner] registers or unregisters.
+     * Adds the given [listener], to be notified when an [ViewRootForTest] registers or unregisters.
      */
     fun addOnRegistrationChangedListener(listener: OnRegistrationChangedListener) {
         synchronized(registryListeners) { registryListeners.add(listener) }
@@ -106,7 +105,7 @@
         synchronized(registryListeners) { registryListeners.remove(listener) }
     }
 
-    private fun dispatchOnRegistrationChanged(owner: AndroidOwner, isRegistered: Boolean) {
+    private fun dispatchOnRegistrationChanged(owner: ViewRootForTest, isRegistered: Boolean) {
         synchronized(registryListeners) { registryListeners.toList() }.forEach {
             it.onRegistrationChanged(owner, isRegistered)
         }
@@ -115,7 +114,7 @@
     /**
      * Registers the [owner] in this registry. Must be called from [View.onAttachedToWindow].
      */
-    internal fun registerOwner(owner: AndroidOwner) {
+    internal fun registerOwner(owner: ViewRootForTest) {
         synchronized(owners) {
             if (owners.add(owner)) {
                 dispatchOnRegistrationChanged(owner, true)
@@ -126,7 +125,7 @@
     /**
      * Unregisters the [owner] from this registry. Must be called from [View.onDetachedFromWindow].
      */
-    internal fun unregisterOwner(owner: AndroidOwner) {
+    internal fun unregisterOwner(owner: ViewRootForTest) {
         synchronized(owners) {
             if (owners.remove(owner)) {
                 dispatchOnRegistrationChanged(owner, false)
@@ -148,15 +147,15 @@
     }
 
     /**
-     * Interface to be implemented by components that want to be notified when an [AndroidOwner]
+     * Interface to be implemented by components that want to be notified when an [ViewRootForTest]
      * registers or unregisters at this registry.
      */
     interface OnRegistrationChangedListener {
-        fun onRegistrationChanged(owner: AndroidOwner, registered: Boolean)
+        fun onRegistrationChanged(owner: ViewRootForTest, registered: Boolean)
     }
 
     private inner class OwnerAttachedListener(
-        private val owner: AndroidOwner
+        private val owner: ViewRootForTest
     ) : View.OnAttachStateChangeListener {
 
         // Note: owner.view === view, because the owner _is_ the view,
@@ -187,7 +186,7 @@
     if (!hasAndroidOwners) {
         val latch = CountDownLatch(1)
         val listener = object : AndroidOwnerRegistry.OnRegistrationChangedListener {
-            override fun onRegistrationChanged(owner: AndroidOwner, registered: Boolean) {
+            override fun onRegistrationChanged(owner: ViewRootForTest, registered: Boolean) {
                 if (hasAndroidOwners) {
                     latch.countDown()
                 }
@@ -220,9 +219,12 @@
                 }
             }
 
-            // Usually we resume if an AndroidOwner is registered while the listener is added
+            // Usually we resume if an ComposeViewTestMarker is registered while the listener is added
             val listener = object : AndroidOwnerRegistry.OnRegistrationChangedListener {
-                override fun onRegistrationChanged(owner: AndroidOwner, registered: Boolean) {
+                override fun onRegistrationChanged(
+                    owner: ViewRootForTest,
+                    registered: Boolean
+                ) {
                     if (hasAndroidOwners) {
                         resume(this)
                     }
@@ -234,7 +236,7 @@
                 removeOnRegistrationChangedListener(listener)
             }
 
-            // Sometimes the AndroidOwner was registered before we added
+            // Sometimes the ComposeViewTestMarker was registered before we added
             // the listener, in which case we missed our signal
             if (hasAndroidOwners) {
                 resume(listener)
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeIdlingResource.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeIdlingResource.kt
index ee8879a..df32ab2 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeIdlingResource.kt
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeIdlingResource.kt
@@ -16,36 +16,19 @@
 
 package androidx.compose.ui.test.junit4.android
 
-import android.os.Handler
-import android.os.Looper
 import androidx.annotation.VisibleForTesting
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.ui.node.Owner
 import androidx.compose.ui.test.ExperimentalTesting
+import androidx.compose.ui.test.IdlingResource
 import androidx.compose.ui.test.TestAnimationClock
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.junit4.isOnUiThread
-import androidx.compose.ui.test.junit4.runOnUiThread
-import androidx.test.espresso.AppNotIdleException
-import androidx.test.espresso.Espresso
-import androidx.test.espresso.IdlingRegistry
-import androidx.test.espresso.IdlingResource
-import androidx.test.espresso.IdlingResourceTimeoutException
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import org.junit.runners.model.Statement
-import java.util.concurrent.atomic.AtomicInteger
-
-/**
- * In case Espresso times out, implementing this interface enables our resources to explain why
- * they failed to synchronize in case they were busy.
- */
-internal interface IdlingResourceWithDiagnostics {
-    // TODO: Consider this as a public API.
-    fun getDiagnosticMessageIfBusy(): String?
-}
 
 /**
  * Register compose's idling check to Espresso.
@@ -119,12 +102,7 @@
  * resource is automatically registered when any compose testing APIs are used including
  * [createAndroidComposeRule].
  */
-internal class ComposeIdlingResource : IdlingResource, IdlingResourceWithDiagnostics {
-
-    override fun getName(): String = "ComposeIdlingResource"
-
-    private var isIdleCheckScheduled = false
-    private var resourceCallback: IdlingResource.ResourceCallback? = null
+internal class ComposeIdlingResource : IdlingResource {
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     internal val androidOwnerRegistry = AndroidOwnerRegistry()
@@ -132,29 +110,22 @@
     @OptIn(ExperimentalTesting::class)
     private val clocks = mutableSetOf<TestAnimationClock>()
 
-    private val handler = Handler(Looper.getMainLooper())
-
     private var hadAnimationClocksIdle = true
     private var hadNoSnapshotChanges = true
     private var hadNoRecomposerChanges = true
-    private var lastCompositionAwaiters = 0
     private var hadNoPendingMeasureLayout = true
     // TODO(b/174244530): Include hadNoPendingDraw when it is reliable
 //    private var hadNoPendingDraw = true
 
-    private var compositionAwaiters = AtomicInteger(0)
-
     /**
-     * Returns whether or not Compose is idle, without starting to poll if it is not.
+     * Returns whether or not Compose is idle now.
      */
-    @OptIn(ExperimentalComposeApi::class)
-    fun isIdle(): Boolean {
-        @Suppress("DEPRECATION")
-        return runOnUiThread {
+    override val isIdleNow: Boolean
+        @OptIn(ExperimentalComposeApi::class)
+        get() {
             hadNoSnapshotChanges = !Snapshot.current.hasPendingChanges()
             hadNoRecomposerChanges = !Recomposer.current().hasInvalidations()
             hadAnimationClocksIdle = areAllClocksIdle()
-            lastCompositionAwaiters = compositionAwaiters.get()
             val owners = androidOwnerRegistry.getUnfilteredOwners()
             hadNoPendingMeasureLayout = !owners.any { it.hasPendingMeasureOrLayout }
             // TODO(b/174244530): Include hadNoPendingDraw when it is reliable
@@ -163,72 +134,13 @@
 //                it.view.isDirty && (hasContent || it.view.isLayoutRequested)
 //            }
 
-            check(lastCompositionAwaiters >= 0) {
-                "More CompositionAwaiters were removed then added ($lastCompositionAwaiters)"
-            }
-
-            hadNoSnapshotChanges &&
+            return hadNoSnapshotChanges &&
                 hadNoRecomposerChanges &&
                 hadAnimationClocksIdle &&
-                lastCompositionAwaiters == 0 &&
                 // TODO(b/174244530): Include hadNoPendingDraw when it is reliable
                 hadNoPendingMeasureLayout /*&&
                 hadNoPendingDraw*/
         }
-    }
-
-    /**
-     * Returns whether or not Compose is idle, and starts polling if it is not. Will always be
-     * called from the main thread by Espresso, and should _only_ be called from Espresso. Use
-     * [isIdle] if you need to query the idleness of Compose manually.
-     */
-    override fun isIdleNow(): Boolean {
-        val isIdle = isIdle()
-        if (!isIdle) {
-            scheduleIdleCheck()
-        }
-        return isIdle
-    }
-
-    private fun scheduleIdleCheck() {
-        if (!isIdleCheckScheduled) {
-            isIdleCheckScheduled = true
-            handler.postDelayed(
-                {
-                    isIdleCheckScheduled = false
-                    if (isIdle()) {
-                        transitionToIdle()
-                    } else {
-                        scheduleIdleCheck()
-                    }
-                }, /* delayMillis = */ 20
-            )
-        }
-    }
-
-    private fun transitionToIdle() {
-        resourceCallback?.onTransitionToIdle()
-    }
-
-    override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
-        resourceCallback = callback
-    }
-
-    /**
-     * Called by [CompositionAwaiter] to indicate that this [ComposeIdlingResource] should report
-     * busy to Espresso while that [CompositionAwaiter] is checking idleness.
-     */
-    internal fun addCompositionAwaiter() {
-        compositionAwaiters.incrementAndGet()
-    }
-
-    /**
-     * Called by [CompositionAwaiter] to indicate that this [ComposeIdlingResource] can report
-     * idle as far as the calling [CompositionAwaiter] is concerned.
-     */
-    internal fun removeCompositionAwaiter() {
-        compositionAwaiters.decrementAndGet()
-    }
 
     @OptIn(ExperimentalTesting::class)
     fun registerTestClock(clock: TestAnimationClock) {
@@ -255,14 +167,13 @@
         val hadSnapshotChanges = !hadNoSnapshotChanges
         val hadRecomposerChanges = !hadNoRecomposerChanges
         val hadRunningAnimations = !hadAnimationClocksIdle
-        val numCompositionAwaiters = lastCompositionAwaiters
-        val wasAwaitingCompositions = numCompositionAwaiters > 0
         val hadPendingMeasureLayout = !hadNoPendingMeasureLayout
         // TODO(b/174244530): Include hadNoPendingDraw when it is reliable
 //        val hadPendingDraw = !hadNoPendingDraw
 
-        val wasIdle = !hadSnapshotChanges && !hadRecomposerChanges &&
-            !hadRunningAnimations && !wasAwaitingCompositions
+        val wasIdle = !hadSnapshotChanges && !hadRecomposerChanges && !hadRunningAnimations &&
+            // TODO(b/174244530): Include hadNoPendingDraw when it is reliable
+            !hadPendingMeasureLayout /*&& !hadPendingDraw*/
 
         if (wasIdle) {
             return null
@@ -272,21 +183,20 @@
         if (hadRunningAnimations) {
             busyReasons.add("animations")
         }
-        val busyRecomposing = hadSnapshotChanges || hadRecomposerChanges || wasAwaitingCompositions
+        val busyRecomposing = hadSnapshotChanges || hadRecomposerChanges
         if (busyRecomposing) {
             busyReasons.add("pending recompositions")
         }
+        if (hadPendingMeasureLayout) {
+            busyReasons.add("pending measure/layout")
+        }
 
-        var message = "$name is busy due to ${busyReasons.joinToString(", ")}.\n"
+        var message = "${javaClass.simpleName} is busy due to ${busyReasons.joinToString(", ")}.\n"
         if (busyRecomposing) {
             message += "- Note: Timeout on pending recomposition means that there are most likely" +
                 " infinite re-compositions happening in the tested code.\n"
             message += "- Debug: hadRecomposerChanges = $hadRecomposerChanges, "
-            message += "hadSnapshotChanges = $hadSnapshotChanges, "
-            message += "numCompositionAwaiters = $numCompositionAwaiters, "
-            message += "hadPendingMeasureLayout = $hadPendingMeasureLayout"
-            // TODO(b/174244530): Include hadNoPendingDraw when it is reliable
-//            message += ", hadPendingDraw = $hadPendingDraw"
+            message += "hadSnapshotChanges = $hadSnapshotChanges"
         }
         return message
     }
@@ -341,101 +251,6 @@
     }
 
     fun getStatementFor(base: Statement): Statement {
-        return androidOwnerRegistry.getStatementFor(
-            object : Statement() {
-                override fun evaluate() {
-                    try {
-                        IdlingRegistry.getInstance().register(this@ComposeIdlingResource)
-                        base.evaluate()
-                    } finally {
-                        IdlingRegistry.getInstance().unregister(this@ComposeIdlingResource)
-                    }
-                }
-            }
-        )
+        return androidOwnerRegistry.getStatementFor(base)
     }
 }
-
-// TODO(b/168223213): Make the CompositionAwaiter a suspend fun, remove ComposeIdlingResource
-//  and blocking await Espresso.onIdle().
-internal fun ComposeIdlingResource.runEspressoOnIdle() {
-    val compositionAwaiter = CompositionAwaiter(this)
-    try {
-        compositionAwaiter.start()
-        Espresso.onIdle()
-    } catch (e: Throwable) {
-        compositionAwaiter.cancel()
-
-        // Happens on the global time out, usually when global idling time out is less
-        // or equal to dynamic idling time out or when the timeout is not due to individual
-        // idling resource. This does not necessary mean that it can't be due to idling
-        // resource being busy. So we try to check if it failed due to compose being busy and
-        // add some extra information to the developer.
-        val appNotIdleMaybe = tryToFindCause<AppNotIdleException>(e)
-        if (appNotIdleMaybe != null) {
-            rethrowWithMoreInfo(appNotIdleMaybe, wasGlobalTimeout = true)
-        }
-
-        // Happens on idling resource taking too long. Espresso gives out which resources caused
-        // it but it won't allow us to give any extra information. So we check if it was our
-        // resource and give more info if we can.
-        val resourceNotIdleMaybe = tryToFindCause<IdlingResourceTimeoutException>(e)
-        if (resourceNotIdleMaybe != null) {
-            rethrowWithMoreInfo(resourceNotIdleMaybe, wasGlobalTimeout = false)
-        }
-
-        // No match, rethrow
-        throw e
-    }
-}
-
-private fun rethrowWithMoreInfo(e: Throwable, wasGlobalTimeout: Boolean) {
-    var diagnosticInfo = ""
-    val listOfIdlingResources = mutableListOf<String>()
-    IdlingRegistry.getInstance().resources.forEach { resource ->
-        if (resource is IdlingResourceWithDiagnostics) {
-            val message = resource.getDiagnosticMessageIfBusy()
-            if (message != null) {
-                diagnosticInfo += "$message \n"
-            }
-        }
-        listOfIdlingResources.add(resource.name)
-    }
-    if (diagnosticInfo.isNotEmpty()) {
-        val prefix = if (wasGlobalTimeout) {
-            "Global time out"
-        } else {
-            "Idling resource timed out"
-        }
-        throw ComposeNotIdleException(
-            "$prefix: possibly due to compose being busy.\n" +
-                diagnosticInfo +
-                "All registered idling resources: " +
-                listOfIdlingResources.joinToString(", "),
-            e
-        )
-    }
-    // No extra info, re-throw the original exception
-    throw e
-}
-
-/**
- * Tries to find if the given exception or any of its cause is of the type of the provided
- * throwable T. Returns null if there is no match. This is required as some exceptions end up
- * wrapped in Runtime or Concurrent exceptions.
- */
-private inline fun <reified T : Throwable> tryToFindCause(e: Throwable): Throwable? {
-    var causeToCheck: Throwable? = e
-    while (causeToCheck != null) {
-        if (causeToCheck is T) {
-            return causeToCheck
-        }
-        causeToCheck = causeToCheck.cause
-    }
-    return null
-}
-
-/**
- * Thrown in cases where Compose can't get idle in Espresso's defined time limit.
- */
-class ComposeNotIdleException(message: String?, cause: Throwable?) : Throwable(message, cause)
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/CompositionAwaiter.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/CompositionAwaiter.kt
deleted file mode 100644
index dd380e9..0000000
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/CompositionAwaiter.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.test.junit4.android
-
-import android.os.Handler
-import android.os.Looper
-import android.view.Choreographer
-import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.snapshots.Snapshot
-import androidx.compose.ui.test.junit4.runOnUiThread
-
-internal class CompositionAwaiter(private val composeIdlingResource: ComposeIdlingResource) {
-
-    private enum class State {
-        Initialized, Running, Finished, Cancelled
-    }
-
-    private val lock = Any()
-    private var state = State.Initialized
-
-    private val handler = Handler(Looper.getMainLooper())
-    @Suppress("DEPRECATION")
-    private val choreographer = runOnUiThread { Choreographer.getInstance() }
-
-    /**
-     * Starts this awaiter, if it wasn't started, cancelled or finished yet.
-     */
-    fun start() {
-        ifStateIsIn(State.Initialized) {
-            state = State.Running
-            startIdlingResource()
-            handler.post(callback)
-        }
-    }
-
-    /**
-     * Cancels this awaiter if it is running or not yet started. Does nothing if it was already
-     * finished or cancelled.
-     */
-    fun cancel() {
-        ifStateIsIn(State.Initialized, State.Running) {
-            state = State.Cancelled
-            stopIdlingResource()
-            handler.removeCallbacks(callback)
-            choreographer.removeFrameCallback(callback)
-        }
-    }
-
-    private fun startIdlingResource() {
-        composeIdlingResource.addCompositionAwaiter()
-    }
-
-    private fun stopIdlingResource() {
-        composeIdlingResource.removeCompositionAwaiter()
-    }
-
-    /**
-     * Runs the given [block] if the current [state] is the [validStates]. Synchronizes on
-     * [lock] to make it thread-safe.
-     */
-    private inline fun ifStateIsIn(vararg validStates: State, block: () -> Unit) {
-        try {
-            synchronized(lock) {
-                if (state in validStates) {
-                    block()
-                }
-            }
-        } catch (t: Throwable) {
-            cancel()
-            throw t
-        }
-    }
-
-    @OptIn(ExperimentalComposeApi::class)
-    private fun isIdle(): Boolean {
-        return !Snapshot.current.hasPendingChanges() && !Recomposer.current().hasInvalidations()
-    }
-
-    private val callback = object : Runnable, Choreographer.FrameCallback {
-        override fun run() {
-            ifStateIsIn(State.Running) {
-                if (!isIdle()) {
-                    // not idle, restart check. this makes sure our frame callback
-                    // will be executed _after_ potentially scheduled onCommits
-                    handler.postDelayed(this, 10)
-                } else {
-                    // Is idle. Either nothing is scheduled on the choreographer, in which
-                    // case our callback will be the only one, or something is scheduled on
-                    // the choreographer, in which case our callback will be after it
-                    choreographer.postFrameCallback(this)
-                }
-            }
-        }
-
-        override fun doFrame(frameTime: Long) {
-            ifStateIsIn(State.Running) {
-                if (!isIdle()) {
-                    // not idle, restart check. onCommits have triggered a recomposition
-                    handler.postDelayed(this, 10)
-                } else {
-                    // is idle. onCommits have _not_ triggered a
-                    // recomposition, or there were no onCommits
-                    state = State.Finished
-                    stopIdlingResource()
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/EspressoLink.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/EspressoLink.kt
new file mode 100644
index 0000000..631caf0
--- /dev/null
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/EspressoLink.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.test.junit4.android
+
+import androidx.compose.ui.test.junit4.IdlingResourceRegistry
+import androidx.test.espresso.AppNotIdleException
+import androidx.test.espresso.Espresso
+import androidx.test.espresso.IdlingRegistry
+import androidx.test.espresso.IdlingResource
+import androidx.test.espresso.IdlingResourceTimeoutException
+import org.junit.runners.model.Statement
+
+internal class EspressoLink(private val registry: IdlingResourceRegistry) : IdlingResource {
+
+    override fun getName(): String = "Compose-Espresso link"
+
+    override fun isIdleNow(): Boolean {
+        return registry.isIdleOrEnsurePolling()
+    }
+
+    override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
+        registry.setOnIdleCallback {
+            callback?.onTransitionToIdle()
+        }
+    }
+
+    fun getDiagnosticMessageIfBusy(): String? = registry.getDiagnosticMessageIfBusy()
+
+    fun getStatementFor(base: Statement): Statement {
+        return object : Statement() {
+            override fun evaluate() {
+                try {
+                    IdlingRegistry.getInstance().register(this@EspressoLink)
+                    base.evaluate()
+                } finally {
+                    IdlingRegistry.getInstance().unregister(this@EspressoLink)
+                }
+            }
+        }
+    }
+}
+
+// TODO(b/168223213): Make the CompositionAwaiter a suspend fun, remove ComposeIdlingResource
+//  and blocking await Espresso.onIdle().
+internal fun runEspressoOnIdle() {
+    try {
+        Espresso.onIdle()
+    } catch (e: Throwable) {
+
+        // Happens on the global time out, usually when global idling time out is less
+        // or equal to dynamic idling time out or when the timeout is not due to individual
+        // idling resource. This does not necessary mean that it can't be due to idling
+        // resource being busy. So we try to check if it failed due to compose being busy and
+        // add some extra information to the developer.
+        val appNotIdleMaybe = tryToFindCause<AppNotIdleException>(e)
+        if (appNotIdleMaybe != null) {
+            rethrowWithMoreInfo(appNotIdleMaybe, wasGlobalTimeout = true)
+        }
+
+        // Happens on idling resource taking too long. Espresso gives out which resources caused
+        // it but it won't allow us to give any extra information. So we check if it was our
+        // resource and give more info if we can.
+        val resourceNotIdleMaybe = tryToFindCause<IdlingResourceTimeoutException>(e)
+        if (resourceNotIdleMaybe != null) {
+            rethrowWithMoreInfo(resourceNotIdleMaybe, wasGlobalTimeout = false)
+        }
+
+        // No match, rethrow
+        throw e
+    }
+}
+
+private fun rethrowWithMoreInfo(e: Throwable, wasGlobalTimeout: Boolean) {
+    var diagnosticInfo = ""
+    val listOfIdlingResources = mutableListOf<String>()
+    IdlingRegistry.getInstance().resources.forEach { resource ->
+        if (resource is EspressoLink) {
+            val message = resource.getDiagnosticMessageIfBusy()
+            if (message != null) {
+                diagnosticInfo += "$message \n"
+            }
+        }
+        listOfIdlingResources.add(resource.name)
+    }
+    if (diagnosticInfo.isNotEmpty()) {
+        val prefix = if (wasGlobalTimeout) {
+            "Global time out"
+        } else {
+            "Idling resource timed out"
+        }
+        throw ComposeNotIdleException(
+            "$prefix: possibly due to compose being busy.\n" +
+                diagnosticInfo +
+                "All registered idling resources: " +
+                listOfIdlingResources.joinToString(", "),
+            e
+        )
+    }
+    // No extra info, re-throw the original exception
+    throw e
+}
+
+/**
+ * Tries to find if the given exception or any of its cause is of the type of the provided
+ * throwable T. Returns null if there is no match. This is required as some exceptions end up
+ * wrapped in Runtime or Concurrent exceptions.
+ */
+private inline fun <reified T : Throwable> tryToFindCause(e: Throwable): Throwable? {
+    var causeToCheck: Throwable? = e
+    while (causeToCheck != null) {
+        if (causeToCheck is T) {
+            return causeToCheck
+        }
+        causeToCheck = causeToCheck.cause
+    }
+    return null
+}
+
+/**
+ * Thrown in cases where Compose can't get idle in Espresso's defined time limit.
+ */
+class ComposeNotIdleException(message: String?, cause: Throwable?) : Throwable(message, cause)
diff --git a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
index 282ac47..f66b098 100644
--- a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
+++ b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
@@ -26,6 +26,7 @@
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.ExperimentalTesting
+import androidx.compose.ui.test.IdlingResource
 import androidx.compose.ui.test.InternalTestingApi
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.SemanticsNodeInteraction
@@ -130,6 +131,14 @@
         return action().also { waitForIdle() }
     }
 
+    override fun registerIdlingResource(idlingResource: IdlingResource) {
+        // TODO: implement
+    }
+
+    override fun unregisterIdlingResource(idlingResource: IdlingResource) {
+        // TODO: implement
+    }
+
     override fun setContent(composable: @Composable () -> Unit) {
         check(owner == null) {
             "Cannot call setContent twice per test!"
@@ -156,7 +165,7 @@
             owners = it
         }
         val owner = DesktopOwner(owners)
-        owner.setContent(composable)
+        owner.setContent(content = composable)
         owner.setSize(displaySize.width, displaySize.height)
         owner.measureAndLayout()
         owner.draw(canvas)
diff --git a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.kt b/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.kt
index 9fb0445..1190b77 100644
--- a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.kt
+++ b/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.test.ExperimentalTesting
+import androidx.compose.ui.test.IdlingResource
 import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
@@ -82,6 +83,16 @@
     suspend fun awaitIdle()
 
     /**
+     * Registers an [IdlingResource] in this test.
+     */
+    fun registerIdlingResource(idlingResource: IdlingResource)
+
+    /**
+     * Unregisters an [IdlingResource] from this test.
+     */
+    fun unregisterIdlingResource(idlingResource: IdlingResource)
+
+    /**
      * Sets the given composable as a content of the current screen.
      *
      * Use this in your tests to setup the UI content to be tested. This should be called exactly
diff --git a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistry.kt b/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistry.kt
new file mode 100644
index 0000000..6ffd62e
--- /dev/null
+++ b/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistry.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.test.junit4
+
+import androidx.annotation.VisibleForTesting
+import androidx.compose.ui.test.IdlingResource
+import androidx.compose.ui.test.InternalTestingApi
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import org.junit.runners.model.Statement
+
+internal class IdlingResourceRegistry
+@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+@InternalTestingApi
+internal constructor(
+    private val pollScopeOverride: CoroutineScope?
+) : IdlingResource {
+    // Publicly facing constructor, that doesn't override the poll scope
+    @OptIn(InternalTestingApi::class)
+    constructor() : this(null)
+
+    private val lock = Any()
+
+    // All registered IdlingResources, both idle and busy ones
+    private val idlingResources = mutableSetOf<IdlingResource>()
+    // Each busy resource is mapped to the job that polls it
+    private val busyResources = mutableSetOf<IdlingResource>()
+    // The job that polls the resources until they are idle
+    private var pollJob: Job = Job().also { it.complete() }
+    // The scope in which to launch the poll job, or await the poll job
+    private val pollScope = pollScopeOverride ?: CoroutineScope(Dispatchers.Main)
+
+    private val isPolling: Boolean
+        get() = !pollJob.isCompleted
+
+    // Callback to be called every time when the last busy resource becomes idle
+    private var onIdle: (() -> Unit)? = null
+
+    /**
+     * Returns if all resources are idle
+     */
+    override val isIdleNow: Boolean get() {
+        @Suppress("DEPRECATION_ERROR")
+        return synchronized(lock) {
+            // If a poll job is running, we're not idle now. Let the job do its job.
+            !isPolling && areAllResourcesIdle()
+        }
+    }
+
+    /**
+     * Installs a callback that will be called when the registry transitions from busy to idle.
+     * Intended for the owner of the registry (e.g. AndroidComposeTestRule).
+     */
+    internal fun setOnIdleCallback(callback: () -> Unit) {
+        onIdle = callback
+    }
+
+    /**
+     * Registers the [idlingResource] into the registry
+     */
+    fun registerIdlingResource(idlingResource: IdlingResource) {
+        @Suppress("DEPRECATION_ERROR")
+        synchronized(lock) {
+            idlingResources.add(idlingResource)
+        }
+    }
+
+    /**
+     * Unregisters the [idlingResource] from the registry
+     */
+    fun unregisterIdlingResource(idlingResource: IdlingResource) {
+        @Suppress("DEPRECATION_ERROR")
+        synchronized(lock) {
+            idlingResources.remove(idlingResource)
+            busyResources.remove(idlingResource)
+        }
+    }
+
+    /**
+     * Starts polling the resources until all resources are idle. Won't start polling if all
+     * resources are already idle when this method is invoked, or if polling has already been
+     * started.
+     */
+    internal fun isIdleOrEnsurePolling(): Boolean {
+        @Suppress("DEPRECATION_ERROR")
+        return synchronized(lock) {
+            !isPolling && areAllResourcesIdle().also { isIdle ->
+                if (!isIdle) {
+                    pollJob = pollScope.launch {
+                        do {
+                            delay(20)
+                        } while (!areAllResourcesIdle())
+                        onIdle?.invoke()
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks all resources for idleness, updates [busyResources] and returns if the registry is
+     * idle now.
+     */
+    private fun areAllResourcesIdle(): Boolean {
+        @Suppress("DEPRECATION_ERROR")
+        return synchronized(lock) {
+            busyResources.clear()
+            idlingResources.filterTo(busyResources) { !it.isIdleNow }.isEmpty()
+        }
+    }
+
+    override fun getDiagnosticMessageIfBusy(): String? {
+        val (idle, busy) =
+            @Suppress("DEPRECATION_ERROR")
+            synchronized(lock) {
+                if (busyResources.isEmpty()) {
+                    return null
+                }
+                Pair(
+                    (idlingResources - busyResources).toList(),
+                    busyResources.map { it.getDiagnosticMessageIfBusy() ?: it.toString() }
+                )
+            }
+        return "IdlingResourceRegistry has the following idling resources registered:" +
+            busy.joinToString { "\n- [busy] ${it.indentBy("         ")}" } +
+            idle.joinToString { "\n- [idle] $it" } +
+            if (idle.isEmpty() && busy.isEmpty()) "\n<none>" else ""
+    }
+
+    /**
+     * Adds the given [prefix] after all non-terminal new lines.
+     *
+     * For example: `"\nfoo\nbar\n".indentBy("-")` gives `"\n-foo\n-bar\n"`
+     */
+    private fun String.indentBy(prefix: String): String {
+        return replace("\n(?=.)".toRegex(), "\n$prefix")
+    }
+
+    fun getStatementFor(base: Statement): Statement {
+        return object : Statement() {
+            override fun evaluate() {
+                try {
+                    base.evaluate()
+                } finally {
+                    if (pollScopeOverride == null) {
+                        if (pollScope.coroutineContext[Job] != null) pollScope.cancel()
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/compose/ui/ui-test/api/current.txt b/compose/ui/ui-test/api/current.txt
index 8144343..d88b6ad 100644
--- a/compose/ui/ui-test/api/current.txt
+++ b/compose/ui/ui-test/api/current.txt
@@ -86,9 +86,9 @@
     method public static androidx.compose.ui.test.SemanticsMatcher hasAnyDescendant(androidx.compose.ui.test.SemanticsMatcher matcher);
     method public static androidx.compose.ui.test.SemanticsMatcher hasAnySibling(androidx.compose.ui.test.SemanticsMatcher matcher);
     method public static androidx.compose.ui.test.SemanticsMatcher hasClickAction();
+    method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescription(String label, optional boolean ignoreCase);
     method public static androidx.compose.ui.test.SemanticsMatcher hasImeAction(androidx.compose.ui.text.input.ImeAction actionType);
     method @Deprecated public static androidx.compose.ui.test.SemanticsMatcher hasInputMethodsSupport();
-    method public static androidx.compose.ui.test.SemanticsMatcher hasLabel(String label, optional boolean ignoreCase);
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoClickAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoScrollAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasParent(androidx.compose.ui.test.SemanticsMatcher matcher);
@@ -120,11 +120,11 @@
   }
 
   public final class FindersKt {
-    method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithLabel(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
+    method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithContentDescription(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithSubstring(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithTag(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String testTag, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithText(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
-    method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithLabel(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
+    method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithContentDescription(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithSubstring(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithTag(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String testTag, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithText(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
@@ -179,6 +179,12 @@
     method public static void up(androidx.compose.ui.test.GestureScope, optional int pointerId);
   }
 
+  public interface IdlingResource {
+    method public default String? getDiagnosticMessageIfBusy();
+    method public boolean isIdleNow();
+    property public abstract boolean isIdleNow;
+  }
+
   @kotlin.RequiresOptIn(message="This is internal API for Compose modules that may change frequently and without warning.") public @interface InternalTestingApi {
   }
 
@@ -301,8 +307,8 @@
   }
 
   @androidx.compose.ui.test.ExperimentalTesting public final class TestUiDispatcher {
-    method public kotlin.coroutines.CoroutineContext getMain();
-    property public final kotlin.coroutines.CoroutineContext Main;
+    method @Deprecated public kotlin.coroutines.CoroutineContext getMain();
+    property @Deprecated public final kotlin.coroutines.CoroutineContext Main;
     field public static final androidx.compose.ui.test.TestUiDispatcher INSTANCE;
   }
 
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 8144343..d88b6ad 100644
--- a/compose/ui/ui-test/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-test/api/public_plus_experimental_current.txt
@@ -86,9 +86,9 @@
     method public static androidx.compose.ui.test.SemanticsMatcher hasAnyDescendant(androidx.compose.ui.test.SemanticsMatcher matcher);
     method public static androidx.compose.ui.test.SemanticsMatcher hasAnySibling(androidx.compose.ui.test.SemanticsMatcher matcher);
     method public static androidx.compose.ui.test.SemanticsMatcher hasClickAction();
+    method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescription(String label, optional boolean ignoreCase);
     method public static androidx.compose.ui.test.SemanticsMatcher hasImeAction(androidx.compose.ui.text.input.ImeAction actionType);
     method @Deprecated public static androidx.compose.ui.test.SemanticsMatcher hasInputMethodsSupport();
-    method public static androidx.compose.ui.test.SemanticsMatcher hasLabel(String label, optional boolean ignoreCase);
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoClickAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoScrollAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasParent(androidx.compose.ui.test.SemanticsMatcher matcher);
@@ -120,11 +120,11 @@
   }
 
   public final class FindersKt {
-    method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithLabel(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
+    method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithContentDescription(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithSubstring(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithTag(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String testTag, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithText(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
-    method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithLabel(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
+    method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithContentDescription(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithSubstring(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithTag(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String testTag, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithText(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
@@ -179,6 +179,12 @@
     method public static void up(androidx.compose.ui.test.GestureScope, optional int pointerId);
   }
 
+  public interface IdlingResource {
+    method public default String? getDiagnosticMessageIfBusy();
+    method public boolean isIdleNow();
+    property public abstract boolean isIdleNow;
+  }
+
   @kotlin.RequiresOptIn(message="This is internal API for Compose modules that may change frequently and without warning.") public @interface InternalTestingApi {
   }
 
@@ -301,8 +307,8 @@
   }
 
   @androidx.compose.ui.test.ExperimentalTesting public final class TestUiDispatcher {
-    method public kotlin.coroutines.CoroutineContext getMain();
-    property public final kotlin.coroutines.CoroutineContext Main;
+    method @Deprecated public kotlin.coroutines.CoroutineContext getMain();
+    property @Deprecated public final kotlin.coroutines.CoroutineContext Main;
     field public static final androidx.compose.ui.test.TestUiDispatcher INSTANCE;
   }
 
diff --git a/compose/ui/ui-test/api/restricted_current.txt b/compose/ui/ui-test/api/restricted_current.txt
index 8144343..d88b6ad 100644
--- a/compose/ui/ui-test/api/restricted_current.txt
+++ b/compose/ui/ui-test/api/restricted_current.txt
@@ -86,9 +86,9 @@
     method public static androidx.compose.ui.test.SemanticsMatcher hasAnyDescendant(androidx.compose.ui.test.SemanticsMatcher matcher);
     method public static androidx.compose.ui.test.SemanticsMatcher hasAnySibling(androidx.compose.ui.test.SemanticsMatcher matcher);
     method public static androidx.compose.ui.test.SemanticsMatcher hasClickAction();
+    method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescription(String label, optional boolean ignoreCase);
     method public static androidx.compose.ui.test.SemanticsMatcher hasImeAction(androidx.compose.ui.text.input.ImeAction actionType);
     method @Deprecated public static androidx.compose.ui.test.SemanticsMatcher hasInputMethodsSupport();
-    method public static androidx.compose.ui.test.SemanticsMatcher hasLabel(String label, optional boolean ignoreCase);
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoClickAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoScrollAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasParent(androidx.compose.ui.test.SemanticsMatcher matcher);
@@ -120,11 +120,11 @@
   }
 
   public final class FindersKt {
-    method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithLabel(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
+    method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithContentDescription(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithSubstring(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithTag(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String testTag, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithText(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
-    method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithLabel(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
+    method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithContentDescription(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithSubstring(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithTag(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String testTag, optional boolean useUnmergedTree);
     method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithText(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean ignoreCase, optional boolean useUnmergedTree);
@@ -179,6 +179,12 @@
     method public static void up(androidx.compose.ui.test.GestureScope, optional int pointerId);
   }
 
+  public interface IdlingResource {
+    method public default String? getDiagnosticMessageIfBusy();
+    method public boolean isIdleNow();
+    property public abstract boolean isIdleNow;
+  }
+
   @kotlin.RequiresOptIn(message="This is internal API for Compose modules that may change frequently and without warning.") public @interface InternalTestingApi {
   }
 
@@ -301,8 +307,8 @@
   }
 
   @androidx.compose.ui.test.ExperimentalTesting public final class TestUiDispatcher {
-    method public kotlin.coroutines.CoroutineContext getMain();
-    property public final kotlin.coroutines.CoroutineContext Main;
+    method @Deprecated public kotlin.coroutines.CoroutineContext getMain();
+    property @Deprecated public final kotlin.coroutines.CoroutineContext Main;
     field public static final androidx.compose.ui.test.TestUiDispatcher INSTANCE;
   }
 
diff --git a/compose/ui/ui-test/build.gradle b/compose/ui/ui-test/build.gradle
index 7a19f17..3e55c2a 100644
--- a/compose/ui/ui-test/build.gradle
+++ b/compose/ui/ui-test/build.gradle
@@ -128,7 +128,6 @@
 android {
     tasks.withType(KotlinCompile).configureEach {
         kotlinOptions {
-            freeCompilerArgs += ["-XXLanguage:-NewInference"]
             useIR = true
         }
     }
diff --git a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/CallSemanticsActionTest.kt b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/CallSemanticsActionTest.kt
index 29ee38a..9319748 100644
--- a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/CallSemanticsActionTest.kt
+++ b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/CallSemanticsActionTest.kt
@@ -24,7 +24,7 @@
 import androidx.compose.ui.semantics.AccessibilityAction
 import androidx.compose.ui.semantics.SemanticsPropertyKey
 import androidx.compose.ui.semantics.SemanticsPropertyReceiver
-import androidx.compose.ui.semantics.accessibilityLabel
+import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.filters.MediumTest
@@ -46,18 +46,18 @@
             val state = remember { mutableStateOf("Nothing") }
             BoundaryNode {
                 setString("SetString") { state.value = it; return@setString true }
-                accessibilityLabel = state.value
+                contentDescription = state.value
             }
         }
 
-        rule.onNodeWithLabel("Nothing")
+        rule.onNodeWithContentDescription("Nothing")
             .assertExists()
             .performSemanticsAction(MyActions.SetString) { it("Hello") }
 
-        rule.onNodeWithLabel("Nothing")
+        rule.onNodeWithContentDescription("Nothing")
             .assertDoesNotExist()
 
-        rule.onNodeWithLabel("Hello")
+        rule.onNodeWithContentDescription("Hello")
             .assertExists()
     }
 
diff --git a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/ErrorMessagesTest.kt b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/ErrorMessagesTest.kt
index 6c0dd94..f6863508 100644
--- a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/ErrorMessagesTest.kt
+++ b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/ErrorMessagesTest.kt
@@ -46,26 +46,13 @@
     val rule = createComposeRule()
 
     @Test
-    fun findByTag_assertHasClickAction_predicateShouldFail() {
+    fun findByTag_assertHasClickAction() {
         rule.setContent {
             ComposeSimpleCase()
         }
 
-        expectErrorMessageMatches(
-            "" +
-                "Failed to assert the following: \\(OnClick is defined\\)\n" +
-                "Semantics of the node:\n" +
-                "Node #X at \\(X, X, X, X\\)px, Tag: 'MyButton'\n" +
-                "Disabled = 'kotlin\\.Unit'\n" +
-                "Text = 'Toggle'\n" +
-                "GetTextLayoutResult = 'AccessibilityAction\\(label=null, action=.*\\)'\n" +
-                "MergeDescendants = 'true'\n" +
-                "Has 1 sibling\n" +
-                "Selector used: \\(TestTag = 'MyButton'\\)"
-        ) {
-            rule.onNodeWithTag("MyButton")
-                .assertHasClickAction()
-        }
+        rule.onNodeWithTag("MyButton")
+            .assertHasClickAction()
     }
 
     @Test
@@ -139,22 +126,6 @@
     }
 
     @Test
-    fun findByTag_callNonExistentSemanticsAction() {
-        rule.setContent {
-            ComposeSimpleCase()
-        }
-
-        expectErrorMessageStartsWith(
-            "" +
-                "Failed to perform OnClick action as it is not defined on the node.\n" +
-                "Semantics of the node:"
-        ) {
-            rule.onNodeWithTag("MyButton")
-                .performSemanticsAction(SemanticsActions.OnClick)
-        }
-    }
-
-    @Test
     fun findByTag_callSemanticsAction_butElementDoesNotExist() {
         rule.setContent {
             ComposeSimpleCase()
diff --git a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/IsDisplayedTest.kt b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/IsDisplayedTest.kt
index ee9de00..d98967c 100644
--- a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/IsDisplayedTest.kt
+++ b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/IsDisplayedTest.kt
@@ -22,8 +22,8 @@
 import androidx.activity.ComponentActivity
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
@@ -33,11 +33,12 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.util.BoundaryNode
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.test.espresso.Espresso.onView
@@ -47,7 +48,6 @@
 import androidx.test.espresso.matcher.ViewMatchers.withId
 import androidx.test.espresso.matcher.ViewMatchers.withParent
 import androidx.test.filters.MediumTest
-import androidx.compose.ui.test.util.BoundaryNode
 import org.hamcrest.CoreMatchers.allOf
 import org.hamcrest.CoreMatchers.not
 import org.junit.Rule
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidAssertions.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidAssertions.kt
index 203fed5b..47c42c1 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidAssertions.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidAssertions.kt
@@ -19,8 +19,8 @@
 import androidx.test.espresso.matcher.ViewMatchers
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.node.LayoutNode
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.layout.LayoutInfo
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.semantics.SemanticsNode
 
 internal actual fun SemanticsNodeInteraction.checkIsDisplayed(): Boolean {
@@ -28,16 +28,16 @@
     val errorMessageOnFail = "Failed to perform isDisplayed check."
     val node = fetchSemanticsNode(errorMessageOnFail)
 
-    fun isNotPlaced(node: LayoutNode): Boolean {
+    fun isNotPlaced(node: LayoutInfo): Boolean {
         return !node.isPlaced
     }
 
-    val layoutNode = node.layoutNode
-    if (isNotPlaced(layoutNode) || layoutNode.findClosestParentNode(::isNotPlaced) != null) {
+    val layoutInfo = node.layoutInfo
+    if (isNotPlaced(layoutInfo) || layoutInfo.findClosestParentNode(::isNotPlaced) != null) {
         return false
     }
 
-    (node.layoutNode.owner as? AndroidOwner)?.let {
+    (node.owner as? ViewRootForTest)?.let {
         if (!ViewMatchers.isDisplayed().matches(it.view)) {
             return false
         }
@@ -53,7 +53,7 @@
 }
 
 internal actual fun SemanticsNode.clippedNodeBoundsInWindow(): Rect {
-    val composeView = (layoutNode.owner as AndroidOwner).view
+    val composeView = (owner as ViewRootForTest).view
     val rootLocationInWindow = intArrayOf(0, 0).let {
         composeView.getLocationInWindow(it)
         Offset(it[0].toFloat(), it[1].toFloat())
@@ -62,7 +62,7 @@
 }
 
 internal actual fun SemanticsNode.isInScreenBounds(): Boolean {
-    val composeView = (layoutNode.owner as AndroidOwner).view
+    val composeView = (owner as ViewRootForTest).view
 
     // Window relative bounds of our node
     val nodeBoundsInWindow = clippedNodeBoundsInWindow()
@@ -83,17 +83,19 @@
 }
 
 /**
- * Executes [selector] on every parent of this [LayoutNode] and returns the closest
- * [LayoutNode] to return `true` from [selector] or null if [selector] returns false
+ * Executes [selector] on every parent of this [LayoutInfo] and returns the closest
+ * [LayoutInfo] to return `true` from [selector] or null if [selector] returns false
  * for all ancestors.
  */
-private fun LayoutNode.findClosestParentNode(selector: (LayoutNode) -> Boolean): LayoutNode? {
-    var currentParent = this.parent
+private fun LayoutInfo.findClosestParentNode(
+    selector: (LayoutInfo) -> Boolean
+): LayoutInfo? {
+    var currentParent = this.parentInfo
     while (currentParent != null) {
         if (selector(currentParent)) {
             return currentParent
         } else {
-            currentParent = currentParent.parent
+            currentParent = currentParent.parentInfo
         }
     }
 
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidImageHelpers.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidImageHelpers.kt
index eaf81f0..e9de46c 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidImageHelpers.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidImageHelpers.kt
@@ -21,7 +21,7 @@
 import android.view.Window
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.graphics.ImageBitmap
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.test.android.captureRegionToImage
 import androidx.compose.ui.window.DialogWindowProvider
@@ -53,7 +53,7 @@
         )
     }
 
-    val view = (node.layoutNode.owner as AndroidOwner).view
+    val view = (node.owner as ViewRootForTest).view
 
     // If we are in dialog use its window to capture the bitmap
     val dialogParentNodeMaybe = node.findClosestParentNode(includeSelf = true) {
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt
index f94df4a..6dfc89b 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt
@@ -28,15 +28,15 @@
 import androidx.compose.runtime.dispatch.AndroidUiDispatcher
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.node.Owner
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.platform.ViewRootForTest
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
 import kotlin.math.max
 
 internal actual fun createInputDispatcher(testContext: TestContext, owner: Owner): InputDispatcher {
-    require(owner is AndroidOwner) {
-        "InputDispatcher currently only supports dispatching to AndroidOwner, not to " +
+    require(owner is ViewRootForTest) {
+        "InputDispatcher currently only supports dispatching to ViewRootForTest, not to " +
             owner::class.java.simpleName
     }
     val view = owner.view
@@ -45,7 +45,7 @@
 
 internal class AndroidInputDispatcher(
     testContext: TestContext,
-    owner: AndroidOwner?,
+    owner: Owner?,
     private val sendEvent: (MotionEvent) -> Unit
 ) : InputDispatcher(testContext, owner) {
 
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/CoroutineBuilders.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/CoroutineBuilders.kt
index 4a03271..4626cb2 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/CoroutineBuilders.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/CoroutineBuilders.kt
@@ -20,6 +20,7 @@
 import androidx.compose.animation.core.MonotonicFrameAnimationClock
 import androidx.compose.animation.core.advanceClockMillis
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.yield
 
@@ -29,7 +30,7 @@
  * [runBlocking] if they want to use a [ManualFrameClock].
  *
  * The clock will start at time 0L and should be driven manually from your test, from the
- * [main dispatcher][TestUiDispatcher.Main]. Pass the clock to the animation that you want to
+ * [main dispatcher][Dispatchers.Main]. Pass the clock to the animation that you want to
  * control in your test, and then [advance][advanceClockMillis] it as necessary. After the block
  * has completed, the clock will be forwarded with 10 second increments until it has drained all
  * work that took frames from that clock. If the work never ends, this function never ends, so
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt
index de53bce..c680bb8 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt
@@ -64,8 +64,8 @@
     // Figure out the (clipped) bounds of the viewPort in its direct parent's content area, in
     // root coordinates. We only want the clipping from the direct parent on the scrollable, not
     // from any other ancestors.
-    val viewPortInParent = scrollableNode.layoutNode.coordinates.boundsInParent
-    val parentInRoot = scrollableNode.layoutNode.coordinates.parentCoordinates
+    val viewPortInParent = scrollableNode.layoutInfo.coordinates.boundsInParent
+    val parentInRoot = scrollableNode.layoutInfo.coordinates.parentCoordinates
         ?.positionInRoot ?: Offset.Zero
 
     val viewPort = viewPortInParent.translate(parentInRoot)
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Assertions.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Assertions.kt
index d9ba5c5..9e06eed 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Assertions.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Assertions.kt
@@ -167,11 +167,11 @@
 
 /**
  * Asserts the node's label equals the given String.
- * For further details please check [SemanticsProperties.AccessibilityLabel].
+ * For further details please check [SemanticsProperties.ContentDescription].
  * Throws [AssertionError] if the node's value is not equal to `value`, or if the node has no value
  */
 fun SemanticsNodeInteraction.assertLabelEquals(value: String): SemanticsNodeInteraction =
-    assert(hasLabel(value))
+    assert(hasContentDescription(value))
 
 /**
  * Asserts the node's text equals the given String.
@@ -184,7 +184,7 @@
 /**
  * Asserts the node's value equals the given value.
  *
- * For further details please check [SemanticsProperties.AccessibilityValue].
+ * For further details please check [SemanticsProperties.StateDescription].
  * Throws [AssertionError] if the node's value is not equal to `value`, or if the node has no value
  */
 fun SemanticsNodeInteraction.assertValueEquals(value: String): SemanticsNodeInteraction =
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt
index 8626416..ac7ff43 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt
@@ -161,13 +161,13 @@
  * @param label Text to match.
  * @param ignoreCase Whether case should be ignored.
  *
- * @see SemanticsProperties.AccessibilityLabel
+ * @see SemanticsProperties.ContentDescription
  */
-fun hasLabel(label: String, ignoreCase: Boolean = false): SemanticsMatcher {
+fun hasContentDescription(label: String, ignoreCase: Boolean = false): SemanticsMatcher {
     return SemanticsMatcher(
-        "${SemanticsProperties.AccessibilityLabel.name} = '$label' (ignoreCase: $ignoreCase)"
+        "${SemanticsProperties.ContentDescription.name} = '$label' (ignoreCase: $ignoreCase)"
     ) {
-        it.config.getOrNull(SemanticsProperties.AccessibilityLabel).equals(label, ignoreCase)
+        it.config.getOrNull(SemanticsProperties.ContentDescription).equals(label, ignoreCase)
     }
 }
 
@@ -212,10 +212,10 @@
  *
  * @param value Value to match.
  *
- * @see SemanticsProperties.AccessibilityValue
+ * @see SemanticsProperties.StateDescription
  */
 fun hasValue(value: String): SemanticsMatcher = SemanticsMatcher.expectValue(
-    SemanticsProperties.AccessibilityValue, value
+    SemanticsProperties.StateDescription, value
 )
 
 /**
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Finders.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Finders.kt
index 6d67243..2489cb4 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Finders.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Finders.kt
@@ -45,7 +45,7 @@
 ): SemanticsNodeInteractionCollection = onAllNodes(hasTestTag(testTag), useUnmergedTree)
 
 /**
- * Finds a semantics node with the given label as its accessibilityLabel.
+ * Finds a semantics node with the given contentDescription.
  *
  * For usage patterns and semantics concepts see [SemanticsNodeInteraction]
  *
@@ -53,11 +53,11 @@
  *
  * @see SemanticsNodeInteractionsProvider.onNode for general find method.
  */
-fun SemanticsNodeInteractionsProvider.onNodeWithLabel(
+fun SemanticsNodeInteractionsProvider.onNodeWithContentDescription(
     label: String,
     ignoreCase: Boolean = false,
     useUnmergedTree: Boolean = false
-): SemanticsNodeInteraction = onNode(hasLabel(label, ignoreCase), useUnmergedTree)
+): SemanticsNodeInteraction = onNode(hasContentDescription(label, ignoreCase), useUnmergedTree)
 
 /**
  * Finds a semantincs node with the given text.
@@ -105,18 +105,19 @@
 ): SemanticsNodeInteractionCollection = onAllNodes(hasText(text, ignoreCase), useUnmergedTree)
 
 /**
- * Finds all semantics nodes with the given label as AccessibilityLabel.
+ * Finds all semantics nodes with the given label as ContentDescription.
  *
  * For usage patterns and semantics concepts see [SemanticsNodeInteraction]
  *
  * @param useUnmergedTree Find within merged composables like Buttons.
  * @see SemanticsNodeInteractionsProvider.onAllNodes for general find method.
  */
-fun SemanticsNodeInteractionsProvider.onAllNodesWithLabel(
+fun SemanticsNodeInteractionsProvider.onAllNodesWithContentDescription(
     label: String,
     ignoreCase: Boolean = false,
     useUnmergedTree: Boolean = false
-): SemanticsNodeInteractionCollection = onAllNodes(hasLabel(label, ignoreCase), useUnmergedTree)
+): SemanticsNodeInteractionCollection =
+    onAllNodes(hasContentDescription(label, ignoreCase), useUnmergedTree)
 
 /**
  * Finds all semantics nodes with text that contains the given substring.
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GestureScope.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GestureScope.kt
index d4b7af9..c745d64 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GestureScope.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GestureScope.kt
@@ -106,7 +106,7 @@
         }
 
     // Convenience property
-    private val owner get() = semanticsNode.layoutNode.owner
+    private val owner get() = semanticsNode.owner
 
     // TODO(b/133217292): Better error: explain which gesture couldn't be performed
     private var _inputDispatcher: InputDispatcher? =
@@ -285,7 +285,7 @@
  * @param position A position in local coordinates
  */
 private fun GestureScope.localToGlobal(position: Offset): Offset {
-    return position + semanticsNode.layoutNode.coordinates.globalBounds.topLeft
+    return position + semanticsNode.layoutInfo.coordinates.globalBounds.topLeft
 }
 
 /**
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/IdlingResource.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/IdlingResource.kt
new file mode 100644
index 0000000..7abd443
--- /dev/null
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/IdlingResource.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.test
+
+/**
+ * Represents a resource of an application under test which can cause asynchronous background
+ * work to happen during test execution (e.g. an http request in response to a button click).
+ *
+ * By default, all interactions from the test with the compose tree (finding nodes, performing
+ * gestures, making assertions) will be synchronized with pending work in Compose's internals
+ * (such as applying state changes, recomposing, measuring, etc). This ensures that the UI is in
+ * a stable state when the interactions are performed, so that e.g. no pending recompositions are
+ * still scheduled that could potentially change the UI. However, any asynchronous work that is
+ * not done through one of Compose's mechanisms won't be included in the default synchronization.
+ * For such work, test authors can create an [IdlingResource] and register it into the test with
+ * [registerIdlingResource][androidx.compose.ui.test.junit4.ComposeTestRule
+ * .registerIdlingResource], and the interaction will wait for that resource to become idle prior
+ * to performing it.
+ */
+interface IdlingResource {
+    /**
+     * Whether or not the [IdlingResource] is idle when reading this value. This should always be
+     * called from the main thread, which is why it should be lightweight and fast.
+     *
+     * If one idling resource returns `false`, the synchronization system will keep polling all
+     * idling resources until they are all idle.
+     */
+    val isIdleNow: Boolean
+
+    /**
+     * Returns diagnostics that explain why the idling resource is busy, or `null` if the
+     * resource is not busy. Default implementation returns `null`.
+     */
+    fun getDiagnosticMessageIfBusy(): String? = null
+}
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInputHelpers.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInputHelpers.kt
index 3892185..86871e9 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInputHelpers.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInputHelpers.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.test
 
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.KeyEvent
 
 /**
@@ -24,10 +23,9 @@
  *
  * @return true if the event was consumed. False otherwise.
  */
-@OptIn(ExperimentalKeyInput::class)
 fun SemanticsNodeInteraction.performKeyPress(keyEvent: KeyEvent): Boolean {
     val semanticsNode = fetchSemanticsNode("Failed to send key Event (${keyEvent.key})")
-    val owner = semanticsNode.layoutNode.owner
+    val owner = semanticsNode.owner
     requireNotNull(owner) { "Failed to find owner" }
     @OptIn(InternalTestingApi::class)
     return testContext.testOwner.runOnUiThread { owner.sendKeyEvent(keyEvent) }
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
index 7b55269..74a2c6d 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
@@ -248,7 +248,7 @@
     operation: Density.(SemanticsNode) -> R
 ): R {
     val node = fetchSemanticsNode("Failed to retrieve density for the node.")
-    val density = node.layoutNode.owner!!.density
+    val density = node.owner!!.density
     return operation.invoke(density, node)
 }
 
@@ -256,7 +256,7 @@
     assertion: Density.(Rect) -> Unit
 ): SemanticsNodeInteraction {
     val node = fetchSemanticsNode("Failed to retrieve bounds of the node.")
-    val density = node.layoutNode.owner!!.density
+    val density = node.owner!!.density
 
     assertion.invoke(density, node.unclippedBoundsInRoot)
     return this
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestUiDispatcher.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestUiDispatcher.kt
index 52e2efe..2c0c3cd 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestUiDispatcher.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestUiDispatcher.kt
@@ -16,16 +16,17 @@
 
 package androidx.compose.ui.test
 
-import androidx.compose.runtime.EmbeddingContext
+import kotlinx.coroutines.Dispatchers
 import kotlin.coroutines.CoroutineContext
 
-// Experimental because it isn't yet clear if the behavior on Robolectric is correct
 @ExperimentalTesting
 object TestUiDispatcher {
     /**
      * The dispatcher to use if you need to dispatch coroutines on the main thread in tests.
      */
-    val Main: CoroutineContext by lazy {
-        EmbeddingContext().mainThreadCompositionContext()
-    }
+    @Deprecated(
+        message = "Removed in favor of Dispatchers.Main",
+        replaceWith = ReplaceWith("Dispatchers.Main", "kotlinx.coroutines.Dispatchers")
+    )
+    val Main: CoroutineContext = Dispatchers.Main
 }
diff --git a/compose/ui/ui-text/build.gradle b/compose/ui/ui-text/build.gradle
index 9e9d531..a8f1082 100644
--- a/compose/ui/ui-text/build.gradle
+++ b/compose/ui/ui-text/build.gradle
@@ -148,6 +148,15 @@
                     exclude group: 'org.mockito' // to keep control on the mockito version
                 }
             }
+
+            desktopTest.dependencies {
+                implementation(TRUTH)
+                implementation(JUNIT)
+                implementation(SKIKO_CURRENT_OS)
+                implementation project(":compose:foundation:foundation")
+                implementation project(":compose:ui:ui-test-junit4")
+                implementation project(":compose:ui:ui-test-font")
+            }
         }
     }
 }
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.kt
index 17fc702..2d64078 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.kt
@@ -28,6 +28,7 @@
 import androidx.compose.ui.text.style.TextIndent
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.isUnspecified
 
 @OptIn(InternalPlatformTextApi::class)
 internal fun createCharSequence(
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/SpannableExtensions.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/SpannableExtensions.kt
index 0bee915..bb5ef07 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/SpannableExtensions.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/SpannableExtensions.kt
@@ -60,6 +60,7 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.TextUnit
 import androidx.compose.ui.unit.TextUnitType
+import androidx.compose.ui.unit.isUnspecified
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.util.fastForEach
 import kotlin.math.ceil
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
index 91495c2..14dec58 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
@@ -23,6 +23,7 @@
 import androidx.compose.ui.text.style.TextIndent
 import androidx.compose.ui.text.style.lerp
 import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.isUnspecified
 
 /**
  * Paragraph styling configuration for a paragraph. The difference between [SpanStyle] and
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt
index a6aa958..f9ed0dd 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.runtime.Immutable
 import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.isUnspecified
 
 /**
  * A placeholder is a rectangle box inserted into text, which tells the text processor to leave an
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt
index d0fab8c..8830895 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt
@@ -21,7 +21,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shadow
 import androidx.compose.ui.graphics.lerp
-import androidx.compose.ui.graphics.useOrElse
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.font.FontStyle
 import androidx.compose.ui.text.font.FontSynthesis
@@ -33,6 +33,7 @@
 import androidx.compose.ui.text.style.TextGeometricTransform
 import androidx.compose.ui.text.style.lerp
 import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.isUnspecified
 import androidx.compose.ui.unit.lerp
 
 /**
@@ -98,7 +99,7 @@
         if (other == null) return this
 
         return SpanStyle(
-            color = other.color.useOrElse { this.color },
+            color = other.color.takeOrElse { this.color },
             fontFamily = other.fontFamily ?: this.fontFamily,
             fontSize = if (!other.fontSize.isUnspecified) other.fontSize else this.fontSize,
             fontWeight = other.fontWeight ?: this.fontWeight,
@@ -113,7 +114,7 @@
             baselineShift = other.baselineShift ?: this.baselineShift,
             textGeometricTransform = other.textGeometricTransform ?: this.textGeometricTransform,
             localeList = other.localeList ?: this.localeList,
-            background = other.background.useOrElse { this.background },
+            background = other.background.takeOrElse { this.background },
             textDecoration = other.textDecoration ?: this.textDecoration,
             shadow = other.shadow ?: this.shadow
         )
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
index f45f129..e9806458 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
@@ -20,7 +20,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shadow
-import androidx.compose.ui.graphics.useOrElse
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.font.FontStyle
 import androidx.compose.ui.text.font.FontSynthesis
@@ -34,6 +34,7 @@
 import androidx.compose.ui.text.style.TextIndent
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.isUnspecified
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.util.annotation.VisibleForTesting
 
@@ -257,7 +258,7 @@
  * @return resolved text style.
  */
 fun resolveDefaults(style: TextStyle, direction: LayoutDirection) = TextStyle(
-    color = style.color.useOrElse { DefaultColor },
+    color = style.color.takeOrElse { DefaultColor },
     fontSize = if (style.fontSize.isUnspecified) DefaultFontSize else style.fontSize,
     fontWeight = style.fontWeight ?: FontWeight.Normal,
     fontStyle = style.fontStyle ?: FontStyle.Normal,
@@ -272,7 +273,7 @@
     baselineShift = style.baselineShift ?: BaselineShift.None,
     textGeometricTransform = style.textGeometricTransform ?: TextGeometricTransform.None,
     localeList = style.localeList ?: LocaleList.current,
-    background = style.background.useOrElse { DefaultBackgroundColor },
+    background = style.background.takeOrElse { DefaultBackgroundColor },
     textDecoration = style.textDecoration ?: TextDecoration.None,
     shadow = style.shadow ?: Shadow.None,
     textAlign = style.textAlign ?: TextAlign.Start,
diff --git a/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.kt b/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.kt
index efac85d..13b1fab 100644
--- a/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.kt
+++ b/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.kt
@@ -47,6 +47,7 @@
 import androidx.compose.ui.text.style.TextDecoration
 import androidx.compose.ui.text.style.TextGeometricTransform
 import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.isUnspecified
 import androidx.compose.ui.unit.TextUnit
 import androidx.compose.ui.unit.sp
 import org.jetbrains.skija.Paint
diff --git a/compose/ui/ui-text/src/desktopTest/kotlin/androidx/compose/ui/text/DesktopParagraphTest.kt b/compose/ui/ui-text/src/desktopTest/kotlin/androidx/compose/ui/text/DesktopParagraphTest.kt
new file mode 100644
index 0000000..c1a9560
--- /dev/null
+++ b/compose/ui/ui-text/src/desktopTest/kotlin/androidx/compose/ui/text/DesktopParagraphTest.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.text
+
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.font.fontFamily
+import androidx.compose.ui.text.platform.FontLoader
+import androidx.compose.ui.text.platform.font
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.sp
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class DesktopParagraphTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val fontLoader = FontLoader()
+    private val defaultDensity = Density(density = 1f)
+    private val fontFamilyMeasureFont =
+        fontFamily(
+            font(
+                "MeasureFont",
+                "font/sample_font.ttf",
+                weight = FontWeight.Normal,
+                style = FontStyle.Normal
+            )
+        )
+
+    @Test
+    fun getBoundingBox_basic() {
+        with(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx()
+            val paragraph = simpleParagraph(
+                text = text,
+                style = TextStyle(fontSize = fontSize)
+            )
+
+            for (i in 0..text.length - 1) {
+                val box = paragraph.getBoundingBox(i)
+                Truth.assertThat(box.left).isEqualTo(i * fontSizeInPx)
+                Truth.assertThat(box.right).isEqualTo((i + 1) * fontSizeInPx)
+                Truth.assertThat(box.top).isZero()
+                Truth.assertThat(box.bottom).isEqualTo(fontSizeInPx + 10)
+            }
+        }
+    }
+
+    @Test
+    fun getBoundingBox_multicodepoints() {
+        with(defaultDensity) {
+            val text = "h\uD83E\uDDD1\uD83C\uDFFF\u200D\uD83E\uDDB0"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx()
+            val paragraph = simpleParagraph(
+                text = text,
+                style = TextStyle(fontSize = 50.sp)
+            )
+
+            Truth.assertThat(paragraph.getBoundingBox(0))
+                .isEqualTo(Rect(0f, 0.37f, fontSizeInPx, 60.37f))
+
+            Truth.assertThat(paragraph.getBoundingBox(1))
+                .isEqualTo(Rect(fontSizeInPx, 0.37f, fontSizeInPx * 2, 66f))
+
+            Truth.assertThat(paragraph.getBoundingBox(5))
+                .isEqualTo(Rect(fontSizeInPx, 0.37f, fontSizeInPx * 2, 66f))
+        }
+    }
+
+    private fun simpleParagraph(
+        text: String = "",
+        style: TextStyle? = null,
+        maxLines: Int = Int.MAX_VALUE,
+        ellipsis: Boolean = false,
+        spanStyles: List<AnnotatedString.Range<SpanStyle>> = listOf(),
+        density: Density? = null,
+        width: Float = 2000f
+    ): Paragraph {
+        return Paragraph(
+            text = text,
+            spanStyles = spanStyles,
+            style = TextStyle(
+                fontFamily = fontFamilyMeasureFont
+            ).merge(style),
+            maxLines = maxLines,
+            ellipsis = ellipsis,
+            width = width,
+            density = density ?: defaultDensity,
+            resourceLoader = fontLoader
+        )
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt
index 0154b69..ea6525e 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt
@@ -30,6 +30,7 @@
 import androidx.compose.ui.text.style.lerp
 import androidx.compose.ui.unit.TextUnit
 import androidx.compose.ui.unit.em
+import androidx.compose.ui.unit.isUnspecified
 import androidx.compose.ui.unit.sp
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt
index 4b7220f..fedbc60 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt
@@ -35,6 +35,7 @@
 import androidx.compose.ui.text.style.lerp
 import androidx.compose.ui.unit.TextUnit
 import androidx.compose.ui.unit.em
+import androidx.compose.ui.unit.isUnspecified
 import androidx.compose.ui.unit.sp
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
diff --git a/compose/ui/ui-tooling/api/current.txt b/compose/ui/ui-tooling/api/current.txt
index 23de9c4..2d94392 100644
--- a/compose/ui/ui-tooling/api/current.txt
+++ b/compose/ui/ui-tooling/api/current.txt
@@ -12,7 +12,7 @@
     method public final java.util.Collection<java.lang.Object> getData();
     method public final Object? getKey();
     method public final androidx.compose.ui.tooling.SourceLocation? getLocation();
-    method public java.util.List<androidx.compose.ui.node.ModifierInfo> getModifierInfo();
+    method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
     method public final String? getName();
     method public java.util.List<androidx.compose.ui.tooling.ParameterInformation> getParameters();
     property public final androidx.compose.ui.unit.IntBounds box;
@@ -20,7 +20,7 @@
     property public final java.util.Collection<java.lang.Object> data;
     property public final Object? key;
     property public final androidx.compose.ui.tooling.SourceLocation? location;
-    property public java.util.List<androidx.compose.ui.node.ModifierInfo> modifierInfo;
+    property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
     property public final String? name;
     property public java.util.List<androidx.compose.ui.tooling.ParameterInformation> parameters;
   }
@@ -41,9 +41,9 @@
   }
 
   public final class NodeGroup extends androidx.compose.ui.tooling.Group {
-    ctor public NodeGroup(Object? key, Object node, androidx.compose.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.compose.ui.node.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.compose.ui.tooling.Group> children);
+    ctor public NodeGroup(Object? key, Object node, androidx.compose.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.compose.ui.tooling.Group> children);
     method public Object getNode();
-    property public java.util.List<androidx.compose.ui.node.ModifierInfo> modifierInfo;
+    property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
     property public final Object node;
   }
 
@@ -74,7 +74,7 @@
   }
 
   public final class SlotTreeKt {
-    method public static androidx.compose.ui.tooling.Group asTree(androidx.compose.runtime.SlotTable);
+    method public static androidx.compose.ui.tooling.Group asTree(androidx.compose.runtime.CompositionData);
     method public static String? getPosition(androidx.compose.ui.tooling.Group);
   }
 
diff --git a/compose/ui/ui-tooling/api/public_plus_experimental_current.txt b/compose/ui/ui-tooling/api/public_plus_experimental_current.txt
index 23de9c4..2d94392 100644
--- a/compose/ui/ui-tooling/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-tooling/api/public_plus_experimental_current.txt
@@ -12,7 +12,7 @@
     method public final java.util.Collection<java.lang.Object> getData();
     method public final Object? getKey();
     method public final androidx.compose.ui.tooling.SourceLocation? getLocation();
-    method public java.util.List<androidx.compose.ui.node.ModifierInfo> getModifierInfo();
+    method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
     method public final String? getName();
     method public java.util.List<androidx.compose.ui.tooling.ParameterInformation> getParameters();
     property public final androidx.compose.ui.unit.IntBounds box;
@@ -20,7 +20,7 @@
     property public final java.util.Collection<java.lang.Object> data;
     property public final Object? key;
     property public final androidx.compose.ui.tooling.SourceLocation? location;
-    property public java.util.List<androidx.compose.ui.node.ModifierInfo> modifierInfo;
+    property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
     property public final String? name;
     property public java.util.List<androidx.compose.ui.tooling.ParameterInformation> parameters;
   }
@@ -41,9 +41,9 @@
   }
 
   public final class NodeGroup extends androidx.compose.ui.tooling.Group {
-    ctor public NodeGroup(Object? key, Object node, androidx.compose.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.compose.ui.node.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.compose.ui.tooling.Group> children);
+    ctor public NodeGroup(Object? key, Object node, androidx.compose.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.compose.ui.tooling.Group> children);
     method public Object getNode();
-    property public java.util.List<androidx.compose.ui.node.ModifierInfo> modifierInfo;
+    property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
     property public final Object node;
   }
 
@@ -74,7 +74,7 @@
   }
 
   public final class SlotTreeKt {
-    method public static androidx.compose.ui.tooling.Group asTree(androidx.compose.runtime.SlotTable);
+    method public static androidx.compose.ui.tooling.Group asTree(androidx.compose.runtime.CompositionData);
     method public static String? getPosition(androidx.compose.ui.tooling.Group);
   }
 
diff --git a/compose/ui/ui-tooling/api/restricted_current.txt b/compose/ui/ui-tooling/api/restricted_current.txt
index 23de9c4..2d94392 100644
--- a/compose/ui/ui-tooling/api/restricted_current.txt
+++ b/compose/ui/ui-tooling/api/restricted_current.txt
@@ -12,7 +12,7 @@
     method public final java.util.Collection<java.lang.Object> getData();
     method public final Object? getKey();
     method public final androidx.compose.ui.tooling.SourceLocation? getLocation();
-    method public java.util.List<androidx.compose.ui.node.ModifierInfo> getModifierInfo();
+    method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
     method public final String? getName();
     method public java.util.List<androidx.compose.ui.tooling.ParameterInformation> getParameters();
     property public final androidx.compose.ui.unit.IntBounds box;
@@ -20,7 +20,7 @@
     property public final java.util.Collection<java.lang.Object> data;
     property public final Object? key;
     property public final androidx.compose.ui.tooling.SourceLocation? location;
-    property public java.util.List<androidx.compose.ui.node.ModifierInfo> modifierInfo;
+    property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
     property public final String? name;
     property public java.util.List<androidx.compose.ui.tooling.ParameterInformation> parameters;
   }
@@ -41,9 +41,9 @@
   }
 
   public final class NodeGroup extends androidx.compose.ui.tooling.Group {
-    ctor public NodeGroup(Object? key, Object node, androidx.compose.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.compose.ui.node.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.compose.ui.tooling.Group> children);
+    ctor public NodeGroup(Object? key, Object node, androidx.compose.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.compose.ui.tooling.Group> children);
     method public Object getNode();
-    property public java.util.List<androidx.compose.ui.node.ModifierInfo> modifierInfo;
+    property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
     property public final Object node;
   }
 
@@ -74,7 +74,7 @@
   }
 
   public final class SlotTreeKt {
-    method public static androidx.compose.ui.tooling.Group asTree(androidx.compose.runtime.SlotTable);
+    method public static androidx.compose.ui.tooling.Group asTree(androidx.compose.runtime.CompositionData);
     method public static String? getPosition(androidx.compose.ui.tooling.Group);
   }
 
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt
index 5317df3..30d1dd4 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt
@@ -55,7 +55,7 @@
 
     @Test
     fun testBounds() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
         show {
             Inspectable(slotTableRecord) {
                 Box {
@@ -98,7 +98,7 @@
 
     @Test
     fun testBoundWithConstraints() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
         show {
             Inspectable(slotTableRecord) {
                 WithConstraints {
@@ -131,7 +131,7 @@
     @Test
     @LargeTest
     fun testDisposeWithComposeTables() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
         var value by mutableStateOf(0)
         var latch = CountDownLatch(1)
         show {
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/InspectableTests.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/InspectableTests.kt
index 7f1b213..a1ea937 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/InspectableTests.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/InspectableTests.kt
@@ -25,7 +25,6 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.InternalComposeApi
-import androidx.compose.runtime.SlotTable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.graphics.Color
@@ -50,7 +49,7 @@
 class InspectableTests : ToolingTest() {
     @Test
     fun simpleInspection() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
         show {
             Inspectable(slotTableRecord) {
                 Column {
@@ -77,7 +76,7 @@
 
     @Test
     fun parametersTest() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
         fun unknown(i: Int) = i
 
         show {
@@ -116,8 +115,8 @@
 
         val tree = slotTableRecord.store.first().asTree()
         val list = tree.asList()
-        val parameters = list.filter {
-            it.parameters.isNotEmpty() && it.location.let {
+        val parameters = list.filter { group ->
+            group.parameters.isNotEmpty() && group.location.let {
                 it != null && it.sourceFile == "InspectableTests.kt"
             }
         }
@@ -295,7 +294,7 @@
     fun inInspectionMode() {
         var displayed = false
         show {
-            Inspectable(SlotTableRecord.create()) {
+            Inspectable(CompositionDataRecord.create()) {
                 Column {
                     InInspectionModeOnly {
                         Box(Modifier.preferredSize(100.dp).background(color = Color(0xFF)))
@@ -326,7 +325,7 @@
     @InternalComposeApi
     @Test // regression test for b/161839910
     fun textParametersAreCorrect() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
         show {
             Inspectable(slotTableRecord) {
                 Text("Test")
@@ -334,8 +333,8 @@
         }
         val tree = slotTableRecord.store.first().asTree()
         val list = tree.asList()
-        val parameters = list.filter {
-            it.parameters.isNotEmpty() && it.location.let {
+        val parameters = list.filter { group ->
+            group.parameters.isNotEmpty() && group.location.let {
                 it != null && it.sourceFile == "InspectableTests.kt"
             }
         }
@@ -431,14 +430,3 @@
     }
     return result
 }
-
-internal fun SlotTableRecord.findGroupForFile(fileName: String) =
-    store.map { it.findGroupForFile(fileName) }.filterNotNull().firstOrNull()
-
-@OptIn(InternalComposeApi::class)
-fun SlotTable.findGroupForFile(fileName: String) = asTree().findGroupForFile(fileName)
-fun Group.findGroupForFile(fileName: String): Group? {
-    val position = position
-    if (position != null && position.contains(fileName)) return this
-    return children.map { it.findGroupForFile(fileName) }.filterNotNull().firstOrNull()
-}
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ModifierInfoTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ModifierInfoTest.kt
index 6a7348f..a5070a6 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ModifierInfoTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ModifierInfoTest.kt
@@ -44,7 +44,7 @@
 
     @Test
     fun testBounds() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
         show {
             Inspectable(slotTableRecord) {
                 with(AmbientDensity.current) {
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ToolingTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ToolingTest.kt
index bf3b298..f8c34a2 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ToolingTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ToolingTest.kt
@@ -21,12 +21,11 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.InternalComposeApi
-import androidx.compose.runtime.SlotTable
+import androidx.compose.runtime.CompositionData
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.R
 import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.platform.setContent
 import org.junit.Before
 import org.junit.Rule
@@ -73,17 +72,16 @@
         activityTestRule.onUiThread { }
     }
 
-    @OptIn(InternalComposeApi::class)
-    internal fun showAndRecord(content: @Composable () -> Unit): MutableSet<SlotTable>? {
+    internal fun showAndRecord(content: @Composable () -> Unit): MutableSet<CompositionData>? {
 
         positionedLatch = CountDownLatch(1)
-        val map: MutableSet<SlotTable> = Collections.newSetFromMap(
-            WeakHashMap<SlotTable, Boolean>()
+        val map: MutableSet<CompositionData> = Collections.newSetFromMap(
+            WeakHashMap<CompositionData, Boolean>()
         )
         activityTestRule.onUiThread {
-            AndroidOwner.onAndroidOwnerCreatedCallback = {
+            ViewRootForTest.onViewCreatedCallback = {
                 it.view.setTag(R.id.inspection_slot_table_set, map)
-                AndroidOwner.onAndroidOwnerCreatedCallback = null
+                ViewRootForTest.onViewCreatedCallback = null
             }
             activity.setContent {
                 Box(
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/InlineClassConverterTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/InlineClassConverterTest.kt
index 1491a4c..6a035f6 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/InlineClassConverterTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/InlineClassConverterTest.kt
@@ -21,7 +21,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.tooling.Group
 import androidx.compose.ui.tooling.Inspectable
-import androidx.compose.ui.tooling.SlotTableRecord
+import androidx.compose.ui.tooling.CompositionDataRecord
 import androidx.compose.ui.tooling.ToolingTest
 import androidx.compose.ui.tooling.asTree
 import androidx.compose.ui.unit.Dp
@@ -38,7 +38,7 @@
 
     @Test
     fun parameterValueTest() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
         show {
             Inspectable(slotTableRecord) {
                 Surface {
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTreeTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTreeTest.kt
index 73d6452..89c3db5 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTreeTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTreeTest.kt
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.foundation.text.BasicText
 import androidx.compose.material.Button
 import androidx.compose.material.ModalDrawerLayout
 import androidx.compose.material.Surface
@@ -33,10 +34,12 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.node.OwnedLayer
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.style.TextDecoration
 import androidx.compose.ui.tooling.Group
 import androidx.compose.ui.tooling.Inspectable
 import androidx.compose.ui.tooling.R
-import androidx.compose.ui.tooling.SlotTableRecord
+import androidx.compose.ui.tooling.CompositionDataRecord
 import androidx.compose.ui.tooling.ToolingTest
 import androidx.compose.ui.tooling.asTree
 import androidx.compose.ui.tooling.position
@@ -73,7 +76,7 @@
     @Ignore("Manual test")
     @Test
     fun buildTree() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
 
         show {
             Inspectable(slotTableRecord) {
@@ -385,7 +388,7 @@
 
     @Test
     fun testStitchTreeFromModelDrawerLayout() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
 
         show {
             Inspectable(slotTableRecord) {
@@ -439,7 +442,7 @@
 
     @Test
     fun testSpacer() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
 
         show {
             Inspectable(slotTableRecord) {
@@ -461,11 +464,35 @@
         assertThat(node).isNotNull()
     }
 
+    @Test // regression test b/174855322
+    fun testBasicText() {
+        val slotTableRecord = CompositionDataRecord.create()
+
+        view.setTag(R.id.inspection_slot_table_set, slotTableRecord.store)
+        show {
+            Column {
+                BasicText(
+                    text = "Some text",
+                    style = TextStyle(textDecoration = TextDecoration.Underline)
+                )
+            }
+        }
+
+        val builder = LayoutInspectorTree()
+        val node = builder.convert(view)
+            .flatMap { flatten(it) }
+            .firstOrNull { it.name == "BasicText" }
+
+        assertThat(node).isNotNull()
+
+        assertThat(node?.parameters).isNotEmpty()
+    }
+
     @SdkSuppress(minSdkVersion = 29) // Render id is not returned for api < 29:  b/171519437
     @Test
     @Ignore("b/174152464")
     fun testTextId() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
 
         show {
             Inspectable(slotTableRecord) {
@@ -637,7 +664,7 @@
             else -> value?.toString() ?: "null"
         }
 
-    private fun dumpSlotTableSet(slotTableRecord: SlotTableRecord) {
+    private fun dumpSlotTableSet(slotTableRecord: CompositionDataRecord) {
         @Suppress("ConstantConditionIf")
         if (!DEBUG) {
             return
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/OffsetInformationTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/OffsetInformationTest.kt
index 96c833c..7c85542 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/OffsetInformationTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/OffsetInformationTest.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.ui.tooling.Group
 import androidx.compose.ui.tooling.Inspectable
-import androidx.compose.ui.tooling.SlotTableRecord
+import androidx.compose.ui.tooling.CompositionDataRecord
 import androidx.compose.ui.tooling.ToolingTest
 import androidx.compose.ui.tooling.asTree
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -32,7 +32,7 @@
 class OffsetInformationTest : ToolingTest() {
     @Test
     fun testOffset() {
-        val slotTableRecord = SlotTableRecord.create()
+        val slotTableRecord = CompositionDataRecord.create()
         show {
             Inspectable(slotTableRecord) {
                 OffsetData()
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt
index 83a5e5d..70cf0f0 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt
@@ -40,8 +40,8 @@
 import androidx.compose.ui.draw.paint
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.LinearGradient
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shadow
 import androidx.compose.ui.graphics.SolidColor
@@ -198,7 +198,11 @@
             factory.create(
                 node,
                 "brush",
-                LinearGradient(listOf(Color.Red, Color.Blue), 0.0f, 0.5f, 5.0f, 10.0f)
+                Brush.linearGradient(
+                    colors = listOf(Color.Red, Color.Blue),
+                    start = Offset(0.0f, 0.5f),
+                    end = Offset(5.0f, 10.0f)
+                )
             )!!
         ) {
             parameter("brush", ParameterType.String, "LinearGradient") {
@@ -206,10 +210,17 @@
                     parameter("0", ParameterType.Color, Color.Red.toArgb())
                     parameter("1", ParameterType.Color, Color.Blue.toArgb())
                 }
-                parameter("endX", ParameterType.Float, 5.0f)
-                parameter("endY", ParameterType.Float, 10.0f)
-                parameter("startX", ParameterType.Float, 0.0f)
-                parameter("startY", ParameterType.Float, 0.5f)
+                // Parameters are traversed in alphabetical order through reflection queries.
+                // Validate createdSize exists before validating end parameter
+                parameter("createdSize", ParameterType.String, "Unspecified")
+                parameter("end", ParameterType.String, Offset::class.java.simpleName) {
+                    parameter("x", ParameterType.DimensionDp, 2.5f)
+                    parameter("y", ParameterType.DimensionDp, 5.0f)
+                }
+                parameter("start", ParameterType.String, Offset::class.java.simpleName) {
+                    parameter("x", ParameterType.DimensionDp, 0.0f)
+                    parameter("y", ParameterType.DimensionDp, 0.25f)
+                }
                 parameter("tileMode", ParameterType.String, "Clamp")
             }
         }
diff --git a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/Inspectable.kt b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/Inspectable.kt
index d8145bc..b0d5680 100644
--- a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/Inspectable.kt
+++ b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/Inspectable.kt
@@ -17,9 +17,9 @@
 package androidx.compose.ui.tooling
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionData
 import androidx.compose.runtime.InternalComposeApi
 import androidx.compose.runtime.Providers
-import androidx.compose.runtime.SlotTable
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.tooling.InspectionTables
 import androidx.compose.ui.platform.InspectionMode
@@ -27,20 +27,19 @@
 import java.util.WeakHashMap
 
 /**
- * Storage for the preview generated [SlotTable]s.
+ * Storage for the preview generated [CompositionData]s.
  */
-internal interface SlotTableRecord {
-    @OptIn(InternalComposeApi::class)
-    val store: Set<SlotTable>
+internal interface CompositionDataRecord {
+    val store: Set<CompositionData>
 
     companion object {
-        fun create(): SlotTableRecord = SlotTableRecordImpl()
+        fun create(): CompositionDataRecord = CompositionDataRecordImpl()
     }
 }
 
-private class SlotTableRecordImpl : SlotTableRecord {
+private class CompositionDataRecordImpl : CompositionDataRecord {
     @OptIn(InternalComposeApi::class)
-    override val store: MutableSet<SlotTable> =
+    override val store: MutableSet<CompositionData> =
         Collections.newSetFromMap(WeakHashMap())
 }
 
@@ -48,19 +47,21 @@
  * A wrapper for compositions in inspection mode. The composition inside the Inspectable component
  * is in inspection mode.
  *
- * @param slotTableRecord [SlotTableRecord] to record the SlotTable used in the composition of [content]
+ * @param compositionDataRecord [CompositionDataRecord] to record the SlotTable used in the
+ * composition of [content]
  *
  * @suppress
  */
 @Composable
 @OptIn(InternalComposeApi::class)
 internal fun Inspectable(
-    slotTableRecord: SlotTableRecord,
+    compositionDataRecord: CompositionDataRecord,
     content: @Composable () -> Unit
 ) {
     currentComposer.collectKeySourceInformation()
-    val store = (slotTableRecord as SlotTableRecordImpl).store
-    store.add(currentComposer.slotTable)
+    currentComposer.collectParameterInformation()
+    val store = (compositionDataRecord as CompositionDataRecordImpl).store
+    store.add(currentComposer.compositionData)
     Providers(
         InspectionMode provides true,
         InspectionTables provides store,
diff --git a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/SlotTree.kt b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/SlotTree.kt
index e93ccb9..f38aa50 100644
--- a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/SlotTree.kt
+++ b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/SlotTree.kt
@@ -14,17 +14,13 @@
  * limitations under the License.
  */
 
-@file:OptIn(InternalComposeApi::class)
-
 package androidx.compose.ui.tooling
 
-import androidx.compose.runtime.InternalComposeApi
-import androidx.compose.runtime.SlotReader
-import androidx.compose.runtime.SlotTable
-import androidx.compose.runtime.keySourceInfoOf
+import androidx.compose.runtime.CompositionData
+import androidx.compose.runtime.CompositionGroup
 import androidx.compose.ui.layout.globalPosition
-import androidx.compose.ui.node.LayoutNode
-import androidx.compose.ui.node.ModifierInfo
+import androidx.compose.ui.layout.LayoutInfo
+import androidx.compose.ui.layout.ModifierInfo
 import androidx.compose.ui.unit.IntBounds
 import java.lang.reflect.Field
 import kotlin.math.max
@@ -160,9 +156,6 @@
  */
 data class JoinedKey(val left: Any?, val right: Any?)
 
-@OptIn(InternalComposeApi::class)
-private fun convertKey(key: Int): Any? = keySourceInfoOf(key)
-
 internal val emptyBox = IntBounds(0, 0, 0, 0)
 
 private val tokenizer = Regex("(\\d+)|([,])|([*])|([:])|L|(P\\([^)]*\\))|(C(\\(([^)]*)\\))?)|@")
@@ -432,30 +425,17 @@
 /**
  * Iterate the slot table and extract a group tree that corresponds to the content of the table.
  */
-@OptIn(InternalComposeApi::class)
-private fun SlotReader.getGroup(parentContext: SourceInformationContext?): Group {
-    val key = convertKey(groupKey)
-    val groupData = groupAux
-    val context = if (groupData != null && groupData is String) {
-        sourceInformationContextOf(groupData, parentContext)
-    } else null
-    val nodeGroup = isNode
-    val end = currentGroup + groupSize
-    val node = if (nodeGroup) groupNode else null
+private fun CompositionGroup.getGroup(parentContext: SourceInformationContext?): Group {
+    val key = key
+    val context = sourceInfo?.let { sourceInformationContextOf(it, parentContext) }
+    val node = node
     val data = mutableListOf<Any?>()
     val children = mutableListOf<Group>()
-    for (index in 0 until groupSlotCount) {
-        data.add(groupGet(index))
-    }
+    data.addAll(this.data)
+    for (child in compositionGroups)
+        children.add(child.getGroup(context))
 
-    reposition(currentGroup + 1)
-
-    // A group ends with a list of groups
-    while (currentGroup < end) {
-        children.add(getGroup(context))
-    }
-
-    val modifierInfo = if (node is LayoutNode) {
+    val modifierInfo = if (node is LayoutInfo) {
         node.getModifierInfo()
     } else {
         emptyList()
@@ -463,14 +443,14 @@
 
     // Calculate bounding box
     val box = when (node) {
-        is LayoutNode -> boundsOfLayoutNode(node)
+        is LayoutInfo -> boundsOfLayoutNode(node)
         else ->
             if (children.isEmpty()) emptyBox else
                 children.map { g -> g.box }.reduce { acc, box -> box.union(acc) }
     }
-    return if (nodeGroup) NodeGroup(
+    return if (node != null) NodeGroup(
         key,
-        node as Any,
+        node,
         box,
         data,
         modifierInfo,
@@ -491,8 +471,8 @@
         )
 }
 
-private fun boundsOfLayoutNode(node: LayoutNode): IntBounds {
-    if (node.owner == null) {
+private fun boundsOfLayoutNode(node: LayoutInfo): IntBounds {
+    if (!node.isAttached) {
         return IntBounds(
             left = 0,
             top = 0,
@@ -513,8 +493,7 @@
  * Return a group tree for for the slot table that represents the entire content of the slot
  * table.
  */
-@OptIn(InternalComposeApi::class)
-fun SlotTable.asTree(): Group = read { it.getGroup(null) }
+fun CompositionData.asTree(): Group = compositionGroups.first().getGroup(null)
 
 internal fun IntBounds.union(other: IntBounds): IntBounds {
     if (this == emptyBox) return other else if (other == emptyBox) return this
@@ -547,62 +526,61 @@
     context: SourceInformationContext?
 ): List<ParameterInformation> {
     if (data.isNotEmpty()) {
-        val recomposeScope = data[0]
+        val recomposeScope = data.firstOrNull {
+            it != null && it.javaClass.name.endsWith(recomposeScopeNameSuffix)
+        }
         if (recomposeScope != null) {
-            val scopeClass = recomposeScope.javaClass
-            if (scopeClass.name.endsWith(recomposeScopeNameSuffix)) {
-                try {
-                    val blockField = scopeClass.accessibleField("block")
-                    if (blockField != null) {
-                        val block = blockField.get(recomposeScope)
-                        if (block != null) {
-                            val blockClass = block.javaClass
-                            val defaultsField = blockClass.accessibleField(defaultFieldName)
-                            val changedField = blockClass.accessibleField(changedFieldName)
-                            val default =
-                                if (defaultsField != null) defaultsField.get(block) as Int else 0
-                            val changed =
-                                if (changedField != null) changedField.get(block) as Int else 0
-                            val fields = blockClass.declaredFields
-                                .filter {
-                                    it.name.startsWith(parameterPrefix) &&
-                                        !it.name.startsWith(internalFieldPrefix) &&
-                                        !it.name.startsWith(jacocoDataField)
-                                }.sortedBy { it.name }
-                            val parameters = mutableListOf<ParameterInformation>()
-                            val parametersMetadata = context?.parameters ?: emptyList()
-                            repeat(fields.size) { index ->
-                                val metadata = if (index < parametersMetadata.size)
-                                    parametersMetadata[index] else Parameter(index)
-                                if (metadata.sortedIndex >= fields.size) return@repeat
-                                val field = fields[metadata.sortedIndex]
-                                field.isAccessible = true
-                                val value = field.get(block)
-                                val fromDefault = (1 shl index) and default != 0
-                                val changedOffset = index * BITS_PER_SLOT + 1
-                                val parameterChanged = (
-                                    (SLOT_MASK shl changedOffset) and changed
-                                    ) shr changedOffset
-                                val static = parameterChanged and STATIC_BITS == STATIC_BITS
-                                val compared = parameterChanged and STATIC_BITS == 0
-                                val stable = parameterChanged and STABLE_BITS == 0
-                                parameters.add(
-                                    ParameterInformation(
-                                        name = field.name.substring(1),
-                                        value = value,
-                                        fromDefault = fromDefault,
-                                        static = static,
-                                        compared = compared && !fromDefault,
-                                        inlineClass = metadata.inlineClass,
-                                        stable = stable
-                                    )
+            try {
+                val blockField = recomposeScope.javaClass.accessibleField("block")
+                if (blockField != null) {
+                    val block = blockField.get(recomposeScope)
+                    if (block != null) {
+                        val blockClass = block.javaClass
+                        val defaultsField = blockClass.accessibleField(defaultFieldName)
+                        val changedField = blockClass.accessibleField(changedFieldName)
+                        val default =
+                            if (defaultsField != null) defaultsField.get(block) as Int else 0
+                        val changed =
+                            if (changedField != null) changedField.get(block) as Int else 0
+                        val fields = blockClass.declaredFields
+                            .filter {
+                                it.name.startsWith(parameterPrefix) &&
+                                    !it.name.startsWith(internalFieldPrefix) &&
+                                    !it.name.startsWith(jacocoDataField)
+                            }.sortedBy { it.name }
+                        val parameters = mutableListOf<ParameterInformation>()
+                        val parametersMetadata = context?.parameters ?: emptyList()
+                        repeat(fields.size) { index ->
+                            val metadata = if (index < parametersMetadata.size)
+                                parametersMetadata[index] else Parameter(index)
+                            if (metadata.sortedIndex >= fields.size) return@repeat
+                            val field = fields[metadata.sortedIndex]
+                            field.isAccessible = true
+                            val value = field.get(block)
+                            val fromDefault = (1 shl index) and default != 0
+                            val changedOffset = index * BITS_PER_SLOT + 1
+                            val parameterChanged = (
+                                (SLOT_MASK shl changedOffset) and changed
+                                ) shr changedOffset
+                            val static = parameterChanged and STATIC_BITS == STATIC_BITS
+                            val compared = parameterChanged and STATIC_BITS == 0
+                            val stable = parameterChanged and STABLE_BITS == 0
+                            parameters.add(
+                                ParameterInformation(
+                                    name = field.name.substring(1),
+                                    value = value,
+                                    fromDefault = fromDefault,
+                                    static = static,
+                                    compared = compared && !fromDefault,
+                                    inlineClass = metadata.inlineClass,
+                                    stable = stable
                                 )
-                            }
-                            return parameters
+                            )
                         }
+                        return parameters
                     }
-                } catch (_: Throwable) {
                 }
+            } catch (_: Throwable) {
             }
         }
     }
diff --git a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/inspector/InspectorNode.kt b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/inspector/InspectorNode.kt
index 5f5bcaf..c6add12 100644
--- a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/inspector/InspectorNode.kt
+++ b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/inspector/InspectorNode.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.ui.tooling.inspector
 
-import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.layout.LayoutInfo
 
 /**
  * Node representing a Composable for the Layout Inspector.
@@ -106,7 +106,7 @@
  */
 internal class MutableInspectorNode {
     var id = 0L
-    var layoutNodes = mutableListOf<LayoutNode>()
+    var layoutNodes = mutableListOf<LayoutInfo>()
     var name = ""
     var fileName = ""
     var packageHash = -1
diff --git a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTree.kt b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTree.kt
index 6f30139..f2acc87 100644
--- a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTree.kt
+++ b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTree.kt
@@ -17,8 +17,9 @@
 package androidx.compose.ui.tooling.inspector
 
 import android.view.View
+import androidx.compose.runtime.CompositionData
 import androidx.compose.runtime.InternalComposeApi
-import androidx.compose.runtime.SlotTable
+import androidx.compose.ui.layout.LayoutInfo
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.node.OwnedLayer
 import androidx.compose.ui.tooling.Group
@@ -62,8 +63,8 @@
     private val parameterFactory = ParameterFactory(inlineClassConverter)
     private val cache = ArrayDeque<MutableInspectorNode>()
     private var generatedId = -1L
-    /** Map from [LayoutNode] to the nearest [InspectorNode] that contains it */
-    private val claimedNodes = IdentityHashMap<LayoutNode, InspectorNode>()
+    /** Map from [LayoutInfo] to the nearest [InspectorNode] that contains it */
+    private val claimedNodes = IdentityHashMap<LayoutInfo, InspectorNode>()
     /** Map from parent tree to child trees that are about to be stitched together */
     private val treeMap = IdentityHashMap<MutableInspectorNode, MutableList<MutableInspectorNode>>()
     /** Map from owner node to child trees that are about to be stitched to this owner */
@@ -73,13 +74,13 @@
         Collections.newSetFromMap(IdentityHashMap<MutableInspectorNode, Boolean>())
 
     /**
-     * Converts the [SlotTable] set held by [view] into a list of root nodes.
+     * Converts the [CompositionData] set held by [view] into a list of root nodes.
      */
     @OptIn(InternalComposeApi::class)
     fun convert(view: View): List<InspectorNode> {
         parameterFactory.density = Density(view.context)
         @Suppress("UNCHECKED_CAST")
-        val tables = view.getTag(R.id.inspection_slot_table_set) as? Set<SlotTable>
+        val tables = view.getTag(R.id.inspection_slot_table_set) as? Set<CompositionData>
             ?: return emptyList()
         clear()
         val result = convert(tables)
@@ -111,7 +112,7 @@
     }
 
     @OptIn(InternalComposeApi::class)
-    private fun convert(tables: Set<SlotTable>): List<InspectorNode> {
+    private fun convert(tables: Set<CompositionData>): List<InspectorNode> {
         val trees = tables.map { convert(it) }
         return when (trees.size) {
             0 -> listOf()
@@ -121,20 +122,21 @@
     }
 
     /**
-     * Stitch separate trees together using the [LayoutNode]s found in the [SlotTable]s.
+     * Stitch separate trees together using the [LayoutNode]s found in the [CompositionData]s.
      *
-     * Some constructs in Compose (e.g. ModalDrawerLayout) will result is multiple [SlotTable]s.
-     * This code will attempt to stitch the resulting [InspectorNode] trees together by looking
-     * at the parent of each [LayoutNode].
+     * Some constructs in Compose (e.g. ModalDrawerLayout) will result is multiple
+     * [CompositionData]s. This code will attempt to stitch the resulting [InspectorNode] trees
+     * together by looking at the parent of each [LayoutNode].
+     *
      * If this algorithm is successful the result of this function will be a list with a single
      * tree.
      */
     private fun stitchTreesByLayoutNode(trees: List<MutableInspectorNode>): List<InspectorNode> {
-        val layoutToTreeMap = IdentityHashMap<LayoutNode, MutableInspectorNode>()
+        val layoutToTreeMap = IdentityHashMap<LayoutInfo, MutableInspectorNode>()
         trees.forEach { tree -> tree.layoutNodes.forEach { layoutToTreeMap[it] = tree } }
         trees.forEach { tree ->
             val layout = tree.layoutNodes.lastOrNull()
-            val parentLayout = generateSequence(layout) { it.parent }.firstOrNull {
+            val parentLayout = generateSequence(layout) { it.parentInfo }.firstOrNull {
                 val otherTree = layoutToTreeMap[it]
                 otherTree != null && otherTree != tree
             }
@@ -199,7 +201,7 @@
     }
 
     @OptIn(InternalComposeApi::class)
-    private fun convert(table: SlotTable): MutableInspectorNode {
+    private fun convert(table: CompositionData): MutableInspectorNode {
         val fakeParent = newNode()
         addToParent(fakeParent, listOf(convert(table.asTree())))
         return fakeParent
@@ -262,7 +264,7 @@
     private fun parse(group: Group): MutableInspectorNode {
         val node = newNode()
         node.id = getRenderNode(group)
-        ((group as? NodeGroup)?.node as? LayoutNode)?.let { node.layoutNodes.add(it) }
+        ((group as? NodeGroup)?.node as? LayoutInfo)?.let { node.layoutNodes.add(it) }
         if (!parseCallLocation(group, node) && group.name.isNullOrEmpty()) {
             return markUnwanted(node)
         }
diff --git a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt
index 7e6d2b6..99eb764 100644
--- a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt
+++ b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt
@@ -40,12 +40,12 @@
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.platform.AmbientFontLoader
-import androidx.compose.ui.platform.AndroidOwner
 import androidx.compose.ui.platform.AnimationClockAmbient
 import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.tooling.Group
 import androidx.compose.ui.tooling.Inspectable
-import androidx.compose.ui.tooling.SlotTableRecord
+import androidx.compose.ui.tooling.CompositionDataRecord
 import androidx.compose.ui.tooling.SourceLocation
 import androidx.compose.ui.tooling.asTree
 import androidx.compose.ui.tooling.preview.animation.PreviewAnimationClock
@@ -126,7 +126,7 @@
      */
     private var debugPaintBounds = false
     internal var viewInfos: List<ViewInfo> = emptyList()
-    private val slotTableRecord = SlotTableRecord.create()
+    private val slotTableRecord = CompositionDataRecord.create()
 
     /**
      * Simple function name of the Composable being previewed.
@@ -461,7 +461,8 @@
                         // (an AndroidOwner) when setting the clock time to make sure the Compose
                         // Preview will animate when the states are read inside the draw scope.
                         val composeView = getChildAt(0) as ComposeView
-                        (composeView.getChildAt(0) as? AndroidOwner)?.invalidateDescendants()
+                        (composeView.getChildAt(0) as? ViewRootForTest)
+                            ?.invalidateDescendants()
                     }
                     Providers(AmbientAnimationClock provides clock) {
                         composable()
diff --git a/compose/ui/ui-unit/api/current.txt b/compose/ui/ui-unit/api/current.txt
index 171a34a..1a4ba07 100644
--- a/compose/ui/ui-unit/api/current.txt
+++ b/compose/ui/ui-unit/api/current.txt
@@ -57,9 +57,10 @@
     method @androidx.compose.runtime.Stable public static long enforce-WVSBfsc(long, long otherConstraints);
     method public static boolean getHasFixedHeight-BRTryo0(long);
     method public static boolean getHasFixedWidth-BRTryo0(long);
+    method @androidx.compose.runtime.Stable public static boolean isSatisfiedBy-m9CmiCs(long, long size);
     method public static boolean isZero-BRTryo0(long);
     method @androidx.compose.runtime.Stable public static long offset-dAqVMF8(long, optional int horizontal, optional int vertical);
-    method @androidx.compose.runtime.Stable public static boolean satisfiedBy-m9CmiCs(long, long size);
+    method @Deprecated @androidx.compose.runtime.Stable public static boolean satisfiedBy-m9CmiCs(long, long size);
   }
 
   @androidx.compose.runtime.Immutable public interface Density {
@@ -92,17 +93,14 @@
     method @androidx.compose.runtime.Stable public static inline operator float div-0680j_4(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float div-D9Ej5fM(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float div-D9Ej5fM(float $this, int other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-_kMlNio(float $this, float other);
     method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
     method public static boolean equals-impl0(float p1, float p2);
     method public float getValue();
     method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
     method @androidx.compose.runtime.Stable public static inline operator float minus-0680j_4(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float plus-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-0680j_4(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-D9Ej5fM(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-D9Ej5fM(float $this, int other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-_kMlNio(float $this, float other);
     method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
     method @androidx.compose.runtime.Stable public static inline operator float unaryMinus-D9Ej5fM(float $this);
     property public final float value;
@@ -118,91 +116,30 @@
     property public final float Unspecified;
   }
 
-  @androidx.compose.runtime.Immutable public final inline class DpCubed implements java.lang.Comparable<androidx.compose.ui.unit.DpCubed> {
-    ctor public DpCubed();
-    method @androidx.compose.runtime.Stable public operator int compareTo-MZZJ3Fw(float p);
-    method @androidx.compose.runtime.Stable public static operator int compareTo-MZZJ3Fw(float $this, float other);
-    method public static float constructor-impl(float value);
-    method @androidx.compose.runtime.Stable public static inline operator float div-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-5FSHFdU(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-MZZJ3Fw(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
-    method public static boolean equals-impl0(float p1, float p2);
-    method public float getValue();
-    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
-    method @androidx.compose.runtime.Stable public static inline operator float minus-MZZJ3Fw(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float plus-MZZJ3Fw(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float times-5FSHFdU(float $this, float other);
-    method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
-    property public final float value;
-  }
-
-  @androidx.compose.runtime.Immutable public final inline class DpInverse implements java.lang.Comparable<androidx.compose.ui.unit.DpInverse> {
-    ctor public DpInverse();
-    method @androidx.compose.runtime.Stable public operator int compareTo-NdYQkfI(float p);
-    method @androidx.compose.runtime.Stable public static operator int compareTo-NdYQkfI(float $this, float other);
-    method public static float constructor-impl(float value);
-    method @androidx.compose.runtime.Stable public static inline operator float div-PAM_5xQ(float $this, float other);
-    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
-    method public static boolean equals-impl0(float p1, float p2);
-    method public float getValue();
-    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
-    method @androidx.compose.runtime.Stable public static inline operator float minus-NdYQkfI(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float plus-NdYQkfI(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float times-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-MZZJ3Fw(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-PAM_5xQ(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
-    property public final float value;
-  }
-
   public final class DpKt {
     method @androidx.compose.runtime.Stable public static inline long Position-ioHfwGI(float x, float y);
     method @androidx.compose.runtime.Stable public static inline float coerceAtLeast-ioHfwGI(float, float minimumValue);
     method @androidx.compose.runtime.Stable public static inline float coerceAtMost-ioHfwGI(float, float maximumValue);
     method @androidx.compose.runtime.Stable public static inline float coerceIn-qYQSm_w(float, float minimumValue, float maximumValue);
-    method @androidx.compose.runtime.Stable public static inline operator float div-Cp9Wa6o(int, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-NSq4UQk(double, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-NnR5yEA(float, float other);
     method @androidx.compose.runtime.Stable public static float getDistance-IsWn59c(long);
     method public static inline float getDp(int);
     method public static inline float getDp(double);
     method public static inline float getDp(float);
     method public static inline float getHeight(androidx.compose.ui.unit.Bounds);
     method public static inline float getWidth(androidx.compose.ui.unit.Bounds);
-    method @androidx.compose.runtime.Stable public static inline boolean isFinite-0680j_4(float);
+    method public static inline boolean isFinite-0680j_4(float);
+    method public static inline boolean isSpecified-0680j_4(float);
+    method public static inline boolean isUnspecified-0680j_4(float);
     method @androidx.compose.runtime.Stable public static long lerp-2vlZtig(long start, long stop, float fraction);
     method @androidx.compose.runtime.Stable public static float lerp-7oHWEOI(float start, float stop, float fraction);
     method @androidx.compose.runtime.Stable public static inline float max-ioHfwGI(float a, float b);
     method @androidx.compose.runtime.Stable public static inline float min-ioHfwGI(float a, float b);
+    method public static inline float takeOrElse-RiydCdY(float, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.Dp> block);
     method @androidx.compose.runtime.Stable public static inline operator float times-Cp9Wa6o(int, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-NSq4UQk(double, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-NnR5yEA(float, float other);
   }
 
-  @androidx.compose.runtime.Immutable public final inline class DpSquared implements java.lang.Comparable<androidx.compose.ui.unit.DpSquared> {
-    ctor public DpSquared();
-    method @androidx.compose.runtime.Stable public operator int compareTo-_kMlNio(float p);
-    method @androidx.compose.runtime.Stable public static operator int compareTo-_kMlNio(float $this, float other);
-    method public static float constructor-impl(float value);
-    method @androidx.compose.runtime.Stable public static inline operator float div-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-MZZJ3Fw(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-ym20N70(float $this, float other);
-    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
-    method public static boolean equals-impl0(float p1, float p2);
-    method public float getValue();
-    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
-    method @androidx.compose.runtime.Stable public static inline operator float minus-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float plus-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-ym20N70(float $this, float other);
-    method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
-    property public final float value;
-  }
-
   @androidx.compose.runtime.Immutable public final inline class Duration implements java.lang.Comparable<androidx.compose.ui.unit.Duration> {
     ctor public Duration();
     method @androidx.compose.runtime.Stable public int compareTo-WUeva1s(long p);
@@ -454,7 +391,6 @@
     method public static boolean isEm-impl(long $this);
     method @Deprecated public static boolean isInherit-impl(long $this);
     method public static boolean isSp-impl(long $this);
-    method public static boolean isUnspecified-impl(long $this);
     method public static inline operator long minus--R2X_6o(long $this, long other);
     method public static inline operator long plus--R2X_6o(long $this, long other);
     method public static inline operator long times-XSAIIZE(long $this, float other);
@@ -489,9 +425,12 @@
     method public static long getSp(float);
     method public static long getSp(double);
     method public static long getSp(int);
+    method public static inline boolean isSpecified--R2X_6o(long);
+    method public static boolean isUnspecified--R2X_6o(long);
     method @androidx.compose.runtime.Stable public static long lerp-KeuwX78(long a, long b, float t);
     method @androidx.compose.runtime.Stable public static inline long max-8E83U4Q(long a, long b);
     method @androidx.compose.runtime.Stable public static inline long min-8E83U4Q(long a, long b);
+    method public static inline long takeOrElse-bAewZlA(long, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.TextUnit> block);
     method @androidx.compose.runtime.Stable public static inline operator long times-0PRCd3Q(double, long other);
     method @androidx.compose.runtime.Stable public static inline operator long times-Ew26DjI(float, long other);
     method @androidx.compose.runtime.Stable public static inline operator long times-VJWtCv4(int, long other);
@@ -531,7 +470,10 @@
   }
 
   public final class UptimeKt {
+    method public static inline boolean isSpecified-fQUwLeo(long);
+    method public static inline boolean isUnspecified-fQUwLeo(long);
     method public static operator long plus-I3RPvYE(long, long uptime);
+    method public static inline long takeOrElse-N-58ELw(long, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.Uptime> block);
   }
 
   @androidx.compose.runtime.Immutable public final inline class Velocity {
diff --git a/compose/ui/ui-unit/api/public_plus_experimental_current.txt b/compose/ui/ui-unit/api/public_plus_experimental_current.txt
index 171a34a..1a4ba07 100644
--- a/compose/ui/ui-unit/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-unit/api/public_plus_experimental_current.txt
@@ -57,9 +57,10 @@
     method @androidx.compose.runtime.Stable public static long enforce-WVSBfsc(long, long otherConstraints);
     method public static boolean getHasFixedHeight-BRTryo0(long);
     method public static boolean getHasFixedWidth-BRTryo0(long);
+    method @androidx.compose.runtime.Stable public static boolean isSatisfiedBy-m9CmiCs(long, long size);
     method public static boolean isZero-BRTryo0(long);
     method @androidx.compose.runtime.Stable public static long offset-dAqVMF8(long, optional int horizontal, optional int vertical);
-    method @androidx.compose.runtime.Stable public static boolean satisfiedBy-m9CmiCs(long, long size);
+    method @Deprecated @androidx.compose.runtime.Stable public static boolean satisfiedBy-m9CmiCs(long, long size);
   }
 
   @androidx.compose.runtime.Immutable public interface Density {
@@ -92,17 +93,14 @@
     method @androidx.compose.runtime.Stable public static inline operator float div-0680j_4(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float div-D9Ej5fM(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float div-D9Ej5fM(float $this, int other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-_kMlNio(float $this, float other);
     method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
     method public static boolean equals-impl0(float p1, float p2);
     method public float getValue();
     method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
     method @androidx.compose.runtime.Stable public static inline operator float minus-0680j_4(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float plus-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-0680j_4(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-D9Ej5fM(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-D9Ej5fM(float $this, int other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-_kMlNio(float $this, float other);
     method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
     method @androidx.compose.runtime.Stable public static inline operator float unaryMinus-D9Ej5fM(float $this);
     property public final float value;
@@ -118,91 +116,30 @@
     property public final float Unspecified;
   }
 
-  @androidx.compose.runtime.Immutable public final inline class DpCubed implements java.lang.Comparable<androidx.compose.ui.unit.DpCubed> {
-    ctor public DpCubed();
-    method @androidx.compose.runtime.Stable public operator int compareTo-MZZJ3Fw(float p);
-    method @androidx.compose.runtime.Stable public static operator int compareTo-MZZJ3Fw(float $this, float other);
-    method public static float constructor-impl(float value);
-    method @androidx.compose.runtime.Stable public static inline operator float div-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-5FSHFdU(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-MZZJ3Fw(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
-    method public static boolean equals-impl0(float p1, float p2);
-    method public float getValue();
-    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
-    method @androidx.compose.runtime.Stable public static inline operator float minus-MZZJ3Fw(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float plus-MZZJ3Fw(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float times-5FSHFdU(float $this, float other);
-    method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
-    property public final float value;
-  }
-
-  @androidx.compose.runtime.Immutable public final inline class DpInverse implements java.lang.Comparable<androidx.compose.ui.unit.DpInverse> {
-    ctor public DpInverse();
-    method @androidx.compose.runtime.Stable public operator int compareTo-NdYQkfI(float p);
-    method @androidx.compose.runtime.Stable public static operator int compareTo-NdYQkfI(float $this, float other);
-    method public static float constructor-impl(float value);
-    method @androidx.compose.runtime.Stable public static inline operator float div-PAM_5xQ(float $this, float other);
-    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
-    method public static boolean equals-impl0(float p1, float p2);
-    method public float getValue();
-    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
-    method @androidx.compose.runtime.Stable public static inline operator float minus-NdYQkfI(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float plus-NdYQkfI(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float times-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-MZZJ3Fw(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-PAM_5xQ(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
-    property public final float value;
-  }
-
   public final class DpKt {
     method @androidx.compose.runtime.Stable public static inline long Position-ioHfwGI(float x, float y);
     method @androidx.compose.runtime.Stable public static inline float coerceAtLeast-ioHfwGI(float, float minimumValue);
     method @androidx.compose.runtime.Stable public static inline float coerceAtMost-ioHfwGI(float, float maximumValue);
     method @androidx.compose.runtime.Stable public static inline float coerceIn-qYQSm_w(float, float minimumValue, float maximumValue);
-    method @androidx.compose.runtime.Stable public static inline operator float div-Cp9Wa6o(int, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-NSq4UQk(double, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-NnR5yEA(float, float other);
     method @androidx.compose.runtime.Stable public static float getDistance-IsWn59c(long);
     method public static inline float getDp(int);
     method public static inline float getDp(double);
     method public static inline float getDp(float);
     method public static inline float getHeight(androidx.compose.ui.unit.Bounds);
     method public static inline float getWidth(androidx.compose.ui.unit.Bounds);
-    method @androidx.compose.runtime.Stable public static inline boolean isFinite-0680j_4(float);
+    method public static inline boolean isFinite-0680j_4(float);
+    method public static inline boolean isSpecified-0680j_4(float);
+    method public static inline boolean isUnspecified-0680j_4(float);
     method @androidx.compose.runtime.Stable public static long lerp-2vlZtig(long start, long stop, float fraction);
     method @androidx.compose.runtime.Stable public static float lerp-7oHWEOI(float start, float stop, float fraction);
     method @androidx.compose.runtime.Stable public static inline float max-ioHfwGI(float a, float b);
     method @androidx.compose.runtime.Stable public static inline float min-ioHfwGI(float a, float b);
+    method public static inline float takeOrElse-RiydCdY(float, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.Dp> block);
     method @androidx.compose.runtime.Stable public static inline operator float times-Cp9Wa6o(int, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-NSq4UQk(double, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-NnR5yEA(float, float other);
   }
 
-  @androidx.compose.runtime.Immutable public final inline class DpSquared implements java.lang.Comparable<androidx.compose.ui.unit.DpSquared> {
-    ctor public DpSquared();
-    method @androidx.compose.runtime.Stable public operator int compareTo-_kMlNio(float p);
-    method @androidx.compose.runtime.Stable public static operator int compareTo-_kMlNio(float $this, float other);
-    method public static float constructor-impl(float value);
-    method @androidx.compose.runtime.Stable public static inline operator float div-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-MZZJ3Fw(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-ym20N70(float $this, float other);
-    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
-    method public static boolean equals-impl0(float p1, float p2);
-    method public float getValue();
-    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
-    method @androidx.compose.runtime.Stable public static inline operator float minus-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float plus-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-ym20N70(float $this, float other);
-    method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
-    property public final float value;
-  }
-
   @androidx.compose.runtime.Immutable public final inline class Duration implements java.lang.Comparable<androidx.compose.ui.unit.Duration> {
     ctor public Duration();
     method @androidx.compose.runtime.Stable public int compareTo-WUeva1s(long p);
@@ -454,7 +391,6 @@
     method public static boolean isEm-impl(long $this);
     method @Deprecated public static boolean isInherit-impl(long $this);
     method public static boolean isSp-impl(long $this);
-    method public static boolean isUnspecified-impl(long $this);
     method public static inline operator long minus--R2X_6o(long $this, long other);
     method public static inline operator long plus--R2X_6o(long $this, long other);
     method public static inline operator long times-XSAIIZE(long $this, float other);
@@ -489,9 +425,12 @@
     method public static long getSp(float);
     method public static long getSp(double);
     method public static long getSp(int);
+    method public static inline boolean isSpecified--R2X_6o(long);
+    method public static boolean isUnspecified--R2X_6o(long);
     method @androidx.compose.runtime.Stable public static long lerp-KeuwX78(long a, long b, float t);
     method @androidx.compose.runtime.Stable public static inline long max-8E83U4Q(long a, long b);
     method @androidx.compose.runtime.Stable public static inline long min-8E83U4Q(long a, long b);
+    method public static inline long takeOrElse-bAewZlA(long, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.TextUnit> block);
     method @androidx.compose.runtime.Stable public static inline operator long times-0PRCd3Q(double, long other);
     method @androidx.compose.runtime.Stable public static inline operator long times-Ew26DjI(float, long other);
     method @androidx.compose.runtime.Stable public static inline operator long times-VJWtCv4(int, long other);
@@ -531,7 +470,10 @@
   }
 
   public final class UptimeKt {
+    method public static inline boolean isSpecified-fQUwLeo(long);
+    method public static inline boolean isUnspecified-fQUwLeo(long);
     method public static operator long plus-I3RPvYE(long, long uptime);
+    method public static inline long takeOrElse-N-58ELw(long, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.Uptime> block);
   }
 
   @androidx.compose.runtime.Immutable public final inline class Velocity {
diff --git a/compose/ui/ui-unit/api/restricted_current.txt b/compose/ui/ui-unit/api/restricted_current.txt
index 6eca502..79c6020 100644
--- a/compose/ui/ui-unit/api/restricted_current.txt
+++ b/compose/ui/ui-unit/api/restricted_current.txt
@@ -57,9 +57,10 @@
     method @androidx.compose.runtime.Stable public static long enforce-WVSBfsc(long, long otherConstraints);
     method public static boolean getHasFixedHeight-BRTryo0(long);
     method public static boolean getHasFixedWidth-BRTryo0(long);
+    method @androidx.compose.runtime.Stable public static boolean isSatisfiedBy-m9CmiCs(long, long size);
     method public static boolean isZero-BRTryo0(long);
     method @androidx.compose.runtime.Stable public static long offset-dAqVMF8(long, optional int horizontal, optional int vertical);
-    method @androidx.compose.runtime.Stable public static boolean satisfiedBy-m9CmiCs(long, long size);
+    method @Deprecated @androidx.compose.runtime.Stable public static boolean satisfiedBy-m9CmiCs(long, long size);
   }
 
   @androidx.compose.runtime.Immutable public interface Density {
@@ -92,17 +93,14 @@
     method @androidx.compose.runtime.Stable public static inline operator float div-0680j_4(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float div-D9Ej5fM(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float div-D9Ej5fM(float $this, int other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-_kMlNio(float $this, float other);
     method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
     method public static boolean equals-impl0(float p1, float p2);
     method public float getValue();
     method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
     method @androidx.compose.runtime.Stable public static inline operator float minus-0680j_4(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float plus-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-0680j_4(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-D9Ej5fM(float $this, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-D9Ej5fM(float $this, int other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-_kMlNio(float $this, float other);
     method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
     method @androidx.compose.runtime.Stable public static inline operator float unaryMinus-D9Ej5fM(float $this);
     property public final float value;
@@ -118,91 +116,30 @@
     property public final float Unspecified;
   }
 
-  @androidx.compose.runtime.Immutable public final inline class DpCubed implements java.lang.Comparable<androidx.compose.ui.unit.DpCubed> {
-    ctor public DpCubed();
-    method @androidx.compose.runtime.Stable public operator int compareTo-MZZJ3Fw(float p);
-    method @androidx.compose.runtime.Stable public static operator int compareTo-MZZJ3Fw(float $this, float other);
-    method public static float constructor-impl(float value);
-    method @androidx.compose.runtime.Stable public static inline operator float div-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-5FSHFdU(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-MZZJ3Fw(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
-    method public static boolean equals-impl0(float p1, float p2);
-    method public float getValue();
-    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
-    method @androidx.compose.runtime.Stable public static inline operator float minus-MZZJ3Fw(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float plus-MZZJ3Fw(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float times-5FSHFdU(float $this, float other);
-    method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
-    property public final float value;
-  }
-
-  @androidx.compose.runtime.Immutable public final inline class DpInverse implements java.lang.Comparable<androidx.compose.ui.unit.DpInverse> {
-    ctor public DpInverse();
-    method @androidx.compose.runtime.Stable public operator int compareTo-NdYQkfI(float p);
-    method @androidx.compose.runtime.Stable public static operator int compareTo-NdYQkfI(float $this, float other);
-    method public static float constructor-impl(float value);
-    method @androidx.compose.runtime.Stable public static inline operator float div-PAM_5xQ(float $this, float other);
-    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
-    method public static boolean equals-impl0(float p1, float p2);
-    method public float getValue();
-    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
-    method @androidx.compose.runtime.Stable public static inline operator float minus-NdYQkfI(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float plus-NdYQkfI(float $this, float dimension);
-    method @androidx.compose.runtime.Stable public static inline operator float times-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-MZZJ3Fw(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-PAM_5xQ(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
-    property public final float value;
-  }
-
   public final class DpKt {
     method @androidx.compose.runtime.Stable public static inline long Position-ioHfwGI(float x, float y);
     method @androidx.compose.runtime.Stable public static inline float coerceAtLeast-ioHfwGI(float, float minimumValue);
     method @androidx.compose.runtime.Stable public static inline float coerceAtMost-ioHfwGI(float, float maximumValue);
     method @androidx.compose.runtime.Stable public static inline float coerceIn-qYQSm_w(float, float minimumValue, float maximumValue);
-    method @androidx.compose.runtime.Stable public static inline operator float div-Cp9Wa6o(int, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-NSq4UQk(double, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-NnR5yEA(float, float other);
     method @androidx.compose.runtime.Stable public static float getDistance-IsWn59c(long);
     method public static inline float getDp(int);
     method public static inline float getDp(double);
     method public static inline float getDp(float);
     method public static inline float getHeight(androidx.compose.ui.unit.Bounds);
     method public static inline float getWidth(androidx.compose.ui.unit.Bounds);
-    method @androidx.compose.runtime.Stable public static inline boolean isFinite-0680j_4(float);
+    method public static inline boolean isFinite-0680j_4(float);
+    method public static inline boolean isSpecified-0680j_4(float);
+    method public static inline boolean isUnspecified-0680j_4(float);
     method @androidx.compose.runtime.Stable public static long lerp-2vlZtig(long start, long stop, float fraction);
     method @androidx.compose.runtime.Stable public static float lerp-7oHWEOI(float start, float stop, float fraction);
     method @androidx.compose.runtime.Stable public static inline float max-ioHfwGI(float a, float b);
     method @androidx.compose.runtime.Stable public static inline float min-ioHfwGI(float a, float b);
+    method public static inline float takeOrElse-RiydCdY(float, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.Dp> block);
     method @androidx.compose.runtime.Stable public static inline operator float times-Cp9Wa6o(int, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-NSq4UQk(double, float other);
     method @androidx.compose.runtime.Stable public static inline operator float times-NnR5yEA(float, float other);
   }
 
-  @androidx.compose.runtime.Immutable public final inline class DpSquared implements java.lang.Comparable<androidx.compose.ui.unit.DpSquared> {
-    ctor public DpSquared();
-    method @androidx.compose.runtime.Stable public operator int compareTo-_kMlNio(float p);
-    method @androidx.compose.runtime.Stable public static operator int compareTo-_kMlNio(float $this, float other);
-    method public static float constructor-impl(float value);
-    method @androidx.compose.runtime.Stable public static inline operator float div-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-MZZJ3Fw(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float div-ym20N70(float $this, float other);
-    method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(float p, Object? p1);
-    method public static boolean equals-impl0(float p1, float p2);
-    method public float getValue();
-    method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(float p);
-    method @androidx.compose.runtime.Stable public static inline operator float minus-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float plus-_kMlNio(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-0680j_4(float $this, float other);
-    method @androidx.compose.runtime.Stable public static inline operator float times-ym20N70(float $this, float other);
-    method @androidx.compose.runtime.Stable public static String toString-impl(float $this);
-    property public final float value;
-  }
-
   @androidx.compose.runtime.Immutable public final inline class Duration implements java.lang.Comparable<androidx.compose.ui.unit.Duration> {
     ctor public Duration();
     method @androidx.compose.runtime.Stable public int compareTo-WUeva1s(long p);
@@ -454,7 +391,6 @@
     method public static boolean isEm-impl(long $this);
     method @Deprecated public static boolean isInherit-impl(long $this);
     method public static boolean isSp-impl(long $this);
-    method public static boolean isUnspecified-impl(long $this);
     method public static inline operator long minus--R2X_6o(long $this, long other);
     method public static inline operator long plus--R2X_6o(long $this, long other);
     method public static inline operator long times-XSAIIZE(long $this, float other);
@@ -492,10 +428,13 @@
     method public static long getSp(float);
     method public static long getSp(double);
     method public static long getSp(int);
+    method public static inline boolean isSpecified--R2X_6o(long);
+    method public static boolean isUnspecified--R2X_6o(long);
     method @androidx.compose.runtime.Stable public static long lerp-KeuwX78(long a, long b, float t);
     method @androidx.compose.runtime.Stable public static inline long max-8E83U4Q(long a, long b);
     method @androidx.compose.runtime.Stable public static inline long min-8E83U4Q(long a, long b);
     method @kotlin.PublishedApi internal static inline long pack(long unitType, float v);
+    method public static inline long takeOrElse-bAewZlA(long, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.TextUnit> block);
     method @androidx.compose.runtime.Stable public static inline operator long times-0PRCd3Q(double, long other);
     method @androidx.compose.runtime.Stable public static inline operator long times-Ew26DjI(float, long other);
     method @androidx.compose.runtime.Stable public static inline operator long times-VJWtCv4(int, long other);
@@ -535,7 +474,10 @@
   }
 
   public final class UptimeKt {
+    method public static inline boolean isSpecified-fQUwLeo(long);
+    method public static inline boolean isUnspecified-fQUwLeo(long);
     method public static operator long plus-I3RPvYE(long, long uptime);
+    method public static inline long takeOrElse-N-58ELw(long, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.Uptime> block);
   }
 
   @androidx.compose.runtime.Immutable public final inline class Velocity {
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
index e2b4738..5df0371 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
@@ -476,6 +476,15 @@
  * Takes a size and returns whether it satisfies the current constraints.
  */
 @Stable
+fun Constraints.isSatisfiedBy(size: IntSize): Boolean {
+    return size.width in minWidth..maxWidth && size.height in minHeight..maxHeight
+}
+
+@Deprecated(
+    "satisfiedBy was renamed to isSatisfiedBy.",
+    ReplaceWith("isSatifiedBy(size)", "androidx.compose.ui.unit.isSatisfiedBy")
+)
+@Stable
 fun Constraints.satisfiedBy(size: IntSize): Boolean {
     return size.width in minWidth..maxWidth && size.height in minHeight..maxHeight
 }
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
index e7fecbaf..bb24b51 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
@@ -19,6 +19,7 @@
 
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
+import androidx.compose.ui.geometry.isSpecified
 import androidx.compose.ui.unit.Dp.Companion.Hairline
 import androidx.compose.ui.util.lerp
 import androidx.compose.ui.util.packFloats
@@ -82,13 +83,6 @@
     inline operator fun div(other: Dp): Float = value / other.value
 
     /**
-     * Divide by [DpSquared] to get a [DpInverse].
-     */
-    @Stable
-    inline operator fun div(other: DpSquared): DpInverse =
-        DpInverse(value = value / other.value)
-
-    /**
      * Multiply a Dp by a scalar.
      */
     @Stable
@@ -100,20 +94,6 @@
         Dp(value = value * other)
 
     /**
-     * Multiply by a Dp to get a [DpSquared] result.
-     */
-    @Stable
-    inline operator fun times(other: Dp): DpSquared =
-        DpSquared(value = value * other.value)
-
-    /**
-     * Multiply by a Dp to get a [DpSquared] result.
-     */
-    @Stable
-    inline operator fun times(other: DpSquared): DpCubed =
-        DpCubed(value = value * other.value)
-
-    /**
      * Support comparing Dimensions with comparison operators.
      */
     @Stable
@@ -145,6 +125,27 @@
 }
 
 /**
+ * `false` when this is [Dp.Unspecified].
+ */
+@Stable
+inline val Dp.isSpecified: Boolean
+    get() = !value.isNaN()
+
+/**
+ * `true` when this is [Dp.Unspecified].
+ */
+@Stable
+inline val Dp.isUnspecified: Boolean
+    get() = value.isNaN()
+
+/**
+ * If this [Dp] [isSpecified] then this is returned, otherwise [block] is executed
+ * and its result is returned.
+ */
+inline fun Dp.takeOrElse(block: () -> Dp): Dp =
+    if (isSpecified) this else block()
+
+/**
  * Create a [Dp] using an [Int]:
  *     val left = 10
  *     val x = left.dp
@@ -175,18 +176,6 @@
 inline val Float.dp: Dp get() = Dp(value = this)
 
 @Stable
-inline operator fun Float.div(other: Dp) =
-    DpInverse(this / other.value)
-
-@Stable
-inline operator fun Double.div(other: Dp) =
-    DpInverse(this.toFloat() / other.value)
-
-@Stable
-inline operator fun Int.div(other: Dp) =
-    DpInverse(this / other.value)
-
-@Stable
 inline operator fun Float.times(other: Dp) =
     Dp(this * other.value)
 
@@ -238,7 +227,7 @@
  * Return `true` when it is finite or `false` when it is [Dp.Infinity]
  */
 @Stable
-inline fun Dp.isFinite(): Boolean = value != Float.POSITIVE_INFINITY
+inline val Dp.isFinite: Boolean get() = value != Float.POSITIVE_INFINITY
 
 /**
  * Linearly interpolate between two [Dp]s.
@@ -256,226 +245,6 @@
     return Dp(lerp(start.value, stop.value, fraction))
 }
 
-/**
- * Holds a unit of squared dimensions, such as `1.value * 2.dp`. [DpSquared], [DpCubed],
- * and [DpInverse] are used primarily for [Dp] calculations to ensure resulting
- * units are as expected. Many times, [Dp] calculations use scalars to determine the final
- * dimension during calculation:
- *     val width = oldWidth * stretchAmount
- * Other times, it is useful to do intermediate calculations with Dimensions directly:
- *     val width = oldWidth * newTotalWidth / oldTotalWidth
- */
-@Suppress("EXPERIMENTAL_FEATURE_WARNING")
-@Immutable
-inline class DpSquared(val value: Float) : Comparable<DpSquared> {
-    /**
-     * Add two DimensionSquares together.
-     */
-    @Stable
-    inline operator fun plus(other: DpSquared) =
-        DpSquared(value = value + other.value)
-
-    /**
-     * Subtract a DimensionSquare from another one.
-     */
-    @Stable
-    inline operator fun minus(other: DpSquared) =
-        DpSquared(value = value - other.value)
-
-    /**
-     * Divide a DimensionSquare by a scalar.
-     */
-    @Stable
-    inline operator fun div(other: Float): DpSquared =
-        DpSquared(value = value / other)
-
-    /**
-     * Divide by a [Dp] to get a [Dp] result.
-     */
-    @Stable
-    inline operator fun div(other: Dp): Dp =
-        Dp(value = value / other.value)
-
-    /**
-     * Divide by a DpSquared to get a scalar result.
-     */
-    @Stable
-    inline operator fun div(other: DpSquared): Float = value / other.value
-
-    /**
-     * Divide by a [DpCubed] to get a [DpInverse] result.
-     */
-    @Stable
-    inline operator fun div(other: DpCubed): DpInverse =
-        DpInverse(value / other.value)
-
-    /**
-     * Multiply by a scalar to get a DpSquared result.
-     */
-    @Stable
-    inline operator fun times(other: Float): DpSquared =
-        DpSquared(value = value * other)
-
-    /**
-     * Multiply by a scalar to get a DpSquared result.
-     */
-    @Stable
-    inline operator fun times(other: Dp): DpCubed =
-        DpCubed(value = value * other.value)
-
-    /**
-     * Support comparing DpSquared with comparison operators.
-     */
-    @Stable
-    override /* TODO: inline */ operator fun compareTo(other: DpSquared) =
-        value.compareTo(other.value)
-
-    @Stable
-    override fun toString(): String = "$value.dp^2"
-}
-
-/**
- * Holds a unit of cubed dimensions, such as `1.value * 2.value * 3.dp`. [DpSquared],
- * [DpCubed], and [DpInverse] are used primarily for [Dp] calculations to
- * ensure resulting units are as expected. Many times, [Dp] calculations use scalars to
- * determine the final dimension during calculation:
- *     val width = oldWidth * stretchAmount
- * Other times, it is useful to do intermediate calculations with Dimensions directly:
- *     val width = oldWidth * newTotalWidth / oldTotalWidth
- */
-@Suppress("EXPERIMENTAL_FEATURE_WARNING")
-@Immutable
-inline class DpCubed(val value: Float) : Comparable<DpCubed> {
-
-    /**
-     * Add two DpCubed together.
-     */
-    @Stable
-    inline operator fun plus(dimension: DpCubed) =
-        DpCubed(value = value + dimension.value)
-
-    /**
-     * Subtract a DpCubed from another one.
-     */
-    @Stable
-    inline operator fun minus(dimension: DpCubed) =
-        DpCubed(value = value - dimension.value)
-
-    /**
-     * Divide a DpCubed by a scalar.
-     */
-    @Stable
-    inline operator fun div(other: Float): DpCubed =
-        DpCubed(value = value / other)
-
-    /**
-     * Divide by a [Dp] to get a [DpSquared] result.
-     */
-    @Stable
-    inline operator fun div(other: Dp): DpSquared =
-        DpSquared(value = value / other.value)
-
-    /**
-     * Divide by a [DpSquared] to get a [Dp] result.
-     */
-    @Stable
-    inline operator fun div(other: DpSquared): Dp =
-        Dp(value = value / other.value)
-
-    /**
-     * Divide by a DpCubed to get a scalar result.
-     */
-    @Stable
-    inline operator fun div(other: DpCubed): Float = value / other.value
-
-    /**
-     * Multiply by a scalar to get a DpCubed result.
-     */
-    @Stable
-    inline operator fun times(other: Float): DpCubed =
-        DpCubed(value = value * other)
-
-    /**
-     * Support comparing DpCubed with comparison operators.
-     */
-    @Stable
-    override /* TODO: inline */ operator fun compareTo(other: DpCubed) =
-        value.compareTo(other.value)
-
-    @Stable
-    override fun toString(): String = "$value.dp^3"
-}
-/**
- * Holds a unit of an inverse dimensions, such as `1.dp / (2.value * 3.dp)`. [DpSquared],
- * [DpCubed], and [DpInverse] are used primarily for [Dp] calculations to
- * ensure resulting units are as expected. Many times, [Dp] calculations use scalars to
- * determine the final dimension during calculation:
- *     val width = oldWidth * stretchAmount
- * Other times, it is useful to do intermediate calculations with Dimensions directly:
- *     val width = oldWidth * newTotalWidth / oldTotalWidth
- */
-@Suppress("EXPERIMENTAL_FEATURE_WARNING")
-@Immutable
-inline class DpInverse(val value: Float) : Comparable<DpInverse> {
-    /**
-     * Add two DpInverse together.
-     */
-    @Stable
-    inline operator fun plus(dimension: DpInverse) =
-        DpInverse(value = value + dimension.value)
-
-    /**
-     * Subtract a DpInverse from another one.
-     */
-    @Stable
-    inline operator fun minus(dimension: DpInverse) =
-        DpInverse(value = value - dimension.value)
-
-    /**
-     * Divide a DpInverse by a scalar.
-     */
-    @Stable
-    inline operator fun div(other: Float): DpInverse =
-        DpInverse(value = value / other)
-
-    /**
-     * Multiply by a scalar to get a DpInverse result.
-     */
-    @Stable
-    inline operator fun times(other: Float): DpInverse =
-        DpInverse(value = value * other)
-
-    /**
-     * Multiply by a [Dp] to get a scalar result.
-     */
-    @Stable
-    inline operator fun times(other: Dp): Float = value * other.value
-
-    /**
-     * Multiply by a [DpSquared] to get a [Dp] result.
-     */
-    @Stable
-    inline operator fun times(other: DpSquared): Dp =
-        Dp(value = value * other.value)
-
-    /**
-     * Multiply by a [DpCubed] to get a [DpSquared] result.
-     */
-    @Stable
-    inline operator fun times(other: DpCubed): DpSquared =
-        DpSquared(value = value * other.value)
-
-    /**
-     * Support comparing DpInverse with comparison operators.
-     */
-    @Stable
-    override /* TODO: inline */ operator fun compareTo(other: DpInverse) =
-        value.compareTo(other.value)
-
-    @Stable
-    override fun toString(): String = "$value.dp^-1"
-}
-
 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 // Structures using Dp
 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/TextUnit.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/TextUnit.kt
index a558db4..5e819bc 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/TextUnit.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/TextUnit.kt
@@ -19,6 +19,7 @@
 
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
+import androidx.compose.ui.geometry.isSpecified
 import androidx.compose.ui.util.lerp
 
 /**
@@ -270,11 +271,6 @@
     val isInherit get() = isUnspecified
 
     /**
-     * True if this is [TextUnit.Unspecified], otherwise false.
-     */
-    val isUnspecified get() = rawType == UNIT_TYPE_UNSPECIFIED
-
-    /**
      * True if this is a SP unit type.
      */
     val isSp get() = rawType == UNIT_TYPE_SP
@@ -291,6 +287,27 @@
 }
 
 /**
+ * `false` when this is [TextUnit.Unspecified].
+ */
+@Stable
+inline val TextUnit.isSpecified: Boolean
+    get() = !isUnspecified
+
+/**
+ * `true` when this is [TextUnit.Unspecified].
+ */
+@Stable
+val TextUnit.isUnspecified: Boolean
+    get() = rawType == UNIT_TYPE_UNSPECIFIED
+
+/**
+ * If this [TextUnit] [isSpecified] then this is returned, otherwise [block] is executed
+ * and its result is returned.
+ */
+inline fun TextUnit.takeOrElse(block: () -> TextUnit): TextUnit =
+    if (isSpecified) this else block()
+
+/**
  * Creates a SP unit [TextUnit]
  */
 @Stable
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Uptime.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Uptime.kt
index 0e5eec9..b015313 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Uptime.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Uptime.kt
@@ -17,6 +17,8 @@
 package androidx.compose.ui.unit
 
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.geometry.isSpecified
 
 /**
  * A single point in time with a time base of the system's uptime [nanoseconds]. Compare to
@@ -67,6 +69,27 @@
 }
 
 /**
+ * `false` when this is [Uptime.Unspecified].
+ */
+@Stable
+inline val Uptime.isSpecified: Boolean
+    get() = nanoseconds != Uptime.Unspecified.nanoseconds
+
+/**
+ * `true` when this is [Uptime.Unspecified].
+ */
+@Stable
+inline val Uptime.isUnspecified: Boolean
+    get() = nanoseconds == Uptime.Unspecified.nanoseconds
+
+/**
+ * If this [Uptime] [isSpecified] then this is returned, otherwise [block] is executed
+ * and its result is returned.
+ */
+inline fun Uptime.takeOrElse(block: () -> Uptime): Uptime =
+    if (isSpecified) this else block()
+
+/**
  * Add a Duration to a [Uptime] and returns the result as a [Uptime].
  */
 operator fun Duration.plus(uptime: Uptime) = Uptime(nanoseconds + uptime.nanoseconds)
\ No newline at end of file
diff --git a/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/DpTest.kt b/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/DpTest.kt
index b03ec12..519afcb 100644
--- a/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/DpTest.kt
+++ b/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/DpTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.unit
 
+import org.junit.Assert
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
@@ -82,39 +83,17 @@
     }
 
     @Test
-    fun multiplyDimension() {
-        assertEquals(DpSquared(40f), 10.dp * 4.dp)
-    }
-
-    @Test
-    fun multiplyDimensionSquared() {
-        assertEquals(DpCubed(40f), 10.dp * (2.dp * 2.dp))
-    }
-
-    @Test
     fun divideOperator() {
         assertEquals(10f, 100.dp / 10f.dp, 0f)
         assertEquals(0f, 0.dp / 10f.dp, 0f)
     }
 
     @Test
-    fun divideOperatorInverse() {
-        assertEquals(DpInverse(10f), 100f / 10.dp)
-        assertEquals(DpInverse(10f), 100.0 / 10.dp)
-        assertEquals(DpInverse(10f), 100 / 10.dp)
-    }
-
-    @Test
     fun divideToScalar() {
         assertEquals(1f, 1.dp / 1.dp, 0f)
     }
 
     @Test
-    fun divideToInverse() {
-        assertEquals(DpInverse(10f), 100.dp / (5.dp * 2.dp))
-    }
-
-    @Test
     fun hairline() {
         assertEquals(0f, Dp.Hairline.value, 0f)
     }
@@ -142,140 +121,6 @@
     }
 
     @Test
-    fun addDimension2() {
-        assertEquals(DpSquared(4f), (2.dp * 1.dp) + (1.dp * 2.dp))
-    }
-
-    @Test
-    fun subtractDimension2() {
-        assertEquals(DpSquared(0f), (2.dp * 3.dp) - (3.dp * 2.dp))
-    }
-
-    @Test
-    fun divideDimension2() {
-        assertEquals(DpSquared(1f), (2.dp * 5.dp) / 10f)
-    }
-
-    @Test
-    fun divideDimension2Dimension() {
-        assertEquals(1f, ((2.dp * 2.dp) / 4.dp).value, 0f)
-    }
-
-    @Test
-    fun divideDimension2Dimension2() {
-        assertEquals(1f, (2.dp * 2.dp) / (2.dp * 2.dp))
-    }
-
-    @Test
-    fun divideDimension2Dimension3() {
-        assertEquals(DpInverse(0.5f), (2.dp * 2.dp) / (2.dp * 2.dp * 2.dp))
-    }
-
-    @Test
-    fun multiplyDimension2() {
-        assertEquals(DpSquared(4f), (2.dp * 1.dp) * 2f)
-    }
-
-    @Test
-    fun multiplyDimension2Dimension() {
-        assertEquals(DpCubed(4f), (2.dp * 1.dp) * 2.dp)
-    }
-
-    @Test
-    fun compareDimension2() {
-        assertTrue(DpSquared(0f) < DpSquared(Float.MIN_VALUE))
-        assertTrue(DpSquared(1f) < DpSquared(3f))
-        assertTrue(DpSquared(1f) == DpSquared(1f))
-        assertTrue(DpSquared(1f) > DpSquared(0f))
-    }
-
-    @Test
-    fun addDimension3() {
-        assertEquals(DpCubed(4f), (2.dp * 1.dp * 1.dp) + (1.dp * 2.dp * 1.dp))
-    }
-
-    @Test
-    fun subtractDimension3() {
-        assertEquals(DpCubed(0f), (2.dp * 3.dp * 1.dp) - (3.dp * 2.dp * 1.dp))
-    }
-
-    @Test
-    fun divideDimension3() {
-        assertEquals(DpCubed(1f), (2.dp * 5.dp * 1.dp) / 10f)
-    }
-
-    @Test
-    fun divideDimension3Dimension() {
-        assertEquals(DpSquared(1f), (2.dp * 2.dp * 1.dp) / 4.dp)
-    }
-
-    @Test
-    fun divideDimension3Dimension2() {
-        assertEquals(1f, ((2.dp * 2.dp * 1.dp) / (2.dp * 2.dp)).value, 0f)
-    }
-
-    @Test
-    fun divideDimension3Dimension3() {
-        assertEquals(1f, (2.dp * 2.dp * 1.dp) / (2.dp * 2.dp * 1.dp))
-    }
-
-    @Test
-    fun multiplyDimension3() {
-        assertEquals(DpCubed(4f), (2.dp * 1.dp * 1.dp) * 2f)
-    }
-
-    @Test
-    fun compareDimension3() {
-        assertTrue(DpCubed(0f) < DpCubed(Float.MIN_VALUE))
-        assertTrue(DpCubed(1f) < DpCubed(3f))
-        assertTrue(DpCubed(1f) == DpCubed(1f))
-        assertTrue(DpCubed(1f) > DpCubed(0f))
-    }
-
-    @Test
-    fun addDimensionInverse() {
-        assertEquals(DpInverse(1f), 1 / 2.dp + 1 / 2.dp)
-    }
-
-    @Test
-    fun subtractDimensionInverse() {
-        assertEquals(DpInverse(0f), 1 / 2.dp - 1 / 2.dp)
-    }
-
-    @Test
-    fun divideDimensionInverse() {
-        assertEquals(DpInverse(1f), (10 / 1.dp) / 10f)
-    }
-
-    @Test
-    fun multiplyDimensionInverse() {
-        assertEquals(DpInverse(4f), (1 / 2.dp) * 8f)
-    }
-
-    @Test
-    fun multiplyDimensionInverseDimension() {
-        assertEquals(4f, (1 / 2.dp) * 8.dp)
-    }
-
-    @Test
-    fun multiplyDimensionInverseDimension2() {
-        assertEquals(4f, ((1 / 2.dp) * (8.dp * 1.dp)).value, 0f)
-    }
-
-    @Test
-    fun multiplyDimensionInverseDimension3() {
-        assertEquals(DpSquared(4f), (1 / 2.dp) * (8.dp * 1.dp * 1.dp))
-    }
-
-    @Test
-    fun compareDimensionInverse() {
-        assertTrue(DpInverse(0f) < DpInverse(Float.MIN_VALUE))
-        assertTrue(DpInverse(1f) < DpInverse(3f))
-        assertTrue(DpInverse(1f) == DpInverse(1f))
-        assertTrue(DpInverse(1f) > DpInverse(0f))
-    }
-
-    @Test
     fun minTest() {
         assertEquals(10f, min(10.dp, 20.dp).value, 0f)
         assertEquals(10f, min(20.dp, 10.dp).value, 0f)
@@ -380,4 +225,26 @@
         assertEquals(19.dp, copy.x)
         assertEquals(67.dp, copy.y)
     }
+
+    @Test
+    fun testIsSpecified() {
+        Assert.assertFalse(Dp.Unspecified.isSpecified)
+        assertTrue(Dp(1f).isSpecified)
+    }
+
+    @Test
+    fun testIsUnspecified() {
+        assertTrue(Dp.Unspecified.isUnspecified)
+        Assert.assertFalse(Dp(1f).isUnspecified)
+    }
+
+    @Test
+    fun testTakeOrElseTrue() {
+        assertTrue(Dp(1f).takeOrElse { Dp.Unspecified }.isSpecified)
+    }
+
+    @Test
+    fun testTakeOrElseFalse() {
+        assertTrue(Dp.Unspecified.takeOrElse { Dp(1f) }.isSpecified)
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/TextUnitTest.kt b/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/TextUnitTest.kt
index e3ec3b3..602bcfb 100644
--- a/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/TextUnitTest.kt
+++ b/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/TextUnitTest.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.unit
 
 import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -1065,4 +1066,26 @@
         assertThat(1.em.isUnspecified).isFalse()
         assertThat(1.em.isInherit).isFalse()
     }
+
+    @Test
+    fun testIsSpecified() {
+        Assert.assertFalse(TextUnit.Unspecified.isSpecified)
+        Assert.assertTrue(1.sp.isSpecified)
+    }
+
+    @Test
+    fun testIsUnspecified() {
+        Assert.assertTrue(TextUnit.Unspecified.isUnspecified)
+        Assert.assertFalse(1.sp.isUnspecified)
+    }
+
+    @Test
+    fun testTakeOrElseTrue() {
+        Assert.assertTrue(1.sp.takeOrElse { TextUnit.Unspecified }.isSpecified)
+    }
+
+    @Test
+    fun testTakeOrElseFalse() {
+        Assert.assertTrue(TextUnit.Unspecified.takeOrElse { 1.sp }.isSpecified)
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/UptimeTest.kt b/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/UptimeTest.kt
index 4f38dc4..14493a0 100644
--- a/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/UptimeTest.kt
+++ b/compose/ui/ui-unit/src/test/kotlin/androidx/compose/ui/unit/UptimeTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.unit
 
+import org.junit.Assert
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Test
@@ -64,4 +65,26 @@
     fun minus_uptime_isCorrect() {
         assertEquals(Duration(3), Uptime(5) - Uptime(2))
     }
+
+    @Test
+    fun testIsSpecified() {
+        Assert.assertFalse(Uptime.Unspecified.isSpecified)
+        assertTrue(Uptime(1).isSpecified)
+    }
+
+    @Test
+    fun testIsUnspecified() {
+        assertTrue(Uptime.Unspecified.isUnspecified)
+        Assert.assertFalse(Uptime(1).isUnspecified)
+    }
+
+    @Test
+    fun testTakeOrElseTrue() {
+        assertTrue(Uptime(1).takeOrElse { Uptime.Unspecified }.isSpecified)
+    }
+
+    @Test
+    fun testTakeOrElseFalse() {
+        assertTrue(Uptime.Unspecified.takeOrElse { Uptime(1) }.isSpecified)
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-util/api/current.txt b/compose/ui/ui-util/api/current.txt
index 8f93c6c..49cf86d 100644
--- a/compose/ui/ui-util/api/current.txt
+++ b/compose/ui/ui-util/api/current.txt
@@ -23,19 +23,9 @@
     method public static int findPrecedingBreak(String, int index);
   }
 
-  public final class JvmMathHelpersKt {
-    method public static String toStringAsFixed(float, int digits);
-  }
-
   public final class JvmMiscHelpersKt {
     method public static StringBuilder deleteAt(StringBuilder, int index);
     method public static String format(String, java.lang.Object?... args);
-    method public static int identityHashCode(Object?);
-    method public static Object nativeClass(Object);
-  }
-
-  public final class JvmSynchronizationHelperKt {
-    method public static <T> T! synchronized(Object lock, kotlin.jvm.functions.Function0<? extends T> block);
   }
 
   public final class ListUtilsKt {
@@ -54,9 +44,6 @@
     method public static float lerp(float start, float stop, float fraction);
     method public static int lerp(int start, int stop, float fraction);
     method public static long lerp(long start, long stop, float fraction);
-    method public static String toHexString(int);
-    method public static float toRadians(float);
-    method public static double toRadians(double);
   }
 
 }
diff --git a/compose/ui/ui-util/api/public_plus_experimental_current.txt b/compose/ui/ui-util/api/public_plus_experimental_current.txt
index 8f93c6c..49cf86d 100644
--- a/compose/ui/ui-util/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-util/api/public_plus_experimental_current.txt
@@ -23,19 +23,9 @@
     method public static int findPrecedingBreak(String, int index);
   }
 
-  public final class JvmMathHelpersKt {
-    method public static String toStringAsFixed(float, int digits);
-  }
-
   public final class JvmMiscHelpersKt {
     method public static StringBuilder deleteAt(StringBuilder, int index);
     method public static String format(String, java.lang.Object?... args);
-    method public static int identityHashCode(Object?);
-    method public static Object nativeClass(Object);
-  }
-
-  public final class JvmSynchronizationHelperKt {
-    method public static <T> T! synchronized(Object lock, kotlin.jvm.functions.Function0<? extends T> block);
   }
 
   public final class ListUtilsKt {
@@ -54,9 +44,6 @@
     method public static float lerp(float start, float stop, float fraction);
     method public static int lerp(int start, int stop, float fraction);
     method public static long lerp(long start, long stop, float fraction);
-    method public static String toHexString(int);
-    method public static float toRadians(float);
-    method public static double toRadians(double);
   }
 
 }
diff --git a/compose/ui/ui-util/api/restricted_current.txt b/compose/ui/ui-util/api/restricted_current.txt
index 8f93c6c..49cf86d 100644
--- a/compose/ui/ui-util/api/restricted_current.txt
+++ b/compose/ui/ui-util/api/restricted_current.txt
@@ -23,19 +23,9 @@
     method public static int findPrecedingBreak(String, int index);
   }
 
-  public final class JvmMathHelpersKt {
-    method public static String toStringAsFixed(float, int digits);
-  }
-
   public final class JvmMiscHelpersKt {
     method public static StringBuilder deleteAt(StringBuilder, int index);
     method public static String format(String, java.lang.Object?... args);
-    method public static int identityHashCode(Object?);
-    method public static Object nativeClass(Object);
-  }
-
-  public final class JvmSynchronizationHelperKt {
-    method public static <T> T! synchronized(Object lock, kotlin.jvm.functions.Function0<? extends T> block);
   }
 
   public final class ListUtilsKt {
@@ -54,9 +44,6 @@
     method public static float lerp(float start, float stop, float fraction);
     method public static int lerp(int start, int stop, float fraction);
     method public static long lerp(long start, long stop, float fraction);
-    method public static String toHexString(int);
-    method public static float toRadians(float);
-    method public static double toRadians(double);
   }
 
 }
diff --git a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MathHelpers.kt b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MathHelpers.kt
index 7650fd4..ebf47195 100644
--- a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MathHelpers.kt
+++ b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MathHelpers.kt
@@ -15,7 +15,6 @@
  */
 package androidx.compose.ui.util
 
-import kotlin.math.PI
 import kotlin.math.roundToInt
 import kotlin.math.roundToLong
 
@@ -39,11 +38,3 @@
 fun lerp(start: Long, stop: Long, fraction: Float): Long {
     return start + ((stop - start) * fraction.toDouble()).roundToLong()
 }
-
-expect fun Float.toStringAsFixed(digits: Int): String
-
-@OptIn(kotlin.ExperimentalUnsignedTypes::class)
-fun Int.toHexString() = "0x${toUInt().toString(16).padStart(8, '0')}"
-
-fun Float.toRadians(): Float = this / 180f * PI.toFloat()
-fun Double.toRadians(): Double = this / 180 * PI
diff --git a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MiscHelpers.kt b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MiscHelpers.kt
index 4dc6dc2..b24fe06 100644
--- a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MiscHelpers.kt
+++ b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MiscHelpers.kt
@@ -16,19 +16,6 @@
 
 package androidx.compose.ui.util
 
-expect fun Any?.identityHashCode(): Int
-
 expect fun String.format(vararg args: Any?): String
 
 expect fun StringBuilder.deleteAt(index: Int): StringBuilder
-
-// For performance optimizations of type.
-expect fun Any.nativeClass(): Any
-
-expect class TreeSet<E>(comparator: Comparator<in E>) {
-    fun add(element: E): Boolean
-    fun remove(element: E): Boolean
-    fun first(): E
-    fun contains(element: E): Boolean
-    fun isEmpty(): Boolean
-}
diff --git a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMiscHelpers.kt b/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMiscHelpers.kt
index d3858d4..be14a1b 100644
--- a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMiscHelpers.kt
+++ b/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMiscHelpers.kt
@@ -16,15 +16,9 @@
 
 package androidx.compose.ui.util
 
-actual fun Any?.identityHashCode(): Int = if (this == null) 0 else System.identityHashCode(this)
-
 actual fun String.format(vararg args: Any?): String = java.lang.String.format(this, *args)
 
 actual fun StringBuilder.deleteAt(index: Int): StringBuilder {
     this.deleteCharAt(index)
     return this
 }
-
-actual fun Any.nativeClass(): Any = this.javaClass
-
-actual typealias TreeSet<T> = java.util.TreeSet<T>
diff --git a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmSynchronizationHelper.kt b/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmSynchronizationHelper.kt
deleted file mode 100644
index f94d1ec..0000000
--- a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmSynchronizationHelper.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.util
-
-import kotlin.contracts.ExperimentalContracts
-import kotlin.contracts.InvocationKind
-import kotlin.contracts.contract
-
-/**
- * [kotlin.synchronized][synchronized] is deprecated, and the build fails if we use
- * [kotlin.synchronized][synchronized] along with the IR compiler. As a workaround, we have this
- * function here, which is in a module that doesn't use the IR Compiler.
- */
-@OptIn(ExperimentalContracts::class)
-fun <T> synchronized(lock: Any, block: () -> T): T {
-    contract {
-        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
-    }
-    return kotlin.synchronized(lock, block)
-}
\ No newline at end of file
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index e4118d8..b64837d 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -132,26 +132,29 @@
     method @Deprecated public static androidx.compose.ui.Modifier drawWithContent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.ContentDrawScope,kotlin.Unit> onDraw);
   }
 
-  public final class FocusModifierKt {
-    method @androidx.compose.ui.focus.ExperimentalFocus public static androidx.compose.ui.Modifier focus(androidx.compose.ui.Modifier);
+  @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") public @interface ExperimentalComposeUiApi {
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public interface FocusObserverModifier extends androidx.compose.ui.Modifier.Element {
-    method public kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusState,kotlin.Unit> getOnFocusChange();
+  public final class FocusModifierKt {
+    method @Deprecated public static androidx.compose.ui.Modifier focus(androidx.compose.ui.Modifier);
+  }
+
+  @Deprecated public interface FocusObserverModifier extends androidx.compose.ui.Modifier.Element {
+    method @Deprecated public kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusState,kotlin.Unit> getOnFocusChange();
     property public abstract kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChange;
   }
 
   public final class FocusObserverModifierKt {
-    method @androidx.compose.ui.focus.ExperimentalFocus public static androidx.compose.ui.Modifier focusObserver(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChange);
+    method @Deprecated public static androidx.compose.ui.Modifier focusObserver(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChange);
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public interface FocusRequesterModifier extends androidx.compose.ui.Modifier.Element {
-    method public androidx.compose.ui.focus.FocusRequester getFocusRequester();
-    property public abstract androidx.compose.ui.focus.FocusRequester focusRequester;
+  @Deprecated public interface FocusRequesterModifier extends androidx.compose.ui.Modifier.Element {
+    method @Deprecated public androidx.compose.ui.focus.FocusReference getFocusReference();
+    property public abstract androidx.compose.ui.focus.FocusReference focusReference;
   }
 
   public final class FocusRequesterModifierKt {
-    method @androidx.compose.ui.focus.ExperimentalFocus public static androidx.compose.ui.Modifier focusRequester(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester);
+    method @Deprecated public static androidx.compose.ui.Modifier focusRequester(androidx.compose.ui.Modifier, Object focusRequester);
   }
 
   @androidx.compose.runtime.Stable public interface Modifier {
@@ -194,17 +197,17 @@
   public final class AndroidAutofillTypeKt {
   }
 
-  public interface Autofill {
+  @androidx.compose.ui.ExperimentalComposeUiApi public interface Autofill {
     method public void cancelAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
     method public void requestAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
   }
 
-  public final class AutofillNode {
+  @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillNode {
     ctor public AutofillNode(java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
     method public java.util.List<androidx.compose.ui.autofill.AutofillType> component1();
     method public androidx.compose.ui.geometry.Rect? component2();
     method public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? component3();
-    method public androidx.compose.ui.autofill.AutofillNode copy(java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
+    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.autofill.AutofillNode copy(java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
     method public java.util.List<androidx.compose.ui.autofill.AutofillType> getAutofillTypes();
     method public androidx.compose.ui.geometry.Rect? getBoundingBox();
     method public int getId();
@@ -216,7 +219,7 @@
     property public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? onFill;
   }
 
-  public final class AutofillTree {
+  @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillTree {
     ctor public AutofillTree();
     method public java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> getChildren();
     method public kotlin.Unit? performAutofill(int id, String value);
@@ -224,7 +227,7 @@
     property public final java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> children;
   }
 
-  public enum AutofillType {
+  @androidx.compose.ui.ExperimentalComposeUiApi public enum AutofillType {
     enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressAuxiliaryDetails;
     enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressCountry;
     enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressLocality;
@@ -328,27 +331,81 @@
 
 package androidx.compose.ui.focus {
 
-  @kotlin.RequiresOptIn(message="The Focus API is experimental and is likely to change in the future.") public @interface ExperimentalFocus {
+  public final class FocusChangedModifierKt {
+    method public static androidx.compose.ui.Modifier onFocusChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChanged);
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public interface FocusManager {
+  public interface FocusEventModifier extends androidx.compose.ui.Modifier.Element {
+    method public void onFocusEvent(androidx.compose.ui.focus.FocusState focusState);
+  }
+
+  public final class FocusEventModifierKt {
+    method public static androidx.compose.ui.Modifier onFocusEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusEvent);
+  }
+
+  public interface FocusManager {
     method public void clearFocus(optional boolean forcedClear);
   }
 
+  public final class FocusModifierKt {
+    method public static androidx.compose.ui.Modifier focusModifier(androidx.compose.ui.Modifier);
+  }
+
   public final class FocusNodeUtilsKt {
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public final class FocusRequester {
-    ctor public FocusRequester();
+  public final class FocusReference {
+    ctor public FocusReference();
     method public boolean captureFocus();
     method public boolean freeFocus();
     method public void requestFocus();
+    field public static final androidx.compose.ui.focus.FocusReference.Companion Companion;
   }
 
-  public final class FocusRequesterKt {
+  public static final class FocusReference.Companion {
+    method public androidx.compose.ui.focus.FocusReference.Companion.FocusReferenceFactory createRefs();
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public enum FocusState {
+  public static final class FocusReference.Companion.FocusReferenceFactory {
+    method public operator androidx.compose.ui.focus.FocusReference component1();
+    method public operator androidx.compose.ui.focus.FocusReference component10();
+    method public operator androidx.compose.ui.focus.FocusReference component11();
+    method public operator androidx.compose.ui.focus.FocusReference component12();
+    method public operator androidx.compose.ui.focus.FocusReference component13();
+    method public operator androidx.compose.ui.focus.FocusReference component14();
+    method public operator androidx.compose.ui.focus.FocusReference component15();
+    method public operator androidx.compose.ui.focus.FocusReference component16();
+    method public operator androidx.compose.ui.focus.FocusReference component2();
+    method public operator androidx.compose.ui.focus.FocusReference component3();
+    method public operator androidx.compose.ui.focus.FocusReference component4();
+    method public operator androidx.compose.ui.focus.FocusReference component5();
+    method public operator androidx.compose.ui.focus.FocusReference component6();
+    method public operator androidx.compose.ui.focus.FocusReference component7();
+    method public operator androidx.compose.ui.focus.FocusReference component8();
+    method public operator androidx.compose.ui.focus.FocusReference component9();
+    field public static final androidx.compose.ui.focus.FocusReference.Companion.FocusReferenceFactory INSTANCE;
+  }
+
+  public final class FocusReferenceKt {
+  }
+
+  public interface FocusReferenceModifier extends androidx.compose.ui.Modifier.Element {
+    method public androidx.compose.ui.focus.FocusReference getFocusReference();
+    property public abstract androidx.compose.ui.focus.FocusReference focusReference;
+  }
+
+  public final class FocusReferenceModifierKt {
+    method public static androidx.compose.ui.Modifier focusReference(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusReference focusReference);
+  }
+
+  @Deprecated public final class FocusRequester {
+    ctor @Deprecated public FocusRequester();
+    method @Deprecated public boolean captureFocus();
+    method @Deprecated public boolean freeFocus();
+    method @Deprecated public void requestFocus();
+  }
+
+  public enum FocusState {
     enum_constant public static final androidx.compose.ui.focus.FocusState Active;
     enum_constant public static final androidx.compose.ui.focus.FocusState ActiveParent;
     enum_constant public static final androidx.compose.ui.focus.FocusState Captured;
@@ -410,9 +467,6 @@
     method public static androidx.compose.ui.Modifier dragSlopExceededGestureFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onDragSlopExceeded, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.gesture.Direction,java.lang.Boolean>? canDrag, optional androidx.compose.ui.gesture.scrollorientationlocking.Orientation? orientation);
   }
 
-  @kotlin.RequiresOptIn(message="This pointer input API is experimental and is likely to change before becoming " + "stable.") public @interface ExperimentalPointerInput {
-  }
-
   public final class GestureUtilsKt {
     method public static boolean anyPointersInBounds-5eFHUEc(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange>, long bounds);
   }
@@ -493,11 +547,11 @@
 
 package androidx.compose.ui.gesture.customevents {
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public final class DelayUpEvent implements androidx.compose.ui.input.pointer.CustomEvent {
+  public final class DelayUpEvent implements androidx.compose.ui.input.pointer.CustomEvent {
     ctor public DelayUpEvent(androidx.compose.ui.gesture.customevents.DelayUpMessage message, java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers);
     method public androidx.compose.ui.gesture.customevents.DelayUpMessage component1();
     method public java.util.Set<androidx.compose.ui.input.pointer.PointerId> component2();
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public androidx.compose.ui.gesture.customevents.DelayUpEvent copy(androidx.compose.ui.gesture.customevents.DelayUpMessage message, java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers);
+    method public androidx.compose.ui.gesture.customevents.DelayUpEvent copy(androidx.compose.ui.gesture.customevents.DelayUpMessage message, java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers);
     method public androidx.compose.ui.gesture.customevents.DelayUpMessage getMessage();
     method public java.util.Set<androidx.compose.ui.input.pointer.PointerId> getPointers();
     method public void setMessage(androidx.compose.ui.gesture.customevents.DelayUpMessage p);
@@ -505,7 +559,7 @@
     property public final java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers;
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public enum DelayUpMessage {
+  public enum DelayUpMessage {
     enum_constant public static final androidx.compose.ui.gesture.customevents.DelayUpMessage DelayUp;
     enum_constant public static final androidx.compose.ui.gesture.customevents.DelayUpMessage DelayedUpConsumed;
     enum_constant public static final androidx.compose.ui.gesture.customevents.DelayUpMessage DelayedUpNotConsumed;
@@ -517,6 +571,37 @@
 
 }
 
+package androidx.compose.ui.gesture.nestedscroll {
+
+  public interface NestedScrollConnection {
+    method public default void onPostFling-Pv53iXo(long consumed, long available, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Velocity,kotlin.Unit> onFinished);
+    method public default long onPostScroll-l-UAZDg(long consumed, long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+    method public default long onPreFling-TH1AsA0(long available);
+    method public default long onPreScroll-vG6bCaM(long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+  }
+
+  public final class NestedScrollDelegatingWrapperKt {
+  }
+
+  public final class NestedScrollDispatcher {
+    ctor public NestedScrollDispatcher();
+    method public void dispatchPostFling-uYzo7IE(long consumed, long available);
+    method public long dispatchPostScroll-l-UAZDg(long consumed, long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+    method public long dispatchPreFling-TH1AsA0(long available);
+    method public long dispatchPreScroll-vG6bCaM(long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+  }
+
+  public final class NestedScrollModifierKt {
+    method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.gesture.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.gesture.nestedscroll.NestedScrollDispatcher? dispatcher);
+  }
+
+  public enum NestedScrollSource {
+    enum_constant public static final androidx.compose.ui.gesture.nestedscroll.NestedScrollSource Drag;
+    enum_constant public static final androidx.compose.ui.gesture.nestedscroll.NestedScrollSource Fling;
+  }
+
+}
+
 package androidx.compose.ui.gesture.scrollorientationlocking {
 
   public enum Orientation {
@@ -524,7 +609,7 @@
     enum_constant public static final androidx.compose.ui.gesture.scrollorientationlocking.Orientation Vertical;
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public final class ScrollOrientationLocker {
+  public final class ScrollOrientationLocker {
     ctor public ScrollOrientationLocker(androidx.compose.ui.input.pointer.CustomEventDispatcher customEventDispatcher);
     method public void attemptToLockPointers(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> changes, androidx.compose.ui.gesture.scrollorientationlocking.Orientation orientation);
     method public java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> getPointersFor(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> changes, androidx.compose.ui.gesture.scrollorientationlocking.Orientation orientation);
@@ -812,18 +897,6 @@
 
 package androidx.compose.ui.input.key {
 
-  @Deprecated @androidx.compose.ui.input.key.ExperimentalKeyInput public interface Alt {
-    method @Deprecated public boolean isLeftAltPressed();
-    method @Deprecated public default boolean isPressed();
-    method @Deprecated public boolean isRightAltPressed();
-    property public abstract boolean isLeftAltPressed;
-    property public default boolean isPressed;
-    property public abstract boolean isRightAltPressed;
-  }
-
-  @kotlin.RequiresOptIn(message="The Key Input API is experimental and is likely to change in the future.") public @interface ExperimentalKeyInput {
-  }
-
   public final inline class Key {
     ctor public Key();
     method public static int constructor-impl(int keyCode);
@@ -1417,8 +1490,7 @@
     property public final int ZoomOut;
   }
 
-  @androidx.compose.ui.input.key.ExperimentalKeyInput public interface KeyEvent {
-    method @Deprecated public androidx.compose.ui.input.key.Alt getAlt();
+  public interface KeyEvent {
     method public int getKey-EK5gGoQ();
     method public androidx.compose.ui.input.key.KeyEventType getType();
     method public int getUtf16CodePoint();
@@ -1426,7 +1498,6 @@
     method public boolean isCtrlPressed();
     method public boolean isMetaPressed();
     method public boolean isShiftPressed();
-    property @Deprecated public abstract androidx.compose.ui.input.key.Alt alt;
     property public abstract boolean isAltPressed;
     property public abstract boolean isCtrlPressed;
     property public abstract boolean isMetaPressed;
@@ -1436,15 +1507,17 @@
     property public abstract int utf16CodePoint;
   }
 
-  @androidx.compose.ui.input.key.ExperimentalKeyInput public enum KeyEventType {
+  public enum KeyEventType {
     enum_constant public static final androidx.compose.ui.input.key.KeyEventType KeyDown;
     enum_constant public static final androidx.compose.ui.input.key.KeyEventType KeyUp;
     enum_constant public static final androidx.compose.ui.input.key.KeyEventType Unknown;
   }
 
   public final class KeyInputModifierKt {
-    method @androidx.compose.ui.input.key.ExperimentalKeyInput public static androidx.compose.ui.Modifier keyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onKeyEvent);
-    method @androidx.compose.ui.input.key.ExperimentalKeyInput public static androidx.compose.ui.Modifier previewKeyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreviewKeyEvent);
+    method @Deprecated public static androidx.compose.ui.Modifier keyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onKeyEvent);
+    method public static androidx.compose.ui.Modifier onKeyEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onKeyEvent);
+    method public static androidx.compose.ui.Modifier onPreviewKeyEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreviewKeyEvent);
+    method @Deprecated public static androidx.compose.ui.Modifier previewKeyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreviewKeyEvent);
   }
 
 }
@@ -1455,6 +1528,16 @@
     method public static boolean isMouseInput();
   }
 
+  @kotlin.coroutines.RestrictsSuspension public interface AwaitPointerEventScope extends androidx.compose.ui.unit.Density {
+    method public suspend Object? awaitPointerEvent(optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerEvent> p);
+    method public androidx.compose.ui.input.pointer.PointerEvent getCurrentEvent();
+    method public long getSize-YbymL2g();
+    method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
+    property public abstract androidx.compose.ui.input.pointer.PointerEvent currentEvent;
+    property public abstract long size;
+    property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
+  }
+
   public final class ConsumedData {
     method public boolean getDownChange();
     method public long getPositionChange-F1C5BW0();
@@ -1473,17 +1556,6 @@
     method public void retainHitPaths(java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointerIds);
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput @kotlin.coroutines.RestrictsSuspension public interface HandlePointerInputScope extends androidx.compose.ui.unit.Density {
-    method public suspend Object? awaitCustomEvent(optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.CustomEvent> p);
-    method public suspend Object? awaitPointerEvent(optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerEvent> p);
-    method public androidx.compose.ui.input.pointer.PointerEvent getCurrentEvent();
-    method public long getSize-YbymL2g();
-    method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
-    property public abstract androidx.compose.ui.input.pointer.PointerEvent currentEvent;
-    property public abstract long size;
-    property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
-  }
-
   public final class HitPathTrackerKt {
   }
 
@@ -1601,12 +1673,11 @@
     property public abstract androidx.compose.ui.input.pointer.PointerInputFilter pointerInputFilter;
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public interface PointerInputScope extends androidx.compose.ui.unit.Density {
-    method public androidx.compose.ui.input.pointer.CustomEventDispatcher getCustomEventDispatcher();
+  public interface PointerInputScope extends androidx.compose.ui.unit.Density {
+    method public suspend <R> Object? awaitPointerEventScope(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R> p);
     method public long getSize-YbymL2g();
     method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
-    method public suspend <R> Object? handlePointerInput(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.HandlePointerInputScope,? super kotlin.coroutines.Continuation<? super R>,?> handler, kotlin.coroutines.Continuation<? super R> p);
-    property public abstract androidx.compose.ui.input.pointer.CustomEventDispatcher customEventDispatcher;
+    method @Deprecated public default suspend <R> Object? handlePointerInput(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super R>,?> handler, kotlin.coroutines.Continuation<? super R> p);
     property public abstract long size;
     property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
   }
@@ -1627,7 +1698,7 @@
   }
 
   public final class SuspendingPointerInputFilterKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
   }
 
 }
@@ -1740,6 +1811,22 @@
     property public abstract Object layoutId;
   }
 
+  public interface LayoutInfo {
+    method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
+    method public int getHeight();
+    method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
+    method public androidx.compose.ui.layout.LayoutInfo? getParentInfo();
+    method public int getWidth();
+    method public boolean isAttached();
+    method public boolean isPlaced();
+    property public abstract androidx.compose.ui.layout.LayoutCoordinates coordinates;
+    property public abstract int height;
+    property public abstract boolean isAttached;
+    property public abstract boolean isPlaced;
+    property public abstract androidx.compose.ui.layout.LayoutInfo? parentInfo;
+    property public abstract int width;
+  }
+
   public final class LayoutKt {
     method @androidx.compose.runtime.Composable public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> minIntrinsicWidthMeasureBlock, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> minIntrinsicHeightMeasureBlock, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> maxIntrinsicWidthMeasureBlock, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> maxIntrinsicHeightMeasureBlock, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.MeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.Measurable>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
     method @androidx.compose.runtime.Composable public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> content, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.MeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.Measurable>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
@@ -1795,6 +1882,16 @@
     method public static inline String! toString-impl(androidx.compose.ui.layout.Placeable! p);
   }
 
+  public final class ModifierInfo {
+    ctor public ModifierInfo(androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.LayoutCoordinates coordinates, Object? extra);
+    method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
+    method public Object? getExtra();
+    method public androidx.compose.ui.Modifier getModifier();
+    property public final androidx.compose.ui.layout.LayoutCoordinates coordinates;
+    property public final Object? extra;
+    property public final androidx.compose.ui.Modifier modifier;
+  }
+
   public interface OnGloballyPositionedModifier extends androidx.compose.ui.Modifier.Element {
     method public void onGloballyPositioned(androidx.compose.ui.layout.LayoutCoordinates coordinates);
   }
@@ -1887,7 +1984,10 @@
   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 public static inline boolean isSpecified-FK8aYYs(long);
+    method public static inline boolean isUnspecified-FK8aYYs(long);
     method @androidx.compose.runtime.Stable public static long lerp-bKVCie4(long start, long stop, float fraction);
+    method public static inline long takeOrElse-L-byAFk(long, kotlin.jvm.functions.Function0<androidx.compose.ui.layout.ScaleFactor> block);
     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);
   }
@@ -1900,6 +2000,9 @@
     method public java.util.List<androidx.compose.ui.layout.Measurable> subcompose(Object? slotId, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class TestModifierUpdaterKt {
+  }
+
   public final class VerticalAlignmentLine extends androidx.compose.ui.layout.AlignmentLine {
     ctor public VerticalAlignmentLine(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,java.lang.Integer> merger);
   }
@@ -1924,25 +2027,15 @@
   @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalCoreApi {
   }
 
-  public final class LayoutNode implements androidx.compose.ui.layout.Measurable androidx.compose.ui.node.OwnerScope androidx.compose.ui.layout.Remeasurement {
-    ctor public LayoutNode();
-    method public void attach(androidx.compose.ui.node.Owner owner);
-    method public void detach();
+  public final class LayoutNode implements androidx.compose.ui.layout.LayoutInfo androidx.compose.ui.layout.Measurable androidx.compose.ui.node.OwnerScope androidx.compose.ui.layout.Remeasurement {
     method public void forceRemeasure();
-    method @Deprecated public boolean getCanMultiMeasure();
-    method public java.util.List<androidx.compose.ui.node.LayoutNode> getChildren();
     method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
-    method public androidx.compose.ui.unit.Density getDensity();
-    method public int getDepth();
     method public int getHeight();
-    method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
-    method public androidx.compose.ui.node.MeasureBlocks getMeasureBlocks();
-    method public androidx.compose.ui.Modifier getModifier();
-    method public java.util.List<androidx.compose.ui.node.ModifierInfo> getModifierInfo();
-    method public androidx.compose.ui.node.Owner? getOwner();
-    method public androidx.compose.ui.node.LayoutNode? getParent();
+    method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
     method public Object? getParentData();
+    method public androidx.compose.ui.layout.LayoutInfo? getParentInfo();
     method public int getWidth();
+    method public boolean isAttached();
     method public boolean isPlaced();
     method public boolean isValid();
     method public int maxIntrinsicHeight(int width);
@@ -1950,28 +2043,14 @@
     method public androidx.compose.ui.layout.Placeable measure-BRTryo0(long constraints);
     method public int minIntrinsicHeight(int width);
     method public int minIntrinsicWidth(int height);
-    method public void place(int x, int y);
-    method @Deprecated public void setCanMultiMeasure(boolean p);
-    method public void setDensity(androidx.compose.ui.unit.Density p);
-    method public void setDepth(int p);
-    method public void setLayoutDirection(androidx.compose.ui.unit.LayoutDirection value);
-    method public void setMeasureBlocks(androidx.compose.ui.node.MeasureBlocks value);
-    method public void setModifier(androidx.compose.ui.Modifier value);
-    property @Deprecated public final boolean canMultiMeasure;
-    property public final java.util.List<androidx.compose.ui.node.LayoutNode> children;
-    property public final androidx.compose.ui.layout.LayoutCoordinates coordinates;
-    property public final androidx.compose.ui.unit.Density density;
-    property public final int depth;
-    property public final int height;
-    property public final boolean isPlaced;
+    property public androidx.compose.ui.layout.LayoutCoordinates coordinates;
+    property public int height;
+    property public boolean isAttached;
+    property public boolean isPlaced;
     property public boolean isValid;
-    property public final androidx.compose.ui.unit.LayoutDirection layoutDirection;
-    property public final androidx.compose.ui.node.MeasureBlocks measureBlocks;
-    property public final androidx.compose.ui.Modifier modifier;
-    property public final androidx.compose.ui.node.Owner? owner;
-    property public final androidx.compose.ui.node.LayoutNode? parent;
     property public Object? parentData;
-    property public final int width;
+    property public androidx.compose.ui.layout.LayoutInfo? parentInfo;
+    property public int width;
   }
 
   public final class LayoutNodeKt {
@@ -1985,16 +2064,6 @@
     method public int minIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope intrinsicMeasureScope, java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable> measurables, int h);
   }
 
-  public final class ModifierInfo {
-    ctor public ModifierInfo(androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.LayoutCoordinates coordinates, Object? extra);
-    method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
-    method public Object? getExtra();
-    method public androidx.compose.ui.Modifier getModifier();
-    property public final androidx.compose.ui.layout.LayoutCoordinates coordinates;
-    property public final Object? extra;
-    property public final androidx.compose.ui.Modifier modifier;
-  }
-
   public interface OwnedLayer {
     method public void destroy();
     method public void drawLayer(androidx.compose.ui.graphics.Canvas canvas);
@@ -2018,7 +2087,6 @@
     method public androidx.compose.ui.focus.FocusManager getFocusManager();
     method public androidx.compose.ui.text.font.Font.ResourceLoader getFontLoader();
     method public androidx.compose.ui.hapticfeedback.HapticFeedback getHapticFeedBack();
-    method public boolean getHasPendingMeasureOrLayout();
     method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
     method public long getMeasureIteration();
     method public androidx.compose.ui.node.LayoutNode getRoot();
@@ -2032,11 +2100,12 @@
     method public void measureAndLayout();
     method public void onAttach(androidx.compose.ui.node.LayoutNode node);
     method public void onDetach(androidx.compose.ui.node.LayoutNode node);
+    method public void onLayoutChange(androidx.compose.ui.node.LayoutNode layoutNode);
     method public void onRequestMeasure(androidx.compose.ui.node.LayoutNode layoutNode);
     method public void onRequestRelayout(androidx.compose.ui.node.LayoutNode layoutNode);
     method public void onSemanticsChange();
     method public boolean requestFocus();
-    method @androidx.compose.ui.input.key.ExperimentalKeyInput public boolean sendKeyEvent(androidx.compose.ui.input.key.KeyEvent keyEvent);
+    method public boolean sendKeyEvent(androidx.compose.ui.input.key.KeyEvent keyEvent);
     property public abstract androidx.compose.ui.autofill.Autofill? autofill;
     property public abstract androidx.compose.ui.autofill.AutofillTree autofillTree;
     property public abstract androidx.compose.ui.platform.ClipboardManager clipboardManager;
@@ -2044,7 +2113,6 @@
     property public abstract androidx.compose.ui.focus.FocusManager focusManager;
     property public abstract androidx.compose.ui.text.font.Font.ResourceLoader fontLoader;
     property public abstract androidx.compose.ui.hapticfeedback.HapticFeedback hapticFeedBack;
-    property public abstract boolean hasPendingMeasureOrLayout;
     property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
     property public abstract long measureIteration;
     property public abstract androidx.compose.ui.node.LayoutNode root;
@@ -2103,10 +2171,14 @@
     method @androidx.compose.runtime.Composable public abstract void Content();
     method public final void createComposition();
     method public final void disposeComposition();
-    method public final boolean isDisposed();
+    method public final boolean getHasComposition();
+    method protected boolean getShouldCreateCompositionOnAttachedToWindow();
     method protected final void onLayout(boolean changed, int left, int top, int right, int bottom);
     method protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec);
-    property public final boolean isDisposed;
+    method public final void setParentCompositionReference(androidx.compose.runtime.CompositionReference? parent);
+    method public final void setViewCompositionStrategy(androidx.compose.ui.platform.ViewCompositionStrategy strategy);
+    property public final boolean hasComposition;
+    property protected boolean shouldCreateCompositionOnAttachedToWindow;
   }
 
   public final class AmbientsKt {
@@ -2124,8 +2196,6 @@
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.platform.ViewConfiguration> getAmbientViewConfiguration();
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.platform.WindowManager> getAmbientWindowManager();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.animation.core.AnimationClockObservable>! getAnimationClockAmbient();
-    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.autofill.Autofill>! getAutofillAmbient();
-    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.autofill.AutofillTree>! getAutofillTreeAmbient();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.platform.ClipboardManager>! getClipboardManagerAmbient();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.unit.Density>! getDensityAmbient();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.focus.FocusManager>! getFocusManagerAmbient();
@@ -2154,34 +2224,12 @@
   public final class AndroidClipboardManagerKt {
   }
 
+  public final class AndroidComposeViewAccessibilityDelegateCompatKt {
+  }
+
   public final class AndroidComposeViewKt {
   }
 
-  public interface AndroidOwner extends androidx.compose.ui.node.Owner {
-    method public void addAndroidView(androidx.compose.ui.viewinterop.AndroidViewHolder view, androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void drawAndroidView(androidx.compose.ui.viewinterop.AndroidViewHolder view, android.graphics.Canvas canvas);
-    method public kotlin.jvm.functions.Function1<android.content.res.Configuration,kotlin.Unit> getConfigurationChangeObserver();
-    method public android.view.View getView();
-    method public androidx.compose.ui.platform.AndroidOwner.ViewTreeOwners? getViewTreeOwners();
-    method public void invalidateDescendants();
-    method public void removeAndroidView(androidx.compose.ui.viewinterop.AndroidViewHolder view);
-    method public void setConfigurationChangeObserver(kotlin.jvm.functions.Function1<? super android.content.res.Configuration,kotlin.Unit> p);
-    method public void setOnViewTreeOwnersAvailable(kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.AndroidOwner.ViewTreeOwners,kotlin.Unit> callback);
-    property public abstract kotlin.jvm.functions.Function1<android.content.res.Configuration,kotlin.Unit> configurationChangeObserver;
-    property public abstract android.view.View view;
-    property public abstract androidx.compose.ui.platform.AndroidOwner.ViewTreeOwners? viewTreeOwners;
-  }
-
-  public static final class AndroidOwner.ViewTreeOwners {
-    ctor public AndroidOwner.ViewTreeOwners(androidx.lifecycle.LifecycleOwner lifecycleOwner, androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, androidx.savedstate.SavedStateRegistryOwner savedStateRegistryOwner);
-    method public androidx.lifecycle.LifecycleOwner getLifecycleOwner();
-    method public androidx.savedstate.SavedStateRegistryOwner getSavedStateRegistryOwner();
-    method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner();
-    property public final androidx.lifecycle.LifecycleOwner lifecycleOwner;
-    property public final androidx.savedstate.SavedStateRegistryOwner savedStateRegistryOwner;
-    property public final androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner;
-  }
-
   public final class AndroidUriHandler implements androidx.compose.ui.platform.UriHandler {
     ctor public AndroidUriHandler(android.content.Context context);
     method public void openUri(String uri);
@@ -2210,6 +2258,7 @@
     ctor public ComposeView(android.content.Context context);
     method @androidx.compose.runtime.Composable public void Content();
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    property protected boolean shouldCreateCompositionOnAttachedToWindow;
   }
 
   public final class DebugUtilsKt {
@@ -2260,10 +2309,6 @@
   public final class JvmActualsKt {
   }
 
-  public final class SubcompositionKt {
-    method @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-  }
-
   public final class TestTagKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier testTag(androidx.compose.ui.Modifier, String tag);
   }
@@ -2304,6 +2349,33 @@
     method public operator void set(String name, Object? value);
   }
 
+  public interface ViewCompositionStrategy {
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+    field public static final androidx.compose.ui.platform.ViewCompositionStrategy.Companion Companion;
+  }
+
+  public static final class ViewCompositionStrategy.Companion {
+  }
+
+  public static final class ViewCompositionStrategy.DisposeOnDetachedFromWindow implements androidx.compose.ui.platform.ViewCompositionStrategy {
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+    field public static final androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnDetachedFromWindow INSTANCE;
+  }
+
+  public static final class ViewCompositionStrategy.DisposeOnLifecycleDestroyed implements androidx.compose.ui.platform.ViewCompositionStrategy {
+    ctor public ViewCompositionStrategy.DisposeOnLifecycleDestroyed(androidx.lifecycle.Lifecycle lifecycle);
+    ctor public ViewCompositionStrategy.DisposeOnLifecycleDestroyed(androidx.lifecycle.LifecycleOwner lifecycleOwner);
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+  }
+
+  public static final class ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed implements androidx.compose.ui.platform.ViewCompositionStrategy {
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+    field public static final androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed INSTANCE;
+  }
+
+  public final class ViewCompositionStrategyKt {
+  }
+
   public interface ViewConfiguration {
     method public long getDoubleTapMinTime-ojFfpTE();
     method public long getDoubleTapTimeout-ojFfpTE();
@@ -2315,6 +2387,23 @@
     property public abstract float touchSlop;
   }
 
+  @VisibleForTesting public interface ViewRootForTest extends androidx.compose.ui.node.Owner {
+    method public boolean getHasPendingMeasureOrLayout();
+    method public android.view.View getView();
+    method public void invalidateDescendants();
+    method public boolean isLifecycleInResumedState();
+    property public abstract boolean hasPendingMeasureOrLayout;
+    property public abstract boolean isLifecycleInResumedState;
+    property public abstract android.view.View view;
+    field public static final androidx.compose.ui.platform.ViewRootForTest.Companion Companion;
+  }
+
+  public static final class ViewRootForTest.Companion {
+    method public kotlin.jvm.functions.Function1<androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? getOnViewCreatedCallback();
+    method public void setOnViewCreatedCallback(kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? p);
+    property public final kotlin.jvm.functions.Function1<androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? onViewCreatedCallback;
+  }
+
   @androidx.compose.runtime.Stable public interface WindowManager {
     method public boolean isWindowFocused();
     property public abstract boolean isWindowFocused;
@@ -2324,9 +2413,15 @@
     method @androidx.compose.runtime.Composable public static void WindowFocusObserver(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onWindowFocusChanged);
   }
 
+  public final class WindowRecomposerKt {
+    method public static androidx.compose.runtime.CompositionReference? findViewTreeCompositionReference(android.view.View);
+    method public static androidx.compose.runtime.CompositionReference? getCompositionReference(android.view.View);
+    method public static void setCompositionReference(android.view.View, androidx.compose.runtime.CompositionReference? value);
+  }
+
   public final class WrapperKt {
     method public static androidx.compose.runtime.Composition setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
     method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.app.Activity, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
   }
@@ -2410,7 +2505,7 @@
 
 package androidx.compose.ui.selection {
 
-  public interface Selectable {
+  @androidx.compose.ui.text.ExperimentalTextApi public interface Selectable {
     method public androidx.compose.ui.geometry.Rect getBoundingBox(int offset);
     method public long getHandlePosition-F1C5BW0(androidx.compose.ui.selection.Selection selection, boolean isStartHandle);
     method public androidx.compose.ui.layout.LayoutCoordinates? getLayoutCoordinates();
@@ -2460,9 +2555,12 @@
   public final class SelectionManagerKt {
   }
 
-  public interface SelectionRegistrar {
-    method public void onPositionChange();
-    method public void onUpdateSelection-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+  @androidx.compose.ui.text.ExperimentalTextApi public interface SelectionRegistrar {
+    method public void notifyPositionChange();
+    method public void notifySelectableChange(androidx.compose.ui.selection.Selectable selectable);
+    method public void notifySelectionUpdate-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+    method public void notifySelectionUpdateEnd();
+    method public void notifySelectionUpdateStart-YJiYy8w(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition);
     method public androidx.compose.ui.selection.Selectable subscribe(androidx.compose.ui.selection.Selectable selectable);
     method public void unsubscribe(androidx.compose.ui.selection.Selectable selectable);
   }
@@ -2610,8 +2708,9 @@
     method public androidx.compose.ui.geometry.Rect getGlobalBounds();
     method public long getGlobalPosition-F1C5BW0();
     method public int getId();
-    method public androidx.compose.ui.node.LayoutNode getLayoutNode();
+    method public androidx.compose.ui.layout.LayoutInfo getLayoutInfo();
     method public boolean getMergingEnabled();
+    method public androidx.compose.ui.node.Owner? getOwner();
     method public androidx.compose.ui.semantics.SemanticsNode? getParent();
     method public long getPositionInRoot-F1C5BW0();
     method public long getSize-YbymL2g();
@@ -2623,8 +2722,9 @@
     property public final long globalPosition;
     property public final int id;
     property public final boolean isRoot;
-    property public final androidx.compose.ui.node.LayoutNode layoutNode;
+    property public final androidx.compose.ui.layout.LayoutInfo layoutInfo;
     property public final boolean mergingEnabled;
+    property public final androidx.compose.ui.node.Owner? owner;
     property public final androidx.compose.ui.semantics.SemanticsNode? parent;
     property public final long positionInRoot;
     property public final long size;
@@ -2647,9 +2747,8 @@
   }
 
   public final class SemanticsProperties {
-    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getAccessibilityLabel();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityRangeInfo> getAccessibilityRangeInfo();
-    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getAccessibilityValue();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getContentDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getDisabled();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getFocused();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getHidden();
@@ -2658,14 +2757,14 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsDialog();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsPopup();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getSelected();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getStateDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getTestTag();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> getText();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> getTextSelectionRange();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> getToggleableState();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityScrollState> getVerticalAccessibilityScrollState();
-    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> AccessibilityLabel;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityRangeInfo> AccessibilityRangeInfo;
-    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> AccessibilityValue;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> ContentDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Disabled;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Focused;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Hidden;
@@ -2674,6 +2773,7 @@
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsDialog;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsPopup;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Selected;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> StateDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> TestTag;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> Text;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> TextSelectionRange;
@@ -2688,14 +2788,16 @@
     method public static void dialog(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void disabled(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void dismiss(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
-    method public static String getAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
-    method public static String getAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
-    method public static androidx.compose.ui.semantics.AccessibilityRangeInfo getAccessibilityValueRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method @Deprecated public static String getAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method @Deprecated public static String getAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static String getContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> getCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static boolean getFocused(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.semantics.AccessibilityScrollState getHorizontalAccessibilityScrollState(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.input.ImeAction getImeAction(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static boolean getSelected(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static String getStateDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static androidx.compose.ui.semantics.AccessibilityRangeInfo getStateDescriptionRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static String getTestTag(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.AnnotatedString getText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void getTextLayoutResult(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean> action);
@@ -2708,9 +2810,9 @@
     method public static void pasteText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public static void popup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean> action);
-    method public static void setAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
-    method public static void setAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
-    method public static void setAccessibilityValueRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.AccessibilityRangeInfo p);
+    method @Deprecated public static void setAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
+    method @Deprecated public static void setAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
+    method public static void setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver, java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> p);
     method public static void setFocused(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean p);
     method public static void setHorizontalAccessibilityScrollState(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.AccessibilityScrollState p);
@@ -2718,6 +2820,8 @@
     method public static void setProgress(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Boolean> action);
     method public static void setSelected(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean p);
     method public static void setSelection(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function3<? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Boolean,java.lang.Boolean> action);
+    method public static void setStateDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
+    method public static void setStateDescriptionRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.AccessibilityRangeInfo p);
     method public static void setTestTag(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString p);
     method public static void setText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString,java.lang.Boolean> action);
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index e4118d8..b64837d 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -132,26 +132,29 @@
     method @Deprecated public static androidx.compose.ui.Modifier drawWithContent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.ContentDrawScope,kotlin.Unit> onDraw);
   }
 
-  public final class FocusModifierKt {
-    method @androidx.compose.ui.focus.ExperimentalFocus public static androidx.compose.ui.Modifier focus(androidx.compose.ui.Modifier);
+  @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") public @interface ExperimentalComposeUiApi {
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public interface FocusObserverModifier extends androidx.compose.ui.Modifier.Element {
-    method public kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusState,kotlin.Unit> getOnFocusChange();
+  public final class FocusModifierKt {
+    method @Deprecated public static androidx.compose.ui.Modifier focus(androidx.compose.ui.Modifier);
+  }
+
+  @Deprecated public interface FocusObserverModifier extends androidx.compose.ui.Modifier.Element {
+    method @Deprecated public kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusState,kotlin.Unit> getOnFocusChange();
     property public abstract kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChange;
   }
 
   public final class FocusObserverModifierKt {
-    method @androidx.compose.ui.focus.ExperimentalFocus public static androidx.compose.ui.Modifier focusObserver(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChange);
+    method @Deprecated public static androidx.compose.ui.Modifier focusObserver(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChange);
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public interface FocusRequesterModifier extends androidx.compose.ui.Modifier.Element {
-    method public androidx.compose.ui.focus.FocusRequester getFocusRequester();
-    property public abstract androidx.compose.ui.focus.FocusRequester focusRequester;
+  @Deprecated public interface FocusRequesterModifier extends androidx.compose.ui.Modifier.Element {
+    method @Deprecated public androidx.compose.ui.focus.FocusReference getFocusReference();
+    property public abstract androidx.compose.ui.focus.FocusReference focusReference;
   }
 
   public final class FocusRequesterModifierKt {
-    method @androidx.compose.ui.focus.ExperimentalFocus public static androidx.compose.ui.Modifier focusRequester(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester);
+    method @Deprecated public static androidx.compose.ui.Modifier focusRequester(androidx.compose.ui.Modifier, Object focusRequester);
   }
 
   @androidx.compose.runtime.Stable public interface Modifier {
@@ -194,17 +197,17 @@
   public final class AndroidAutofillTypeKt {
   }
 
-  public interface Autofill {
+  @androidx.compose.ui.ExperimentalComposeUiApi public interface Autofill {
     method public void cancelAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
     method public void requestAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
   }
 
-  public final class AutofillNode {
+  @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillNode {
     ctor public AutofillNode(java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
     method public java.util.List<androidx.compose.ui.autofill.AutofillType> component1();
     method public androidx.compose.ui.geometry.Rect? component2();
     method public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? component3();
-    method public androidx.compose.ui.autofill.AutofillNode copy(java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
+    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.autofill.AutofillNode copy(java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
     method public java.util.List<androidx.compose.ui.autofill.AutofillType> getAutofillTypes();
     method public androidx.compose.ui.geometry.Rect? getBoundingBox();
     method public int getId();
@@ -216,7 +219,7 @@
     property public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? onFill;
   }
 
-  public final class AutofillTree {
+  @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillTree {
     ctor public AutofillTree();
     method public java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> getChildren();
     method public kotlin.Unit? performAutofill(int id, String value);
@@ -224,7 +227,7 @@
     property public final java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> children;
   }
 
-  public enum AutofillType {
+  @androidx.compose.ui.ExperimentalComposeUiApi public enum AutofillType {
     enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressAuxiliaryDetails;
     enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressCountry;
     enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressLocality;
@@ -328,27 +331,81 @@
 
 package androidx.compose.ui.focus {
 
-  @kotlin.RequiresOptIn(message="The Focus API is experimental and is likely to change in the future.") public @interface ExperimentalFocus {
+  public final class FocusChangedModifierKt {
+    method public static androidx.compose.ui.Modifier onFocusChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChanged);
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public interface FocusManager {
+  public interface FocusEventModifier extends androidx.compose.ui.Modifier.Element {
+    method public void onFocusEvent(androidx.compose.ui.focus.FocusState focusState);
+  }
+
+  public final class FocusEventModifierKt {
+    method public static androidx.compose.ui.Modifier onFocusEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusEvent);
+  }
+
+  public interface FocusManager {
     method public void clearFocus(optional boolean forcedClear);
   }
 
+  public final class FocusModifierKt {
+    method public static androidx.compose.ui.Modifier focusModifier(androidx.compose.ui.Modifier);
+  }
+
   public final class FocusNodeUtilsKt {
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public final class FocusRequester {
-    ctor public FocusRequester();
+  public final class FocusReference {
+    ctor public FocusReference();
     method public boolean captureFocus();
     method public boolean freeFocus();
     method public void requestFocus();
+    field public static final androidx.compose.ui.focus.FocusReference.Companion Companion;
   }
 
-  public final class FocusRequesterKt {
+  public static final class FocusReference.Companion {
+    method public androidx.compose.ui.focus.FocusReference.Companion.FocusReferenceFactory createRefs();
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public enum FocusState {
+  public static final class FocusReference.Companion.FocusReferenceFactory {
+    method public operator androidx.compose.ui.focus.FocusReference component1();
+    method public operator androidx.compose.ui.focus.FocusReference component10();
+    method public operator androidx.compose.ui.focus.FocusReference component11();
+    method public operator androidx.compose.ui.focus.FocusReference component12();
+    method public operator androidx.compose.ui.focus.FocusReference component13();
+    method public operator androidx.compose.ui.focus.FocusReference component14();
+    method public operator androidx.compose.ui.focus.FocusReference component15();
+    method public operator androidx.compose.ui.focus.FocusReference component16();
+    method public operator androidx.compose.ui.focus.FocusReference component2();
+    method public operator androidx.compose.ui.focus.FocusReference component3();
+    method public operator androidx.compose.ui.focus.FocusReference component4();
+    method public operator androidx.compose.ui.focus.FocusReference component5();
+    method public operator androidx.compose.ui.focus.FocusReference component6();
+    method public operator androidx.compose.ui.focus.FocusReference component7();
+    method public operator androidx.compose.ui.focus.FocusReference component8();
+    method public operator androidx.compose.ui.focus.FocusReference component9();
+    field public static final androidx.compose.ui.focus.FocusReference.Companion.FocusReferenceFactory INSTANCE;
+  }
+
+  public final class FocusReferenceKt {
+  }
+
+  public interface FocusReferenceModifier extends androidx.compose.ui.Modifier.Element {
+    method public androidx.compose.ui.focus.FocusReference getFocusReference();
+    property public abstract androidx.compose.ui.focus.FocusReference focusReference;
+  }
+
+  public final class FocusReferenceModifierKt {
+    method public static androidx.compose.ui.Modifier focusReference(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusReference focusReference);
+  }
+
+  @Deprecated public final class FocusRequester {
+    ctor @Deprecated public FocusRequester();
+    method @Deprecated public boolean captureFocus();
+    method @Deprecated public boolean freeFocus();
+    method @Deprecated public void requestFocus();
+  }
+
+  public enum FocusState {
     enum_constant public static final androidx.compose.ui.focus.FocusState Active;
     enum_constant public static final androidx.compose.ui.focus.FocusState ActiveParent;
     enum_constant public static final androidx.compose.ui.focus.FocusState Captured;
@@ -410,9 +467,6 @@
     method public static androidx.compose.ui.Modifier dragSlopExceededGestureFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onDragSlopExceeded, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.gesture.Direction,java.lang.Boolean>? canDrag, optional androidx.compose.ui.gesture.scrollorientationlocking.Orientation? orientation);
   }
 
-  @kotlin.RequiresOptIn(message="This pointer input API is experimental and is likely to change before becoming " + "stable.") public @interface ExperimentalPointerInput {
-  }
-
   public final class GestureUtilsKt {
     method public static boolean anyPointersInBounds-5eFHUEc(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange>, long bounds);
   }
@@ -493,11 +547,11 @@
 
 package androidx.compose.ui.gesture.customevents {
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public final class DelayUpEvent implements androidx.compose.ui.input.pointer.CustomEvent {
+  public final class DelayUpEvent implements androidx.compose.ui.input.pointer.CustomEvent {
     ctor public DelayUpEvent(androidx.compose.ui.gesture.customevents.DelayUpMessage message, java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers);
     method public androidx.compose.ui.gesture.customevents.DelayUpMessage component1();
     method public java.util.Set<androidx.compose.ui.input.pointer.PointerId> component2();
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public androidx.compose.ui.gesture.customevents.DelayUpEvent copy(androidx.compose.ui.gesture.customevents.DelayUpMessage message, java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers);
+    method public androidx.compose.ui.gesture.customevents.DelayUpEvent copy(androidx.compose.ui.gesture.customevents.DelayUpMessage message, java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers);
     method public androidx.compose.ui.gesture.customevents.DelayUpMessage getMessage();
     method public java.util.Set<androidx.compose.ui.input.pointer.PointerId> getPointers();
     method public void setMessage(androidx.compose.ui.gesture.customevents.DelayUpMessage p);
@@ -505,7 +559,7 @@
     property public final java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers;
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public enum DelayUpMessage {
+  public enum DelayUpMessage {
     enum_constant public static final androidx.compose.ui.gesture.customevents.DelayUpMessage DelayUp;
     enum_constant public static final androidx.compose.ui.gesture.customevents.DelayUpMessage DelayedUpConsumed;
     enum_constant public static final androidx.compose.ui.gesture.customevents.DelayUpMessage DelayedUpNotConsumed;
@@ -517,6 +571,37 @@
 
 }
 
+package androidx.compose.ui.gesture.nestedscroll {
+
+  public interface NestedScrollConnection {
+    method public default void onPostFling-Pv53iXo(long consumed, long available, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Velocity,kotlin.Unit> onFinished);
+    method public default long onPostScroll-l-UAZDg(long consumed, long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+    method public default long onPreFling-TH1AsA0(long available);
+    method public default long onPreScroll-vG6bCaM(long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+  }
+
+  public final class NestedScrollDelegatingWrapperKt {
+  }
+
+  public final class NestedScrollDispatcher {
+    ctor public NestedScrollDispatcher();
+    method public void dispatchPostFling-uYzo7IE(long consumed, long available);
+    method public long dispatchPostScroll-l-UAZDg(long consumed, long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+    method public long dispatchPreFling-TH1AsA0(long available);
+    method public long dispatchPreScroll-vG6bCaM(long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+  }
+
+  public final class NestedScrollModifierKt {
+    method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.gesture.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.gesture.nestedscroll.NestedScrollDispatcher? dispatcher);
+  }
+
+  public enum NestedScrollSource {
+    enum_constant public static final androidx.compose.ui.gesture.nestedscroll.NestedScrollSource Drag;
+    enum_constant public static final androidx.compose.ui.gesture.nestedscroll.NestedScrollSource Fling;
+  }
+
+}
+
 package androidx.compose.ui.gesture.scrollorientationlocking {
 
   public enum Orientation {
@@ -524,7 +609,7 @@
     enum_constant public static final androidx.compose.ui.gesture.scrollorientationlocking.Orientation Vertical;
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public final class ScrollOrientationLocker {
+  public final class ScrollOrientationLocker {
     ctor public ScrollOrientationLocker(androidx.compose.ui.input.pointer.CustomEventDispatcher customEventDispatcher);
     method public void attemptToLockPointers(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> changes, androidx.compose.ui.gesture.scrollorientationlocking.Orientation orientation);
     method public java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> getPointersFor(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> changes, androidx.compose.ui.gesture.scrollorientationlocking.Orientation orientation);
@@ -812,18 +897,6 @@
 
 package androidx.compose.ui.input.key {
 
-  @Deprecated @androidx.compose.ui.input.key.ExperimentalKeyInput public interface Alt {
-    method @Deprecated public boolean isLeftAltPressed();
-    method @Deprecated public default boolean isPressed();
-    method @Deprecated public boolean isRightAltPressed();
-    property public abstract boolean isLeftAltPressed;
-    property public default boolean isPressed;
-    property public abstract boolean isRightAltPressed;
-  }
-
-  @kotlin.RequiresOptIn(message="The Key Input API is experimental and is likely to change in the future.") public @interface ExperimentalKeyInput {
-  }
-
   public final inline class Key {
     ctor public Key();
     method public static int constructor-impl(int keyCode);
@@ -1417,8 +1490,7 @@
     property public final int ZoomOut;
   }
 
-  @androidx.compose.ui.input.key.ExperimentalKeyInput public interface KeyEvent {
-    method @Deprecated public androidx.compose.ui.input.key.Alt getAlt();
+  public interface KeyEvent {
     method public int getKey-EK5gGoQ();
     method public androidx.compose.ui.input.key.KeyEventType getType();
     method public int getUtf16CodePoint();
@@ -1426,7 +1498,6 @@
     method public boolean isCtrlPressed();
     method public boolean isMetaPressed();
     method public boolean isShiftPressed();
-    property @Deprecated public abstract androidx.compose.ui.input.key.Alt alt;
     property public abstract boolean isAltPressed;
     property public abstract boolean isCtrlPressed;
     property public abstract boolean isMetaPressed;
@@ -1436,15 +1507,17 @@
     property public abstract int utf16CodePoint;
   }
 
-  @androidx.compose.ui.input.key.ExperimentalKeyInput public enum KeyEventType {
+  public enum KeyEventType {
     enum_constant public static final androidx.compose.ui.input.key.KeyEventType KeyDown;
     enum_constant public static final androidx.compose.ui.input.key.KeyEventType KeyUp;
     enum_constant public static final androidx.compose.ui.input.key.KeyEventType Unknown;
   }
 
   public final class KeyInputModifierKt {
-    method @androidx.compose.ui.input.key.ExperimentalKeyInput public static androidx.compose.ui.Modifier keyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onKeyEvent);
-    method @androidx.compose.ui.input.key.ExperimentalKeyInput public static androidx.compose.ui.Modifier previewKeyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreviewKeyEvent);
+    method @Deprecated public static androidx.compose.ui.Modifier keyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onKeyEvent);
+    method public static androidx.compose.ui.Modifier onKeyEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onKeyEvent);
+    method public static androidx.compose.ui.Modifier onPreviewKeyEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreviewKeyEvent);
+    method @Deprecated public static androidx.compose.ui.Modifier previewKeyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreviewKeyEvent);
   }
 
 }
@@ -1455,6 +1528,16 @@
     method public static boolean isMouseInput();
   }
 
+  @kotlin.coroutines.RestrictsSuspension public interface AwaitPointerEventScope extends androidx.compose.ui.unit.Density {
+    method public suspend Object? awaitPointerEvent(optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerEvent> p);
+    method public androidx.compose.ui.input.pointer.PointerEvent getCurrentEvent();
+    method public long getSize-YbymL2g();
+    method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
+    property public abstract androidx.compose.ui.input.pointer.PointerEvent currentEvent;
+    property public abstract long size;
+    property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
+  }
+
   public final class ConsumedData {
     method public boolean getDownChange();
     method public long getPositionChange-F1C5BW0();
@@ -1473,17 +1556,6 @@
     method public void retainHitPaths(java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointerIds);
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput @kotlin.coroutines.RestrictsSuspension public interface HandlePointerInputScope extends androidx.compose.ui.unit.Density {
-    method public suspend Object? awaitCustomEvent(optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.CustomEvent> p);
-    method public suspend Object? awaitPointerEvent(optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerEvent> p);
-    method public androidx.compose.ui.input.pointer.PointerEvent getCurrentEvent();
-    method public long getSize-YbymL2g();
-    method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
-    property public abstract androidx.compose.ui.input.pointer.PointerEvent currentEvent;
-    property public abstract long size;
-    property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
-  }
-
   public final class HitPathTrackerKt {
   }
 
@@ -1601,12 +1673,11 @@
     property public abstract androidx.compose.ui.input.pointer.PointerInputFilter pointerInputFilter;
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public interface PointerInputScope extends androidx.compose.ui.unit.Density {
-    method public androidx.compose.ui.input.pointer.CustomEventDispatcher getCustomEventDispatcher();
+  public interface PointerInputScope extends androidx.compose.ui.unit.Density {
+    method public suspend <R> Object? awaitPointerEventScope(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R> p);
     method public long getSize-YbymL2g();
     method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
-    method public suspend <R> Object? handlePointerInput(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.HandlePointerInputScope,? super kotlin.coroutines.Continuation<? super R>,?> handler, kotlin.coroutines.Continuation<? super R> p);
-    property public abstract androidx.compose.ui.input.pointer.CustomEventDispatcher customEventDispatcher;
+    method @Deprecated public default suspend <R> Object? handlePointerInput(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super R>,?> handler, kotlin.coroutines.Continuation<? super R> p);
     property public abstract long size;
     property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
   }
@@ -1627,7 +1698,7 @@
   }
 
   public final class SuspendingPointerInputFilterKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
   }
 
 }
@@ -1740,6 +1811,22 @@
     property public abstract Object layoutId;
   }
 
+  public interface LayoutInfo {
+    method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
+    method public int getHeight();
+    method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
+    method public androidx.compose.ui.layout.LayoutInfo? getParentInfo();
+    method public int getWidth();
+    method public boolean isAttached();
+    method public boolean isPlaced();
+    property public abstract androidx.compose.ui.layout.LayoutCoordinates coordinates;
+    property public abstract int height;
+    property public abstract boolean isAttached;
+    property public abstract boolean isPlaced;
+    property public abstract androidx.compose.ui.layout.LayoutInfo? parentInfo;
+    property public abstract int width;
+  }
+
   public final class LayoutKt {
     method @androidx.compose.runtime.Composable public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> minIntrinsicWidthMeasureBlock, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> minIntrinsicHeightMeasureBlock, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> maxIntrinsicWidthMeasureBlock, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> maxIntrinsicHeightMeasureBlock, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.MeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.Measurable>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
     method @androidx.compose.runtime.Composable public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> content, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.MeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.Measurable>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
@@ -1795,6 +1882,16 @@
     method public static inline String! toString-impl(androidx.compose.ui.layout.Placeable! p);
   }
 
+  public final class ModifierInfo {
+    ctor public ModifierInfo(androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.LayoutCoordinates coordinates, Object? extra);
+    method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
+    method public Object? getExtra();
+    method public androidx.compose.ui.Modifier getModifier();
+    property public final androidx.compose.ui.layout.LayoutCoordinates coordinates;
+    property public final Object? extra;
+    property public final androidx.compose.ui.Modifier modifier;
+  }
+
   public interface OnGloballyPositionedModifier extends androidx.compose.ui.Modifier.Element {
     method public void onGloballyPositioned(androidx.compose.ui.layout.LayoutCoordinates coordinates);
   }
@@ -1887,7 +1984,10 @@
   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 public static inline boolean isSpecified-FK8aYYs(long);
+    method public static inline boolean isUnspecified-FK8aYYs(long);
     method @androidx.compose.runtime.Stable public static long lerp-bKVCie4(long start, long stop, float fraction);
+    method public static inline long takeOrElse-L-byAFk(long, kotlin.jvm.functions.Function0<androidx.compose.ui.layout.ScaleFactor> block);
     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);
   }
@@ -1900,6 +2000,9 @@
     method public java.util.List<androidx.compose.ui.layout.Measurable> subcompose(Object? slotId, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class TestModifierUpdaterKt {
+  }
+
   public final class VerticalAlignmentLine extends androidx.compose.ui.layout.AlignmentLine {
     ctor public VerticalAlignmentLine(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,java.lang.Integer> merger);
   }
@@ -1924,25 +2027,15 @@
   @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalCoreApi {
   }
 
-  public final class LayoutNode implements androidx.compose.ui.layout.Measurable androidx.compose.ui.node.OwnerScope androidx.compose.ui.layout.Remeasurement {
-    ctor public LayoutNode();
-    method public void attach(androidx.compose.ui.node.Owner owner);
-    method public void detach();
+  public final class LayoutNode implements androidx.compose.ui.layout.LayoutInfo androidx.compose.ui.layout.Measurable androidx.compose.ui.node.OwnerScope androidx.compose.ui.layout.Remeasurement {
     method public void forceRemeasure();
-    method @Deprecated public boolean getCanMultiMeasure();
-    method public java.util.List<androidx.compose.ui.node.LayoutNode> getChildren();
     method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
-    method public androidx.compose.ui.unit.Density getDensity();
-    method public int getDepth();
     method public int getHeight();
-    method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
-    method public androidx.compose.ui.node.MeasureBlocks getMeasureBlocks();
-    method public androidx.compose.ui.Modifier getModifier();
-    method public java.util.List<androidx.compose.ui.node.ModifierInfo> getModifierInfo();
-    method public androidx.compose.ui.node.Owner? getOwner();
-    method public androidx.compose.ui.node.LayoutNode? getParent();
+    method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
     method public Object? getParentData();
+    method public androidx.compose.ui.layout.LayoutInfo? getParentInfo();
     method public int getWidth();
+    method public boolean isAttached();
     method public boolean isPlaced();
     method public boolean isValid();
     method public int maxIntrinsicHeight(int width);
@@ -1950,28 +2043,14 @@
     method public androidx.compose.ui.layout.Placeable measure-BRTryo0(long constraints);
     method public int minIntrinsicHeight(int width);
     method public int minIntrinsicWidth(int height);
-    method public void place(int x, int y);
-    method @Deprecated public void setCanMultiMeasure(boolean p);
-    method public void setDensity(androidx.compose.ui.unit.Density p);
-    method public void setDepth(int p);
-    method public void setLayoutDirection(androidx.compose.ui.unit.LayoutDirection value);
-    method public void setMeasureBlocks(androidx.compose.ui.node.MeasureBlocks value);
-    method public void setModifier(androidx.compose.ui.Modifier value);
-    property @Deprecated public final boolean canMultiMeasure;
-    property public final java.util.List<androidx.compose.ui.node.LayoutNode> children;
-    property public final androidx.compose.ui.layout.LayoutCoordinates coordinates;
-    property public final androidx.compose.ui.unit.Density density;
-    property public final int depth;
-    property public final int height;
-    property public final boolean isPlaced;
+    property public androidx.compose.ui.layout.LayoutCoordinates coordinates;
+    property public int height;
+    property public boolean isAttached;
+    property public boolean isPlaced;
     property public boolean isValid;
-    property public final androidx.compose.ui.unit.LayoutDirection layoutDirection;
-    property public final androidx.compose.ui.node.MeasureBlocks measureBlocks;
-    property public final androidx.compose.ui.Modifier modifier;
-    property public final androidx.compose.ui.node.Owner? owner;
-    property public final androidx.compose.ui.node.LayoutNode? parent;
     property public Object? parentData;
-    property public final int width;
+    property public androidx.compose.ui.layout.LayoutInfo? parentInfo;
+    property public int width;
   }
 
   public final class LayoutNodeKt {
@@ -1985,16 +2064,6 @@
     method public int minIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope intrinsicMeasureScope, java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable> measurables, int h);
   }
 
-  public final class ModifierInfo {
-    ctor public ModifierInfo(androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.LayoutCoordinates coordinates, Object? extra);
-    method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
-    method public Object? getExtra();
-    method public androidx.compose.ui.Modifier getModifier();
-    property public final androidx.compose.ui.layout.LayoutCoordinates coordinates;
-    property public final Object? extra;
-    property public final androidx.compose.ui.Modifier modifier;
-  }
-
   public interface OwnedLayer {
     method public void destroy();
     method public void drawLayer(androidx.compose.ui.graphics.Canvas canvas);
@@ -2018,7 +2087,6 @@
     method public androidx.compose.ui.focus.FocusManager getFocusManager();
     method public androidx.compose.ui.text.font.Font.ResourceLoader getFontLoader();
     method public androidx.compose.ui.hapticfeedback.HapticFeedback getHapticFeedBack();
-    method public boolean getHasPendingMeasureOrLayout();
     method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
     method public long getMeasureIteration();
     method public androidx.compose.ui.node.LayoutNode getRoot();
@@ -2032,11 +2100,12 @@
     method public void measureAndLayout();
     method public void onAttach(androidx.compose.ui.node.LayoutNode node);
     method public void onDetach(androidx.compose.ui.node.LayoutNode node);
+    method public void onLayoutChange(androidx.compose.ui.node.LayoutNode layoutNode);
     method public void onRequestMeasure(androidx.compose.ui.node.LayoutNode layoutNode);
     method public void onRequestRelayout(androidx.compose.ui.node.LayoutNode layoutNode);
     method public void onSemanticsChange();
     method public boolean requestFocus();
-    method @androidx.compose.ui.input.key.ExperimentalKeyInput public boolean sendKeyEvent(androidx.compose.ui.input.key.KeyEvent keyEvent);
+    method public boolean sendKeyEvent(androidx.compose.ui.input.key.KeyEvent keyEvent);
     property public abstract androidx.compose.ui.autofill.Autofill? autofill;
     property public abstract androidx.compose.ui.autofill.AutofillTree autofillTree;
     property public abstract androidx.compose.ui.platform.ClipboardManager clipboardManager;
@@ -2044,7 +2113,6 @@
     property public abstract androidx.compose.ui.focus.FocusManager focusManager;
     property public abstract androidx.compose.ui.text.font.Font.ResourceLoader fontLoader;
     property public abstract androidx.compose.ui.hapticfeedback.HapticFeedback hapticFeedBack;
-    property public abstract boolean hasPendingMeasureOrLayout;
     property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
     property public abstract long measureIteration;
     property public abstract androidx.compose.ui.node.LayoutNode root;
@@ -2103,10 +2171,14 @@
     method @androidx.compose.runtime.Composable public abstract void Content();
     method public final void createComposition();
     method public final void disposeComposition();
-    method public final boolean isDisposed();
+    method public final boolean getHasComposition();
+    method protected boolean getShouldCreateCompositionOnAttachedToWindow();
     method protected final void onLayout(boolean changed, int left, int top, int right, int bottom);
     method protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec);
-    property public final boolean isDisposed;
+    method public final void setParentCompositionReference(androidx.compose.runtime.CompositionReference? parent);
+    method public final void setViewCompositionStrategy(androidx.compose.ui.platform.ViewCompositionStrategy strategy);
+    property public final boolean hasComposition;
+    property protected boolean shouldCreateCompositionOnAttachedToWindow;
   }
 
   public final class AmbientsKt {
@@ -2124,8 +2196,6 @@
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.platform.ViewConfiguration> getAmbientViewConfiguration();
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.platform.WindowManager> getAmbientWindowManager();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.animation.core.AnimationClockObservable>! getAnimationClockAmbient();
-    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.autofill.Autofill>! getAutofillAmbient();
-    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.autofill.AutofillTree>! getAutofillTreeAmbient();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.platform.ClipboardManager>! getClipboardManagerAmbient();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.unit.Density>! getDensityAmbient();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.focus.FocusManager>! getFocusManagerAmbient();
@@ -2154,34 +2224,12 @@
   public final class AndroidClipboardManagerKt {
   }
 
+  public final class AndroidComposeViewAccessibilityDelegateCompatKt {
+  }
+
   public final class AndroidComposeViewKt {
   }
 
-  public interface AndroidOwner extends androidx.compose.ui.node.Owner {
-    method public void addAndroidView(androidx.compose.ui.viewinterop.AndroidViewHolder view, androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void drawAndroidView(androidx.compose.ui.viewinterop.AndroidViewHolder view, android.graphics.Canvas canvas);
-    method public kotlin.jvm.functions.Function1<android.content.res.Configuration,kotlin.Unit> getConfigurationChangeObserver();
-    method public android.view.View getView();
-    method public androidx.compose.ui.platform.AndroidOwner.ViewTreeOwners? getViewTreeOwners();
-    method public void invalidateDescendants();
-    method public void removeAndroidView(androidx.compose.ui.viewinterop.AndroidViewHolder view);
-    method public void setConfigurationChangeObserver(kotlin.jvm.functions.Function1<? super android.content.res.Configuration,kotlin.Unit> p);
-    method public void setOnViewTreeOwnersAvailable(kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.AndroidOwner.ViewTreeOwners,kotlin.Unit> callback);
-    property public abstract kotlin.jvm.functions.Function1<android.content.res.Configuration,kotlin.Unit> configurationChangeObserver;
-    property public abstract android.view.View view;
-    property public abstract androidx.compose.ui.platform.AndroidOwner.ViewTreeOwners? viewTreeOwners;
-  }
-
-  public static final class AndroidOwner.ViewTreeOwners {
-    ctor public AndroidOwner.ViewTreeOwners(androidx.lifecycle.LifecycleOwner lifecycleOwner, androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, androidx.savedstate.SavedStateRegistryOwner savedStateRegistryOwner);
-    method public androidx.lifecycle.LifecycleOwner getLifecycleOwner();
-    method public androidx.savedstate.SavedStateRegistryOwner getSavedStateRegistryOwner();
-    method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner();
-    property public final androidx.lifecycle.LifecycleOwner lifecycleOwner;
-    property public final androidx.savedstate.SavedStateRegistryOwner savedStateRegistryOwner;
-    property public final androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner;
-  }
-
   public final class AndroidUriHandler implements androidx.compose.ui.platform.UriHandler {
     ctor public AndroidUriHandler(android.content.Context context);
     method public void openUri(String uri);
@@ -2210,6 +2258,7 @@
     ctor public ComposeView(android.content.Context context);
     method @androidx.compose.runtime.Composable public void Content();
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    property protected boolean shouldCreateCompositionOnAttachedToWindow;
   }
 
   public final class DebugUtilsKt {
@@ -2260,10 +2309,6 @@
   public final class JvmActualsKt {
   }
 
-  public final class SubcompositionKt {
-    method @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-  }
-
   public final class TestTagKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier testTag(androidx.compose.ui.Modifier, String tag);
   }
@@ -2304,6 +2349,33 @@
     method public operator void set(String name, Object? value);
   }
 
+  public interface ViewCompositionStrategy {
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+    field public static final androidx.compose.ui.platform.ViewCompositionStrategy.Companion Companion;
+  }
+
+  public static final class ViewCompositionStrategy.Companion {
+  }
+
+  public static final class ViewCompositionStrategy.DisposeOnDetachedFromWindow implements androidx.compose.ui.platform.ViewCompositionStrategy {
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+    field public static final androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnDetachedFromWindow INSTANCE;
+  }
+
+  public static final class ViewCompositionStrategy.DisposeOnLifecycleDestroyed implements androidx.compose.ui.platform.ViewCompositionStrategy {
+    ctor public ViewCompositionStrategy.DisposeOnLifecycleDestroyed(androidx.lifecycle.Lifecycle lifecycle);
+    ctor public ViewCompositionStrategy.DisposeOnLifecycleDestroyed(androidx.lifecycle.LifecycleOwner lifecycleOwner);
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+  }
+
+  public static final class ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed implements androidx.compose.ui.platform.ViewCompositionStrategy {
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+    field public static final androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed INSTANCE;
+  }
+
+  public final class ViewCompositionStrategyKt {
+  }
+
   public interface ViewConfiguration {
     method public long getDoubleTapMinTime-ojFfpTE();
     method public long getDoubleTapTimeout-ojFfpTE();
@@ -2315,6 +2387,23 @@
     property public abstract float touchSlop;
   }
 
+  @VisibleForTesting public interface ViewRootForTest extends androidx.compose.ui.node.Owner {
+    method public boolean getHasPendingMeasureOrLayout();
+    method public android.view.View getView();
+    method public void invalidateDescendants();
+    method public boolean isLifecycleInResumedState();
+    property public abstract boolean hasPendingMeasureOrLayout;
+    property public abstract boolean isLifecycleInResumedState;
+    property public abstract android.view.View view;
+    field public static final androidx.compose.ui.platform.ViewRootForTest.Companion Companion;
+  }
+
+  public static final class ViewRootForTest.Companion {
+    method public kotlin.jvm.functions.Function1<androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? getOnViewCreatedCallback();
+    method public void setOnViewCreatedCallback(kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? p);
+    property public final kotlin.jvm.functions.Function1<androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? onViewCreatedCallback;
+  }
+
   @androidx.compose.runtime.Stable public interface WindowManager {
     method public boolean isWindowFocused();
     property public abstract boolean isWindowFocused;
@@ -2324,9 +2413,15 @@
     method @androidx.compose.runtime.Composable public static void WindowFocusObserver(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onWindowFocusChanged);
   }
 
+  public final class WindowRecomposerKt {
+    method public static androidx.compose.runtime.CompositionReference? findViewTreeCompositionReference(android.view.View);
+    method public static androidx.compose.runtime.CompositionReference? getCompositionReference(android.view.View);
+    method public static void setCompositionReference(android.view.View, androidx.compose.runtime.CompositionReference? value);
+  }
+
   public final class WrapperKt {
     method public static androidx.compose.runtime.Composition setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
     method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.app.Activity, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
   }
@@ -2410,7 +2505,7 @@
 
 package androidx.compose.ui.selection {
 
-  public interface Selectable {
+  @androidx.compose.ui.text.ExperimentalTextApi public interface Selectable {
     method public androidx.compose.ui.geometry.Rect getBoundingBox(int offset);
     method public long getHandlePosition-F1C5BW0(androidx.compose.ui.selection.Selection selection, boolean isStartHandle);
     method public androidx.compose.ui.layout.LayoutCoordinates? getLayoutCoordinates();
@@ -2460,9 +2555,12 @@
   public final class SelectionManagerKt {
   }
 
-  public interface SelectionRegistrar {
-    method public void onPositionChange();
-    method public void onUpdateSelection-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+  @androidx.compose.ui.text.ExperimentalTextApi public interface SelectionRegistrar {
+    method public void notifyPositionChange();
+    method public void notifySelectableChange(androidx.compose.ui.selection.Selectable selectable);
+    method public void notifySelectionUpdate-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+    method public void notifySelectionUpdateEnd();
+    method public void notifySelectionUpdateStart-YJiYy8w(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition);
     method public androidx.compose.ui.selection.Selectable subscribe(androidx.compose.ui.selection.Selectable selectable);
     method public void unsubscribe(androidx.compose.ui.selection.Selectable selectable);
   }
@@ -2610,8 +2708,9 @@
     method public androidx.compose.ui.geometry.Rect getGlobalBounds();
     method public long getGlobalPosition-F1C5BW0();
     method public int getId();
-    method public androidx.compose.ui.node.LayoutNode getLayoutNode();
+    method public androidx.compose.ui.layout.LayoutInfo getLayoutInfo();
     method public boolean getMergingEnabled();
+    method public androidx.compose.ui.node.Owner? getOwner();
     method public androidx.compose.ui.semantics.SemanticsNode? getParent();
     method public long getPositionInRoot-F1C5BW0();
     method public long getSize-YbymL2g();
@@ -2623,8 +2722,9 @@
     property public final long globalPosition;
     property public final int id;
     property public final boolean isRoot;
-    property public final androidx.compose.ui.node.LayoutNode layoutNode;
+    property public final androidx.compose.ui.layout.LayoutInfo layoutInfo;
     property public final boolean mergingEnabled;
+    property public final androidx.compose.ui.node.Owner? owner;
     property public final androidx.compose.ui.semantics.SemanticsNode? parent;
     property public final long positionInRoot;
     property public final long size;
@@ -2647,9 +2747,8 @@
   }
 
   public final class SemanticsProperties {
-    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getAccessibilityLabel();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityRangeInfo> getAccessibilityRangeInfo();
-    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getAccessibilityValue();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getContentDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getDisabled();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getFocused();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getHidden();
@@ -2658,14 +2757,14 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsDialog();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsPopup();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getSelected();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getStateDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getTestTag();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> getText();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> getTextSelectionRange();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> getToggleableState();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityScrollState> getVerticalAccessibilityScrollState();
-    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> AccessibilityLabel;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityRangeInfo> AccessibilityRangeInfo;
-    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> AccessibilityValue;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> ContentDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Disabled;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Focused;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Hidden;
@@ -2674,6 +2773,7 @@
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsDialog;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsPopup;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Selected;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> StateDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> TestTag;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> Text;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> TextSelectionRange;
@@ -2688,14 +2788,16 @@
     method public static void dialog(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void disabled(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void dismiss(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
-    method public static String getAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
-    method public static String getAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
-    method public static androidx.compose.ui.semantics.AccessibilityRangeInfo getAccessibilityValueRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method @Deprecated public static String getAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method @Deprecated public static String getAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static String getContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> getCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static boolean getFocused(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.semantics.AccessibilityScrollState getHorizontalAccessibilityScrollState(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.input.ImeAction getImeAction(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static boolean getSelected(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static String getStateDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static androidx.compose.ui.semantics.AccessibilityRangeInfo getStateDescriptionRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static String getTestTag(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.AnnotatedString getText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void getTextLayoutResult(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean> action);
@@ -2708,9 +2810,9 @@
     method public static void pasteText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public static void popup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean> action);
-    method public static void setAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
-    method public static void setAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
-    method public static void setAccessibilityValueRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.AccessibilityRangeInfo p);
+    method @Deprecated public static void setAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
+    method @Deprecated public static void setAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
+    method public static void setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver, java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> p);
     method public static void setFocused(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean p);
     method public static void setHorizontalAccessibilityScrollState(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.AccessibilityScrollState p);
@@ -2718,6 +2820,8 @@
     method public static void setProgress(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Boolean> action);
     method public static void setSelected(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean p);
     method public static void setSelection(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function3<? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Boolean,java.lang.Boolean> action);
+    method public static void setStateDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
+    method public static void setStateDescriptionRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.AccessibilityRangeInfo p);
     method public static void setTestTag(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString p);
     method public static void setText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString,java.lang.Boolean> action);
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 5d8e28b..88c1c65 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -132,26 +132,29 @@
     method @Deprecated public static androidx.compose.ui.Modifier drawWithContent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.ContentDrawScope,kotlin.Unit> onDraw);
   }
 
-  public final class FocusModifierKt {
-    method @androidx.compose.ui.focus.ExperimentalFocus public static androidx.compose.ui.Modifier focus(androidx.compose.ui.Modifier);
+  @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") public @interface ExperimentalComposeUiApi {
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public interface FocusObserverModifier extends androidx.compose.ui.Modifier.Element {
-    method public kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusState,kotlin.Unit> getOnFocusChange();
+  public final class FocusModifierKt {
+    method @Deprecated public static androidx.compose.ui.Modifier focus(androidx.compose.ui.Modifier);
+  }
+
+  @Deprecated public interface FocusObserverModifier extends androidx.compose.ui.Modifier.Element {
+    method @Deprecated public kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusState,kotlin.Unit> getOnFocusChange();
     property public abstract kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChange;
   }
 
   public final class FocusObserverModifierKt {
-    method @androidx.compose.ui.focus.ExperimentalFocus public static androidx.compose.ui.Modifier focusObserver(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChange);
+    method @Deprecated public static androidx.compose.ui.Modifier focusObserver(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChange);
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public interface FocusRequesterModifier extends androidx.compose.ui.Modifier.Element {
-    method public androidx.compose.ui.focus.FocusRequester getFocusRequester();
-    property public abstract androidx.compose.ui.focus.FocusRequester focusRequester;
+  @Deprecated public interface FocusRequesterModifier extends androidx.compose.ui.Modifier.Element {
+    method @Deprecated public androidx.compose.ui.focus.FocusReference getFocusReference();
+    property public abstract androidx.compose.ui.focus.FocusReference focusReference;
   }
 
   public final class FocusRequesterModifierKt {
-    method @androidx.compose.ui.focus.ExperimentalFocus public static androidx.compose.ui.Modifier focusRequester(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester);
+    method @Deprecated public static androidx.compose.ui.Modifier focusRequester(androidx.compose.ui.Modifier, Object focusRequester);
   }
 
   @androidx.compose.runtime.Stable public interface Modifier {
@@ -194,17 +197,17 @@
   public final class AndroidAutofillTypeKt {
   }
 
-  public interface Autofill {
+  @androidx.compose.ui.ExperimentalComposeUiApi public interface Autofill {
     method public void cancelAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
     method public void requestAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
   }
 
-  public final class AutofillNode {
+  @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillNode {
     ctor public AutofillNode(java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
     method public java.util.List<androidx.compose.ui.autofill.AutofillType> component1();
     method public androidx.compose.ui.geometry.Rect? component2();
     method public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? component3();
-    method public androidx.compose.ui.autofill.AutofillNode copy(java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
+    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.autofill.AutofillNode copy(java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
     method public java.util.List<androidx.compose.ui.autofill.AutofillType> getAutofillTypes();
     method public androidx.compose.ui.geometry.Rect? getBoundingBox();
     method public int getId();
@@ -216,7 +219,7 @@
     property public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? onFill;
   }
 
-  public final class AutofillTree {
+  @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillTree {
     ctor public AutofillTree();
     method public java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> getChildren();
     method public kotlin.Unit? performAutofill(int id, String value);
@@ -224,7 +227,7 @@
     property public final java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> children;
   }
 
-  public enum AutofillType {
+  @androidx.compose.ui.ExperimentalComposeUiApi public enum AutofillType {
     enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressAuxiliaryDetails;
     enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressCountry;
     enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressLocality;
@@ -328,27 +331,81 @@
 
 package androidx.compose.ui.focus {
 
-  @kotlin.RequiresOptIn(message="The Focus API is experimental and is likely to change in the future.") public @interface ExperimentalFocus {
+  public final class FocusChangedModifierKt {
+    method public static androidx.compose.ui.Modifier onFocusChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChanged);
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public interface FocusManager {
+  public interface FocusEventModifier extends androidx.compose.ui.Modifier.Element {
+    method public void onFocusEvent(androidx.compose.ui.focus.FocusState focusState);
+  }
+
+  public final class FocusEventModifierKt {
+    method public static androidx.compose.ui.Modifier onFocusEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusEvent);
+  }
+
+  public interface FocusManager {
     method public void clearFocus(optional boolean forcedClear);
   }
 
+  public final class FocusModifierKt {
+    method public static androidx.compose.ui.Modifier focusModifier(androidx.compose.ui.Modifier);
+  }
+
   public final class FocusNodeUtilsKt {
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public final class FocusRequester {
-    ctor public FocusRequester();
+  public final class FocusReference {
+    ctor public FocusReference();
     method public boolean captureFocus();
     method public boolean freeFocus();
     method public void requestFocus();
+    field public static final androidx.compose.ui.focus.FocusReference.Companion Companion;
   }
 
-  public final class FocusRequesterKt {
+  public static final class FocusReference.Companion {
+    method public androidx.compose.ui.focus.FocusReference.Companion.FocusReferenceFactory createRefs();
   }
 
-  @androidx.compose.ui.focus.ExperimentalFocus public enum FocusState {
+  public static final class FocusReference.Companion.FocusReferenceFactory {
+    method public operator androidx.compose.ui.focus.FocusReference component1();
+    method public operator androidx.compose.ui.focus.FocusReference component10();
+    method public operator androidx.compose.ui.focus.FocusReference component11();
+    method public operator androidx.compose.ui.focus.FocusReference component12();
+    method public operator androidx.compose.ui.focus.FocusReference component13();
+    method public operator androidx.compose.ui.focus.FocusReference component14();
+    method public operator androidx.compose.ui.focus.FocusReference component15();
+    method public operator androidx.compose.ui.focus.FocusReference component16();
+    method public operator androidx.compose.ui.focus.FocusReference component2();
+    method public operator androidx.compose.ui.focus.FocusReference component3();
+    method public operator androidx.compose.ui.focus.FocusReference component4();
+    method public operator androidx.compose.ui.focus.FocusReference component5();
+    method public operator androidx.compose.ui.focus.FocusReference component6();
+    method public operator androidx.compose.ui.focus.FocusReference component7();
+    method public operator androidx.compose.ui.focus.FocusReference component8();
+    method public operator androidx.compose.ui.focus.FocusReference component9();
+    field public static final androidx.compose.ui.focus.FocusReference.Companion.FocusReferenceFactory INSTANCE;
+  }
+
+  public final class FocusReferenceKt {
+  }
+
+  public interface FocusReferenceModifier extends androidx.compose.ui.Modifier.Element {
+    method public androidx.compose.ui.focus.FocusReference getFocusReference();
+    property public abstract androidx.compose.ui.focus.FocusReference focusReference;
+  }
+
+  public final class FocusReferenceModifierKt {
+    method public static androidx.compose.ui.Modifier focusReference(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusReference focusReference);
+  }
+
+  @Deprecated public final class FocusRequester {
+    ctor @Deprecated public FocusRequester();
+    method @Deprecated public boolean captureFocus();
+    method @Deprecated public boolean freeFocus();
+    method @Deprecated public void requestFocus();
+  }
+
+  public enum FocusState {
     enum_constant public static final androidx.compose.ui.focus.FocusState Active;
     enum_constant public static final androidx.compose.ui.focus.FocusState ActiveParent;
     enum_constant public static final androidx.compose.ui.focus.FocusState Captured;
@@ -410,9 +467,6 @@
     method public static androidx.compose.ui.Modifier dragSlopExceededGestureFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onDragSlopExceeded, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.gesture.Direction,java.lang.Boolean>? canDrag, optional androidx.compose.ui.gesture.scrollorientationlocking.Orientation? orientation);
   }
 
-  @kotlin.RequiresOptIn(message="This pointer input API is experimental and is likely to change before becoming " + "stable.") public @interface ExperimentalPointerInput {
-  }
-
   public final class GestureUtilsKt {
     method public static boolean anyPointersInBounds-5eFHUEc(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange>, long bounds);
   }
@@ -493,11 +547,11 @@
 
 package androidx.compose.ui.gesture.customevents {
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public final class DelayUpEvent implements androidx.compose.ui.input.pointer.CustomEvent {
+  public final class DelayUpEvent implements androidx.compose.ui.input.pointer.CustomEvent {
     ctor public DelayUpEvent(androidx.compose.ui.gesture.customevents.DelayUpMessage message, java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers);
     method public androidx.compose.ui.gesture.customevents.DelayUpMessage component1();
     method public java.util.Set<androidx.compose.ui.input.pointer.PointerId> component2();
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public androidx.compose.ui.gesture.customevents.DelayUpEvent copy(androidx.compose.ui.gesture.customevents.DelayUpMessage message, java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers);
+    method public androidx.compose.ui.gesture.customevents.DelayUpEvent copy(androidx.compose.ui.gesture.customevents.DelayUpMessage message, java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers);
     method public androidx.compose.ui.gesture.customevents.DelayUpMessage getMessage();
     method public java.util.Set<androidx.compose.ui.input.pointer.PointerId> getPointers();
     method public void setMessage(androidx.compose.ui.gesture.customevents.DelayUpMessage p);
@@ -505,7 +559,7 @@
     property public final java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointers;
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public enum DelayUpMessage {
+  public enum DelayUpMessage {
     enum_constant public static final androidx.compose.ui.gesture.customevents.DelayUpMessage DelayUp;
     enum_constant public static final androidx.compose.ui.gesture.customevents.DelayUpMessage DelayedUpConsumed;
     enum_constant public static final androidx.compose.ui.gesture.customevents.DelayUpMessage DelayedUpNotConsumed;
@@ -517,6 +571,37 @@
 
 }
 
+package androidx.compose.ui.gesture.nestedscroll {
+
+  public interface NestedScrollConnection {
+    method public default void onPostFling-Pv53iXo(long consumed, long available, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Velocity,kotlin.Unit> onFinished);
+    method public default long onPostScroll-l-UAZDg(long consumed, long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+    method public default long onPreFling-TH1AsA0(long available);
+    method public default long onPreScroll-vG6bCaM(long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+  }
+
+  public final class NestedScrollDelegatingWrapperKt {
+  }
+
+  public final class NestedScrollDispatcher {
+    ctor public NestedScrollDispatcher();
+    method public void dispatchPostFling-uYzo7IE(long consumed, long available);
+    method public long dispatchPostScroll-l-UAZDg(long consumed, long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+    method public long dispatchPreFling-TH1AsA0(long available);
+    method public long dispatchPreScroll-vG6bCaM(long available, androidx.compose.ui.gesture.nestedscroll.NestedScrollSource source);
+  }
+
+  public final class NestedScrollModifierKt {
+    method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.gesture.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.gesture.nestedscroll.NestedScrollDispatcher? dispatcher);
+  }
+
+  public enum NestedScrollSource {
+    enum_constant public static final androidx.compose.ui.gesture.nestedscroll.NestedScrollSource Drag;
+    enum_constant public static final androidx.compose.ui.gesture.nestedscroll.NestedScrollSource Fling;
+  }
+
+}
+
 package androidx.compose.ui.gesture.scrollorientationlocking {
 
   public enum Orientation {
@@ -524,7 +609,7 @@
     enum_constant public static final androidx.compose.ui.gesture.scrollorientationlocking.Orientation Vertical;
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public final class ScrollOrientationLocker {
+  public final class ScrollOrientationLocker {
     ctor public ScrollOrientationLocker(androidx.compose.ui.input.pointer.CustomEventDispatcher customEventDispatcher);
     method public void attemptToLockPointers(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> changes, androidx.compose.ui.gesture.scrollorientationlocking.Orientation orientation);
     method public java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> getPointersFor(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> changes, androidx.compose.ui.gesture.scrollorientationlocking.Orientation orientation);
@@ -812,18 +897,6 @@
 
 package androidx.compose.ui.input.key {
 
-  @Deprecated @androidx.compose.ui.input.key.ExperimentalKeyInput public interface Alt {
-    method @Deprecated public boolean isLeftAltPressed();
-    method @Deprecated public default boolean isPressed();
-    method @Deprecated public boolean isRightAltPressed();
-    property public abstract boolean isLeftAltPressed;
-    property public default boolean isPressed;
-    property public abstract boolean isRightAltPressed;
-  }
-
-  @kotlin.RequiresOptIn(message="The Key Input API is experimental and is likely to change in the future.") public @interface ExperimentalKeyInput {
-  }
-
   public final inline class Key {
     ctor public Key();
     method public static int constructor-impl(int keyCode);
@@ -1417,8 +1490,7 @@
     property public final int ZoomOut;
   }
 
-  @androidx.compose.ui.input.key.ExperimentalKeyInput public interface KeyEvent {
-    method @Deprecated public androidx.compose.ui.input.key.Alt getAlt();
+  public interface KeyEvent {
     method public int getKey-EK5gGoQ();
     method public androidx.compose.ui.input.key.KeyEventType getType();
     method public int getUtf16CodePoint();
@@ -1426,7 +1498,6 @@
     method public boolean isCtrlPressed();
     method public boolean isMetaPressed();
     method public boolean isShiftPressed();
-    property @Deprecated public abstract androidx.compose.ui.input.key.Alt alt;
     property public abstract boolean isAltPressed;
     property public abstract boolean isCtrlPressed;
     property public abstract boolean isMetaPressed;
@@ -1436,15 +1507,17 @@
     property public abstract int utf16CodePoint;
   }
 
-  @androidx.compose.ui.input.key.ExperimentalKeyInput public enum KeyEventType {
+  public enum KeyEventType {
     enum_constant public static final androidx.compose.ui.input.key.KeyEventType KeyDown;
     enum_constant public static final androidx.compose.ui.input.key.KeyEventType KeyUp;
     enum_constant public static final androidx.compose.ui.input.key.KeyEventType Unknown;
   }
 
   public final class KeyInputModifierKt {
-    method @androidx.compose.ui.input.key.ExperimentalKeyInput public static androidx.compose.ui.Modifier keyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onKeyEvent);
-    method @androidx.compose.ui.input.key.ExperimentalKeyInput public static androidx.compose.ui.Modifier previewKeyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreviewKeyEvent);
+    method @Deprecated public static androidx.compose.ui.Modifier keyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onKeyEvent);
+    method public static androidx.compose.ui.Modifier onKeyEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onKeyEvent);
+    method public static androidx.compose.ui.Modifier onPreviewKeyEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreviewKeyEvent);
+    method @Deprecated public static androidx.compose.ui.Modifier previewKeyInputFilter(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreviewKeyEvent);
   }
 
 }
@@ -1455,6 +1528,16 @@
     method public static boolean isMouseInput();
   }
 
+  @kotlin.coroutines.RestrictsSuspension public interface AwaitPointerEventScope extends androidx.compose.ui.unit.Density {
+    method public suspend Object? awaitPointerEvent(optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerEvent> p);
+    method public androidx.compose.ui.input.pointer.PointerEvent getCurrentEvent();
+    method public long getSize-YbymL2g();
+    method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
+    property public abstract androidx.compose.ui.input.pointer.PointerEvent currentEvent;
+    property public abstract long size;
+    property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
+  }
+
   public final class ConsumedData {
     method public boolean getDownChange();
     method public long getPositionChange-F1C5BW0();
@@ -1473,17 +1556,6 @@
     method public void retainHitPaths(java.util.Set<androidx.compose.ui.input.pointer.PointerId> pointerIds);
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput @kotlin.coroutines.RestrictsSuspension public interface HandlePointerInputScope extends androidx.compose.ui.unit.Density {
-    method public suspend Object? awaitCustomEvent(optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.CustomEvent> p);
-    method public suspend Object? awaitPointerEvent(optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerEvent> p);
-    method public androidx.compose.ui.input.pointer.PointerEvent getCurrentEvent();
-    method public long getSize-YbymL2g();
-    method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
-    property public abstract androidx.compose.ui.input.pointer.PointerEvent currentEvent;
-    property public abstract long size;
-    property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
-  }
-
   public final class HitPathTrackerKt {
   }
 
@@ -1601,12 +1673,11 @@
     property public abstract androidx.compose.ui.input.pointer.PointerInputFilter pointerInputFilter;
   }
 
-  @androidx.compose.ui.gesture.ExperimentalPointerInput public interface PointerInputScope extends androidx.compose.ui.unit.Density {
-    method public androidx.compose.ui.input.pointer.CustomEventDispatcher getCustomEventDispatcher();
+  public interface PointerInputScope extends androidx.compose.ui.unit.Density {
+    method public suspend <R> Object? awaitPointerEventScope(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R> p);
     method public long getSize-YbymL2g();
     method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
-    method public suspend <R> Object? handlePointerInput(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.HandlePointerInputScope,? super kotlin.coroutines.Continuation<? super R>,?> handler, kotlin.coroutines.Continuation<? super R> p);
-    property public abstract androidx.compose.ui.input.pointer.CustomEventDispatcher customEventDispatcher;
+    method @Deprecated public default suspend <R> Object? handlePointerInput(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super R>,?> handler, kotlin.coroutines.Continuation<? super R> p);
     property public abstract long size;
     property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
   }
@@ -1627,7 +1698,7 @@
   }
 
   public final class SuspendingPointerInputFilterKt {
-    method @androidx.compose.ui.gesture.ExperimentalPointerInput public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
   }
 
 }
@@ -1786,6 +1857,22 @@
     property public abstract Object layoutId;
   }
 
+  public interface LayoutInfo {
+    method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
+    method public int getHeight();
+    method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
+    method public androidx.compose.ui.layout.LayoutInfo? getParentInfo();
+    method public int getWidth();
+    method public boolean isAttached();
+    method public boolean isPlaced();
+    property public abstract androidx.compose.ui.layout.LayoutCoordinates coordinates;
+    property public abstract int height;
+    property public abstract boolean isAttached;
+    property public abstract boolean isPlaced;
+    property public abstract androidx.compose.ui.layout.LayoutInfo? parentInfo;
+    property public abstract int width;
+  }
+
   public final class LayoutKt {
     method @androidx.compose.runtime.Composable public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> minIntrinsicWidthMeasureBlock, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> minIntrinsicHeightMeasureBlock, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> maxIntrinsicWidthMeasureBlock, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntrinsicMeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>,? super java.lang.Integer,java.lang.Integer> maxIntrinsicHeightMeasureBlock, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.MeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.Measurable>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
     method @androidx.compose.runtime.Composable public static void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> content, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.MeasureScope,? super java.util.List<? extends androidx.compose.ui.layout.Measurable>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
@@ -1842,6 +1929,16 @@
     method public static inline String! toString-impl(androidx.compose.ui.layout.Placeable! p);
   }
 
+  public final class ModifierInfo {
+    ctor public ModifierInfo(androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.LayoutCoordinates coordinates, Object? extra);
+    method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
+    method public Object? getExtra();
+    method public androidx.compose.ui.Modifier getModifier();
+    property public final androidx.compose.ui.layout.LayoutCoordinates coordinates;
+    property public final Object? extra;
+    property public final androidx.compose.ui.Modifier modifier;
+  }
+
   public interface OnGloballyPositionedModifier extends androidx.compose.ui.Modifier.Element {
     method public void onGloballyPositioned(androidx.compose.ui.layout.LayoutCoordinates coordinates);
   }
@@ -1934,7 +2031,10 @@
   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 public static inline boolean isSpecified-FK8aYYs(long);
+    method public static inline boolean isUnspecified-FK8aYYs(long);
     method @androidx.compose.runtime.Stable public static long lerp-bKVCie4(long start, long stop, float fraction);
+    method public static inline long takeOrElse-L-byAFk(long, kotlin.jvm.functions.Function0<androidx.compose.ui.layout.ScaleFactor> block);
     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);
   }
@@ -1947,6 +2047,9 @@
     method public java.util.List<androidx.compose.ui.layout.Measurable> subcompose(Object? slotId, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class TestModifierUpdaterKt {
+  }
+
   public final class VerticalAlignmentLine extends androidx.compose.ui.layout.AlignmentLine {
     ctor public VerticalAlignmentLine(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,java.lang.Integer> merger);
   }
@@ -1986,25 +2089,15 @@
     property public final kotlin.jvm.functions.Function2<androidx.compose.ui.node.LayoutNode,androidx.compose.ui.node.Ref<androidx.compose.ui.node.LayoutNode>,kotlin.Unit> setRef;
   }
 
-  public final class LayoutNode implements androidx.compose.ui.layout.Measurable androidx.compose.ui.node.OwnerScope androidx.compose.ui.layout.Remeasurement {
-    ctor public LayoutNode();
-    method public void attach(androidx.compose.ui.node.Owner owner);
-    method public void detach();
+  public final class LayoutNode implements androidx.compose.ui.layout.LayoutInfo androidx.compose.ui.layout.Measurable androidx.compose.ui.node.OwnerScope androidx.compose.ui.layout.Remeasurement {
     method public void forceRemeasure();
-    method @Deprecated public boolean getCanMultiMeasure();
-    method public java.util.List<androidx.compose.ui.node.LayoutNode> getChildren();
     method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
-    method public androidx.compose.ui.unit.Density getDensity();
-    method public int getDepth();
     method public int getHeight();
-    method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
-    method public androidx.compose.ui.node.MeasureBlocks getMeasureBlocks();
-    method public androidx.compose.ui.Modifier getModifier();
-    method public java.util.List<androidx.compose.ui.node.ModifierInfo> getModifierInfo();
-    method public androidx.compose.ui.node.Owner? getOwner();
-    method public androidx.compose.ui.node.LayoutNode? getParent();
+    method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
     method public Object? getParentData();
+    method public androidx.compose.ui.layout.LayoutInfo? getParentInfo();
     method public int getWidth();
+    method public boolean isAttached();
     method public boolean isPlaced();
     method public boolean isValid();
     method public int maxIntrinsicHeight(int width);
@@ -2012,28 +2105,14 @@
     method public androidx.compose.ui.layout.Placeable measure-BRTryo0(long constraints);
     method public int minIntrinsicHeight(int width);
     method public int minIntrinsicWidth(int height);
-    method public void place(int x, int y);
-    method @Deprecated public void setCanMultiMeasure(boolean p);
-    method public void setDensity(androidx.compose.ui.unit.Density p);
-    method public void setDepth(int p);
-    method public void setLayoutDirection(androidx.compose.ui.unit.LayoutDirection value);
-    method public void setMeasureBlocks(androidx.compose.ui.node.MeasureBlocks value);
-    method public void setModifier(androidx.compose.ui.Modifier value);
-    property @Deprecated public final boolean canMultiMeasure;
-    property public final java.util.List<androidx.compose.ui.node.LayoutNode> children;
-    property public final androidx.compose.ui.layout.LayoutCoordinates coordinates;
-    property public final androidx.compose.ui.unit.Density density;
-    property public final int depth;
-    property public final int height;
-    property public final boolean isPlaced;
+    property public androidx.compose.ui.layout.LayoutCoordinates coordinates;
+    property public int height;
+    property public boolean isAttached;
+    property public boolean isPlaced;
     property public boolean isValid;
-    property public final androidx.compose.ui.unit.LayoutDirection layoutDirection;
-    property public final androidx.compose.ui.node.MeasureBlocks measureBlocks;
-    property public final androidx.compose.ui.Modifier modifier;
-    property public final androidx.compose.ui.node.Owner? owner;
-    property public final androidx.compose.ui.node.LayoutNode? parent;
     property public Object? parentData;
-    property public final int width;
+    property public androidx.compose.ui.layout.LayoutInfo? parentInfo;
+    property public int width;
   }
 
   public final class LayoutNodeKt {
@@ -2047,16 +2126,6 @@
     method public int minIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope intrinsicMeasureScope, java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable> measurables, int h);
   }
 
-  public final class ModifierInfo {
-    ctor public ModifierInfo(androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.LayoutCoordinates coordinates, Object? extra);
-    method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
-    method public Object? getExtra();
-    method public androidx.compose.ui.Modifier getModifier();
-    property public final androidx.compose.ui.layout.LayoutCoordinates coordinates;
-    property public final Object? extra;
-    property public final androidx.compose.ui.Modifier modifier;
-  }
-
   public interface OwnedLayer {
     method public void destroy();
     method public void drawLayer(androidx.compose.ui.graphics.Canvas canvas);
@@ -2080,7 +2149,6 @@
     method public androidx.compose.ui.focus.FocusManager getFocusManager();
     method public androidx.compose.ui.text.font.Font.ResourceLoader getFontLoader();
     method public androidx.compose.ui.hapticfeedback.HapticFeedback getHapticFeedBack();
-    method public boolean getHasPendingMeasureOrLayout();
     method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
     method public long getMeasureIteration();
     method public androidx.compose.ui.node.LayoutNode getRoot();
@@ -2094,11 +2162,12 @@
     method public void measureAndLayout();
     method public void onAttach(androidx.compose.ui.node.LayoutNode node);
     method public void onDetach(androidx.compose.ui.node.LayoutNode node);
+    method public void onLayoutChange(androidx.compose.ui.node.LayoutNode layoutNode);
     method public void onRequestMeasure(androidx.compose.ui.node.LayoutNode layoutNode);
     method public void onRequestRelayout(androidx.compose.ui.node.LayoutNode layoutNode);
     method public void onSemanticsChange();
     method public boolean requestFocus();
-    method @androidx.compose.ui.input.key.ExperimentalKeyInput public boolean sendKeyEvent(androidx.compose.ui.input.key.KeyEvent keyEvent);
+    method public boolean sendKeyEvent(androidx.compose.ui.input.key.KeyEvent keyEvent);
     property public abstract androidx.compose.ui.autofill.Autofill? autofill;
     property public abstract androidx.compose.ui.autofill.AutofillTree autofillTree;
     property public abstract androidx.compose.ui.platform.ClipboardManager clipboardManager;
@@ -2106,7 +2175,6 @@
     property public abstract androidx.compose.ui.focus.FocusManager focusManager;
     property public abstract androidx.compose.ui.text.font.Font.ResourceLoader fontLoader;
     property public abstract androidx.compose.ui.hapticfeedback.HapticFeedback hapticFeedBack;
-    property public abstract boolean hasPendingMeasureOrLayout;
     property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
     property public abstract long measureIteration;
     property public abstract androidx.compose.ui.node.LayoutNode root;
@@ -2165,10 +2233,14 @@
     method @androidx.compose.runtime.Composable public abstract void Content();
     method public final void createComposition();
     method public final void disposeComposition();
-    method public final boolean isDisposed();
+    method public final boolean getHasComposition();
+    method protected boolean getShouldCreateCompositionOnAttachedToWindow();
     method protected final void onLayout(boolean changed, int left, int top, int right, int bottom);
     method protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec);
-    property public final boolean isDisposed;
+    method public final void setParentCompositionReference(androidx.compose.runtime.CompositionReference? parent);
+    method public final void setViewCompositionStrategy(androidx.compose.ui.platform.ViewCompositionStrategy strategy);
+    property public final boolean hasComposition;
+    property protected boolean shouldCreateCompositionOnAttachedToWindow;
   }
 
   public final class AmbientsKt {
@@ -2186,8 +2258,6 @@
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.platform.ViewConfiguration> getAmbientViewConfiguration();
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.platform.WindowManager> getAmbientWindowManager();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.animation.core.AnimationClockObservable>! getAnimationClockAmbient();
-    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.autofill.Autofill>! getAutofillAmbient();
-    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.autofill.AutofillTree>! getAutofillTreeAmbient();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.platform.ClipboardManager>! getClipboardManagerAmbient();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.unit.Density>! getDensityAmbient();
     method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.focus.FocusManager>! getFocusManagerAmbient();
@@ -2216,34 +2286,12 @@
   public final class AndroidClipboardManagerKt {
   }
 
+  public final class AndroidComposeViewAccessibilityDelegateCompatKt {
+  }
+
   public final class AndroidComposeViewKt {
   }
 
-  public interface AndroidOwner extends androidx.compose.ui.node.Owner {
-    method public void addAndroidView(androidx.compose.ui.viewinterop.AndroidViewHolder view, androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void drawAndroidView(androidx.compose.ui.viewinterop.AndroidViewHolder view, android.graphics.Canvas canvas);
-    method public kotlin.jvm.functions.Function1<android.content.res.Configuration,kotlin.Unit> getConfigurationChangeObserver();
-    method public android.view.View getView();
-    method public androidx.compose.ui.platform.AndroidOwner.ViewTreeOwners? getViewTreeOwners();
-    method public void invalidateDescendants();
-    method public void removeAndroidView(androidx.compose.ui.viewinterop.AndroidViewHolder view);
-    method public void setConfigurationChangeObserver(kotlin.jvm.functions.Function1<? super android.content.res.Configuration,kotlin.Unit> p);
-    method public void setOnViewTreeOwnersAvailable(kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.AndroidOwner.ViewTreeOwners,kotlin.Unit> callback);
-    property public abstract kotlin.jvm.functions.Function1<android.content.res.Configuration,kotlin.Unit> configurationChangeObserver;
-    property public abstract android.view.View view;
-    property public abstract androidx.compose.ui.platform.AndroidOwner.ViewTreeOwners? viewTreeOwners;
-  }
-
-  public static final class AndroidOwner.ViewTreeOwners {
-    ctor public AndroidOwner.ViewTreeOwners(androidx.lifecycle.LifecycleOwner lifecycleOwner, androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, androidx.savedstate.SavedStateRegistryOwner savedStateRegistryOwner);
-    method public androidx.lifecycle.LifecycleOwner getLifecycleOwner();
-    method public androidx.savedstate.SavedStateRegistryOwner getSavedStateRegistryOwner();
-    method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner();
-    property public final androidx.lifecycle.LifecycleOwner lifecycleOwner;
-    property public final androidx.savedstate.SavedStateRegistryOwner savedStateRegistryOwner;
-    property public final androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner;
-  }
-
   public final class AndroidUriHandler implements androidx.compose.ui.platform.UriHandler {
     ctor public AndroidUriHandler(android.content.Context context);
     method public void openUri(String uri);
@@ -2272,6 +2320,7 @@
     ctor public ComposeView(android.content.Context context);
     method @androidx.compose.runtime.Composable public void Content();
     method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    property protected boolean shouldCreateCompositionOnAttachedToWindow;
   }
 
   public final class DebugUtilsKt {
@@ -2322,10 +2371,6 @@
   public final class JvmActualsKt {
   }
 
-  public final class SubcompositionKt {
-    method @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
-  }
-
   public final class TestTagKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier testTag(androidx.compose.ui.Modifier, String tag);
   }
@@ -2366,6 +2411,33 @@
     method public operator void set(String name, Object? value);
   }
 
+  public interface ViewCompositionStrategy {
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+    field public static final androidx.compose.ui.platform.ViewCompositionStrategy.Companion Companion;
+  }
+
+  public static final class ViewCompositionStrategy.Companion {
+  }
+
+  public static final class ViewCompositionStrategy.DisposeOnDetachedFromWindow implements androidx.compose.ui.platform.ViewCompositionStrategy {
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+    field public static final androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnDetachedFromWindow INSTANCE;
+  }
+
+  public static final class ViewCompositionStrategy.DisposeOnLifecycleDestroyed implements androidx.compose.ui.platform.ViewCompositionStrategy {
+    ctor public ViewCompositionStrategy.DisposeOnLifecycleDestroyed(androidx.lifecycle.Lifecycle lifecycle);
+    ctor public ViewCompositionStrategy.DisposeOnLifecycleDestroyed(androidx.lifecycle.LifecycleOwner lifecycleOwner);
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+  }
+
+  public static final class ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed implements androidx.compose.ui.platform.ViewCompositionStrategy {
+    method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+    field public static final androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed INSTANCE;
+  }
+
+  public final class ViewCompositionStrategyKt {
+  }
+
   public interface ViewConfiguration {
     method public long getDoubleTapMinTime-ojFfpTE();
     method public long getDoubleTapTimeout-ojFfpTE();
@@ -2377,6 +2449,23 @@
     property public abstract float touchSlop;
   }
 
+  @VisibleForTesting public interface ViewRootForTest extends androidx.compose.ui.node.Owner {
+    method public boolean getHasPendingMeasureOrLayout();
+    method public android.view.View getView();
+    method public void invalidateDescendants();
+    method public boolean isLifecycleInResumedState();
+    property public abstract boolean hasPendingMeasureOrLayout;
+    property public abstract boolean isLifecycleInResumedState;
+    property public abstract android.view.View view;
+    field public static final androidx.compose.ui.platform.ViewRootForTest.Companion Companion;
+  }
+
+  public static final class ViewRootForTest.Companion {
+    method public kotlin.jvm.functions.Function1<androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? getOnViewCreatedCallback();
+    method public void setOnViewCreatedCallback(kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? p);
+    property public final kotlin.jvm.functions.Function1<androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? onViewCreatedCallback;
+  }
+
   @androidx.compose.runtime.Stable public interface WindowManager {
     method public boolean isWindowFocused();
     property public abstract boolean isWindowFocused;
@@ -2386,9 +2475,15 @@
     method @androidx.compose.runtime.Composable public static void WindowFocusObserver(kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onWindowFocusChanged);
   }
 
+  public final class WindowRecomposerKt {
+    method public static androidx.compose.runtime.CompositionReference? findViewTreeCompositionReference(android.view.View);
+    method public static androidx.compose.runtime.CompositionReference? getCompositionReference(android.view.View);
+    method public static void setCompositionReference(android.view.View, androidx.compose.runtime.CompositionReference? value);
+  }
+
   public final class WrapperKt {
     method public static androidx.compose.runtime.Composition setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
     method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.app.Activity, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
   }
@@ -2472,7 +2567,7 @@
 
 package androidx.compose.ui.selection {
 
-  public interface Selectable {
+  @androidx.compose.ui.text.ExperimentalTextApi public interface Selectable {
     method public androidx.compose.ui.geometry.Rect getBoundingBox(int offset);
     method public long getHandlePosition-F1C5BW0(androidx.compose.ui.selection.Selection selection, boolean isStartHandle);
     method public androidx.compose.ui.layout.LayoutCoordinates? getLayoutCoordinates();
@@ -2522,9 +2617,12 @@
   public final class SelectionManagerKt {
   }
 
-  public interface SelectionRegistrar {
-    method public void onPositionChange();
-    method public void onUpdateSelection-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+  @androidx.compose.ui.text.ExperimentalTextApi public interface SelectionRegistrar {
+    method public void notifyPositionChange();
+    method public void notifySelectableChange(androidx.compose.ui.selection.Selectable selectable);
+    method public void notifySelectionUpdate-rULFVbc(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition, long endPosition);
+    method public void notifySelectionUpdateEnd();
+    method public void notifySelectionUpdateStart-YJiYy8w(androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates, long startPosition);
     method public androidx.compose.ui.selection.Selectable subscribe(androidx.compose.ui.selection.Selectable selectable);
     method public void unsubscribe(androidx.compose.ui.selection.Selectable selectable);
   }
@@ -2672,8 +2770,9 @@
     method public androidx.compose.ui.geometry.Rect getGlobalBounds();
     method public long getGlobalPosition-F1C5BW0();
     method public int getId();
-    method public androidx.compose.ui.node.LayoutNode getLayoutNode();
+    method public androidx.compose.ui.layout.LayoutInfo getLayoutInfo();
     method public boolean getMergingEnabled();
+    method public androidx.compose.ui.node.Owner? getOwner();
     method public androidx.compose.ui.semantics.SemanticsNode? getParent();
     method public long getPositionInRoot-F1C5BW0();
     method public long getSize-YbymL2g();
@@ -2685,8 +2784,9 @@
     property public final long globalPosition;
     property public final int id;
     property public final boolean isRoot;
-    property public final androidx.compose.ui.node.LayoutNode layoutNode;
+    property public final androidx.compose.ui.layout.LayoutInfo layoutInfo;
     property public final boolean mergingEnabled;
+    property public final androidx.compose.ui.node.Owner? owner;
     property public final androidx.compose.ui.semantics.SemanticsNode? parent;
     property public final long positionInRoot;
     property public final long size;
@@ -2709,9 +2809,8 @@
   }
 
   public final class SemanticsProperties {
-    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getAccessibilityLabel();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityRangeInfo> getAccessibilityRangeInfo();
-    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getAccessibilityValue();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getContentDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getDisabled();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getFocused();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getHidden();
@@ -2720,14 +2819,14 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsDialog();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsPopup();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getSelected();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getStateDescription();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getTestTag();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> getText();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> getTextSelectionRange();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> getToggleableState();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityScrollState> getVerticalAccessibilityScrollState();
-    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> AccessibilityLabel;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityRangeInfo> AccessibilityRangeInfo;
-    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> AccessibilityValue;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> ContentDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Disabled;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Focused;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Hidden;
@@ -2736,6 +2835,7 @@
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsDialog;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsPopup;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Selected;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> StateDescription;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> TestTag;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> Text;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> TextSelectionRange;
@@ -2750,14 +2850,16 @@
     method public static void dialog(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void disabled(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void dismiss(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
-    method public static String getAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
-    method public static String getAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
-    method public static androidx.compose.ui.semantics.AccessibilityRangeInfo getAccessibilityValueRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method @Deprecated public static String getAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method @Deprecated public static String getAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static String getContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> getCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static boolean getFocused(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.semantics.AccessibilityScrollState getHorizontalAccessibilityScrollState(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.input.ImeAction getImeAction(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static boolean getSelected(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static String getStateDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+    method public static androidx.compose.ui.semantics.AccessibilityRangeInfo getStateDescriptionRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static String getTestTag(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static androidx.compose.ui.text.AnnotatedString getText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void getTextLayoutResult(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean> action);
@@ -2770,9 +2872,9 @@
     method public static void pasteText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public static void popup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean> action);
-    method public static void setAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
-    method public static void setAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
-    method public static void setAccessibilityValueRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.AccessibilityRangeInfo p);
+    method @Deprecated public static void setAccessibilityLabel(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
+    method @Deprecated public static void setAccessibilityValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
+    method public static void setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver, java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> p);
     method public static void setFocused(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean p);
     method public static void setHorizontalAccessibilityScrollState(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.AccessibilityScrollState p);
@@ -2780,6 +2882,8 @@
     method public static void setProgress(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Boolean> action);
     method public static void setSelected(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean p);
     method public static void setSelection(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function3<? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Boolean,java.lang.Boolean> action);
+    method public static void setStateDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
+    method public static void setStateDescriptionRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.AccessibilityRangeInfo p);
     method public static void setTestTag(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String p);
     method public static void setText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString p);
     method public static void setText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString,java.lang.Boolean> action);
diff --git a/compose/ui/ui/integration-tests/ui-demos/build.gradle b/compose/ui/ui/integration-tests/ui-demos/build.gradle
index eb67fb8..9a5536b 100644
--- a/compose/ui/ui/integration-tests/ui-demos/build.gradle
+++ b/compose/ui/ui/integration-tests/ui-demos/build.gradle
@@ -18,6 +18,7 @@
     implementation project(":compose:foundation:foundation")
     implementation project(":compose:foundation:foundation-layout")
     implementation project(":compose:integration-tests:demos:common")
+    implementation project(":compose:ui:ui:ui-samples")
     implementation project(":compose:material:material")
     implementation project(":compose:runtime:runtime")
     implementation project(":compose:runtime:runtime-livedata")
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
index 021cf64..5a1de0d 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.ui.demos.autofill.ExplicitAutofillTypesDemo
 import androidx.compose.ui.demos.focus.FocusableDemo
-import androidx.compose.ui.demos.focus.ReuseFocusRequester
+import androidx.compose.ui.demos.focus.ReuseFocusReference
 import androidx.compose.ui.demos.gestures.DoubleTapGestureFilterDemo
 import androidx.compose.ui.demos.gestures.DoubleTapInTapDemo
 import androidx.compose.ui.demos.gestures.DragAndScaleGestureFilterDemo
@@ -45,6 +45,7 @@
 import androidx.compose.integration.demos.common.DemoCategory
 import androidx.compose.ui.demos.focus.FocusInDialog
 import androidx.compose.ui.demos.focus.FocusInPopup
+import androidx.compose.ui.samples.NestedScrollSample
 
 private val GestureDemos = DemoCategory(
     "Gestures",
@@ -87,7 +88,8 @@
                 ComposableDemo("Nested Long Press") { NestedLongPressDemo() },
                 ComposableDemo("Pointer Input During Sub Comp") { PointerInputDuringSubComp() }
             )
-        )
+        ),
+        ComposableDemo("New nested scroll") { NestedScrollSample() }
     )
 )
 
@@ -97,7 +99,7 @@
         ComposableDemo("Focusable Siblings") { FocusableDemo() },
         ComposableDemo("Focus Within Dialog") { FocusInDialog() },
         ComposableDemo("Focus Within Popup") { FocusInPopup() },
-        ComposableDemo("Reuse Focus Requester") { ReuseFocusRequester() }
+        ComposableDemo("Reuse Focus Reference") { ReuseFocusReference() }
     )
 )
 
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/VectorGraphicsDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/VectorGraphicsDemo.kt
index d225bf9..8465693 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/VectorGraphicsDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/VectorGraphicsDemo.kt
@@ -24,12 +24,11 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.HorizontalGradient
-import androidx.compose.ui.graphics.RadialGradient
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.TileMode
-import androidx.compose.ui.graphics.VerticalGradient
 import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.graphics.vector.Group
 import androidx.compose.ui.graphics.vector.Path
@@ -104,7 +103,7 @@
                     close()
                 }
                 Path(
-                    fill = HorizontalGradient(
+                    fill = Brush.horizontalGradient(
                         listOf(
                             Color.Red,
                             Color.Blue
@@ -130,12 +129,13 @@
     }
 
     Path(
-        fill = VerticalGradient(
+        fill = Brush.verticalGradient(
             0.0f to Color.Cyan,
             0.3f to Color.Green,
             1.0f to Color.Magenta,
             startY = 0.0f,
-            endY = vectorHeight
+            endY = vectorHeight,
+            tileMode = TileMode.Clamp
         ),
         pathData = background
     )
@@ -145,14 +145,13 @@
 private fun Triangle() {
     val length = 150.0f
     Path(
-        fill = RadialGradient(
+        fill = Brush.radialGradient(
             listOf(
                 Color(0xFF000080),
                 Color(0xFF808000),
                 Color(0xFF008080)
             ),
-            centerX = length / 2.0f,
-            centerY = length / 2.0f,
+            Offset(length / 2.0f, length / 2.0f),
             radius = length / 2.0f,
             tileMode = TileMode.Repeated
         ),
@@ -170,13 +169,13 @@
     val side1 = 150.0f
     val side2 = 150.0f
     Path(
-        fill = RadialGradient(
+        fill = Brush.radialGradient(
             0.0f to Color(0xFF800000),
             0.3f to Color.Cyan,
             0.8f to Color.Yellow,
-            centerX = side1 / 2.0f,
-            centerY = side2 / 2.0f,
-            radius = side1 / 2.0f
+            center = Offset(side1 / 2.0f, side2 / 2.0f),
+            radius = side1 / 2.0f,
+            tileMode = TileMode.Clamp
         ),
         pathData = PathData {
             horizontalLineToRelative(side1)
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/ExplicitAutofillTypesDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/ExplicitAutofillTypesDemo.kt
index 9a32990..6ae1e6e 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/ExplicitAutofillTypesDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/ExplicitAutofillTypesDemo.kt
@@ -29,12 +29,12 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.autofill.AutofillNode
 import androidx.compose.ui.autofill.AutofillType
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focusObserver
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.toComposeRect
 import androidx.compose.ui.layout.LayoutCoordinates
@@ -46,10 +46,7 @@
 import androidx.compose.ui.unit.dp
 
 @Composable
-@OptIn(
-    ExperimentalFocus::class,
-    ExperimentalFoundationApi::class
-)
+@OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class)
 fun ExplicitAutofillTypesDemo() {
     Column {
         val nameState = remember { mutableStateOf("Enter name here") }
@@ -64,7 +61,7 @@
             onFill = { nameState.value = it }
         ) { autofillNode ->
             BasicTextField(
-                modifier = Modifier.focusObserver {
+                modifier = Modifier.onFocusChanged {
                     autofill?.apply {
                         if (it.isFocused) {
                             requestAutofillForNode(autofillNode)
@@ -91,7 +88,7 @@
             onFill = { emailState.value = it }
         ) { autofillNode ->
             BasicTextField(
-                modifier = Modifier.focusObserver {
+                modifier = Modifier.onFocusChanged {
                     autofill?.run {
                         if (it.isFocused) {
                             requestAutofillForNode(autofillNode)
@@ -112,6 +109,7 @@
     }
 }
 
+@ExperimentalComposeUiApi
 @Composable
 private fun Autofill(
     autofillTypes: List<AutofillType>,
@@ -139,4 +137,4 @@
         x.toInt() + size.width,
         y.toInt() + size.height
     )
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/focus/FocusableDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/focus/FocusableDemo.kt
index efbbc4e..bd06873 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/focus/FocusableDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/focus/FocusableDemo.kt
@@ -27,12 +27,11 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusModifier
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.gesture.tapGestureFilter
 import androidx.compose.ui.graphics.Color.Companion.Black
 import androidx.compose.ui.graphics.Color.Companion.Green
@@ -58,16 +57,15 @@
 }
 
 @Composable
-@OptIn(ExperimentalFocus::class)
 private fun FocusableText(text: String) {
     var color by remember { mutableStateOf(Black) }
-    val focusRequester = FocusRequester()
+    val focusReference = FocusReference()
     Text(
         modifier = Modifier
-            .focusRequester(focusRequester)
-            .focusObserver { color = if (it.isFocused) Green else Black }
-            .focus()
-            .tapGestureFilter { focusRequester.requestFocus() },
+            .focusReference(focusReference)
+            .onFocusChanged { color = if (it.isFocused) Green else Black }
+            .focusModifier()
+            .tapGestureFilter { focusReference.requestFocus() },
         text = text,
         color = color
     )
@@ -80,4 +78,4 @@
         horizontalArrangement = Arrangement.Center,
         content = content
     )
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/focus/ReuseFocusRequester.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/focus/ReuseFocusReference.kt
similarity index 80%
rename from compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/focus/ReuseFocusRequester.kt
rename to compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/focus/ReuseFocusReference.kt
index f0998a0..c091c1c 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/focus/ReuseFocusRequester.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/focus/ReuseFocusReference.kt
@@ -29,12 +29,11 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusModifier
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.gesture.tapGestureFilter
@@ -44,8 +43,7 @@
 private enum class CurrentShape { Circle, Square }
 
 @Composable
-@OptIn(ExperimentalFocus::class)
-fun ReuseFocusRequester() {
+fun ReuseFocusReference() {
     Column(
         verticalArrangement = Arrangement.Top
     ) {
@@ -54,21 +52,21 @@
                 "another shape. The same focus requester is used for both shapes."
         )
 
-        // Shared focus requester.
-        val focusRequester = FocusRequester()
+        // Shared focus reference.
+        val focusReference = FocusReference()
 
         var shape by remember { mutableStateOf(CurrentShape.Square) }
         when (shape) {
             CurrentShape.Circle -> Circle(
                 modifier = Modifier
-                    .focusRequester(focusRequester)
-                    .tapGestureFilter { focusRequester.requestFocus() },
+                    .focusReference(focusReference)
+                    .tapGestureFilter { focusReference.requestFocus() },
                 nextShape = { shape = CurrentShape.Square }
             )
             CurrentShape.Square -> Square(
                 modifier = Modifier
-                    .focusRequester(focusRequester)
-                    .tapGestureFilter { focusRequester.requestFocus() },
+                    .focusReference(focusReference)
+                    .tapGestureFilter { focusReference.requestFocus() },
                 nextShape = { shape = CurrentShape.Circle }
             )
         }
@@ -76,7 +74,6 @@
 }
 
 @Composable
-@OptIn(ExperimentalFocus::class)
 private fun Circle(modifier: Modifier = Modifier, nextShape: () -> Unit) {
     var isFocused by remember { mutableStateOf(false) }
     val scale = animate(if (isFocused) 0f else 1f, TweenSpec(2000)) {
@@ -87,9 +84,9 @@
     val radius = size / 2
     Canvas(
         modifier
-            .focusObserver { isFocused = it.isFocused }
+            .onFocusChanged { isFocused = it.isFocused }
             .fillMaxSize()
-            .focus()
+            .focusModifier()
     ) {
         drawCircle(
             color = if (isFocused) Color.Red else Color.Blue,
@@ -100,7 +97,6 @@
 }
 
 @Composable
-@OptIn(ExperimentalFocus::class)
 private fun Square(modifier: Modifier = Modifier, nextShape: () -> Unit) {
     var isFocused by remember { mutableStateOf(false) }
     val scale = animate(if (isFocused) 0f else 1f, TweenSpec(2000)) {
@@ -111,9 +107,9 @@
     val side = size
     Canvas(
         modifier
-            .focusObserver { isFocused = it.isFocused }
+            .onFocusChanged { isFocused = it.isFocused }
             .fillMaxSize()
-            .focus()
+            .focusModifier()
     ) {
         drawRect(
             color = if (isFocused) Color.Red else Color.Blue,
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt
index d459774..d6641f8 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt
@@ -25,6 +25,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
@@ -63,7 +64,7 @@
 @Composable
 private fun PressableContainer(
     modifier: Modifier = Modifier,
-    content: @Composable () -> Unit = {}
+    content: @Composable () -> Unit = emptyContent()
 ) {
     val defaultColor = DefaultBackgroundColor
     val pressedColor = PressedColor
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/PointerInputDuringSubCompDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/PointerInputDuringSubCompDemo.kt
index f6ea061..8901b03 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/PointerInputDuringSubCompDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/PointerInputDuringSubCompDemo.kt
@@ -23,7 +23,7 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
-import androidx.compose.foundation.lazy.LazyColumnFor
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
@@ -60,27 +60,28 @@
                 "it is actually a new item that has not been hit tested yet.  If you keep " +
                 "your finger there and then add more fingers, it will track those new fingers."
         )
-        LazyColumnFor(
-            List(100) { index -> index },
+        LazyColumn(
             Modifier
                 .fillMaxSize()
                 .wrapContentSize(Alignment.Center)
                 .size(200.dp)
                 .background(color = Color.White)
         ) {
-            val pointerCount = remember { mutableStateOf(0) }
+            items(List(100) { index -> index }) {
+                val pointerCount = remember { mutableStateOf(0) }
 
-            Box(
-                Modifier.fillParentMaxSize()
-                    .border(width = 1.dp, color = Color.Black)
-                    .pointerCounterGestureFilter { newCount -> pointerCount.value = newCount },
-                contentAlignment = Alignment.Center
-            ) {
-                Text(
-                    "${pointerCount.value}",
-                    fontSize = TextUnit.Em(16),
-                    color = Color.Black
-                )
+                Box(
+                    Modifier.fillParentMaxSize()
+                        .border(width = 1.dp, color = Color.Black)
+                        .pointerCounterGestureFilter { newCount -> pointerCount.value = newCount },
+                    contentAlignment = Alignment.Center
+                ) {
+                    Text(
+                        "${pointerCount.value}",
+                        fontSize = TextUnit.Em(16),
+                        color = Color.Black
+                    )
+                }
             }
         }
     }
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/VerticalScrollerInDrawerLayoutDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/VerticalScrollerInDrawerLayoutDemo.kt
index a7dab04..b888fa7 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/VerticalScrollerInDrawerLayoutDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/VerticalScrollerInDrawerLayoutDemo.kt
@@ -52,6 +52,7 @@
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import kotlin.math.roundToInt
 
@@ -115,7 +116,7 @@
             Modifier
                 .fillMaxHeight()
                 .width(drawerWidth)
-                .offset(x = { currentOffset.value })
+                .offset { IntOffset(currentOffset.value.roundToInt(), 0) }
                 .background(color = DefaultBackgroundColor)
         ) {
             Text(
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/keyinput/KeyInputDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/keyinput/KeyInputDemo.kt
index 4fe780d..5cddf04 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/keyinput/KeyInputDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/keyinput/KeyInputDemo.kt
@@ -28,17 +28,15 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusModifier
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.gesture.tapGestureFilter
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.KeyEventType.KeyDown
-import androidx.compose.ui.input.key.keyInputFilter
+import androidx.compose.ui.input.key.onKeyEvent
 
 @Composable
 fun KeyInputDemo() {
@@ -63,20 +61,16 @@
 }
 
 @Composable
-@OptIn(
-    ExperimentalFocus::class,
-    ExperimentalKeyInput::class
-)
 private fun FocusableText(text: MutableState<String>) {
     var color by remember { mutableStateOf(Color.Black) }
-    val focusRequester = FocusRequester()
+    val focusReference = FocusReference()
     Text(
         modifier = Modifier
-            .focusRequester(focusRequester)
-            .focusObserver { color = if (it.isFocused) Color.Green else Color.Black }
-            .focus()
-            .tapGestureFilter { focusRequester.requestFocus() }
-            .keyInputFilter {
+            .focusReference(focusReference)
+            .onFocusChanged { color = if (it.isFocused) Color.Green else Color.Black }
+            .focusModifier()
+            .tapGestureFilter { focusReference.requestFocus() }
+            .onKeyEvent {
                 if (it.type == KeyDown) {
                     text.value = StringBuilder(text.value)
                         .appendCodePoint(it.utf16CodePoint)
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt
index a53a638..15c1dbd 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt
@@ -29,9 +29,10 @@
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.BlendMode
+import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.LinearGradient
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.vector.Path
 import androidx.compose.ui.graphics.vector.PathData
@@ -50,12 +51,10 @@
 fun DrawWithCacheModifierSample() {
     Box(
         Modifier.drawWithCache {
-            val gradient = LinearGradient(
-                startX = 0.0f,
-                startY = 0.0f,
-                endX = size.width,
-                endY = size.height,
-                colors = listOf(Color.Red, Color.Blue)
+            val gradient = Brush.linearGradient(
+                colors = listOf(Color.Red, Color.Blue),
+                start = Offset.Zero,
+                end = Offset(size.width, size.height)
             )
             onDrawBehind {
                 drawRect(gradient)
@@ -66,9 +65,9 @@
 
 /**
  * Sample showing how to leverage [Modifier.drawWithCache] to persist data across
- * draw calls. In the example below, the [LinearGradient] will be re-created if either
+ * draw calls. In the example below, the linear gradient will be re-created if either
  * the size of the drawing area changes, or the toggle flag represented by a mutable state
- * object changes. Otherwise the same [LinearGradient] instance is re-used for each call
+ * object changes. Otherwise the same linear gradient instance is re-used for each call
  * to drawRect.
  */
 @Sampled
@@ -79,12 +78,10 @@
     var toggle by remember { mutableStateOf(true) }
     Box(
         Modifier.clickable { toggle = !toggle }.drawWithCache {
-            val gradient = LinearGradient(
-                startX = 0.0f,
-                startY = 0.0f,
-                endX = size.width,
-                endY = size.height,
-                colors = if (toggle) colors1 else colors2
+            val gradient = Brush.linearGradient(
+                colors = if (toggle) colors1 else colors2,
+                start = Offset.Zero,
+                end = Offset(size.width, size.height)
             )
             onDrawBehind {
                 drawRect(gradient)
@@ -115,12 +112,10 @@
     Image(
         painter = vectorPainter,
         modifier = Modifier.size(120.dp).drawWithCache {
-            val gradient = LinearGradient(
+            val gradient = Brush.linearGradient(
                 colors = listOf(Color.Red, Color.Blue),
-                startX = 0f,
-                startY = 0f,
-                endX = 0f,
-                endY = size.height
+                start = Offset.Zero,
+                end = Offset(0f, size.height)
             )
             onDrawWithContent {
                 drawContent()
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/NestedScrollSamples.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/NestedScrollSamples.kt
new file mode 100644
index 0000000..4a95ba4
--- /dev/null
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/NestedScrollSamples.kt
@@ -0,0 +1,163 @@
+/*
+ * 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.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.ScrollCallback
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollDispatcher
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollSource
+import androidx.compose.ui.gesture.nestedscroll.nestedScroll
+import androidx.compose.ui.gesture.scrollGestureFilter
+import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.minus
+import kotlin.math.roundToInt
+
+@Sampled
+@Composable
+fun NestedScrollSample() {
+    // constructing the box with next that scrolls as long as text within 0 .. 300
+    // to support nested scrolling, we need to scroll ourselves, dispatch nested scroll events
+    // as we scroll, and listen to potential children when we're scrolling.
+    val maxValue = 300f
+    val minValue = 0f
+    // our state that we update as scroll
+    var value by remember { mutableStateOf(maxValue / 2) }
+    // create dispatch to dispatch scroll events up to the nested scroll parents
+    val nestedScrollDispatcher = remember { NestedScrollDispatcher() }
+    // we're going to scroll vertically, so set the orientation to vertical
+    val orientation = Orientation.Vertical
+
+    // callback to listen to scroll events and dispatch nested scroll events
+    val scrollCallback = remember {
+        object : ScrollCallback {
+            override fun onScroll(scrollDistance: Float): Float {
+                // dispatch prescroll with Y axis since we're going vertical scroll
+                val aboveConsumed = nestedScrollDispatcher.dispatchPreScroll(
+                    Offset(x = 0f, y = scrollDistance),
+                    NestedScrollSource.Drag
+                )
+                // adjust what we can consume according to pre-scroll
+                val available = scrollDistance - aboveConsumed.y
+                // let's calculate how much we want to consume and how much is left
+                val newTotal = value + available
+                val newValue = newTotal.coerceIn(minValue, maxValue)
+                val toConsume = newValue - value
+                val leftAfterUs = available - toConsume
+                // consume ourselves what we need and dispatch "scroll" phase of nested scroll
+                value += toConsume
+                nestedScrollDispatcher.dispatchPostScroll(
+                    Offset(x = 0f, y = toConsume),
+                    Offset(x = 0f, y = leftAfterUs),
+                    NestedScrollSource.Drag
+                )
+                // indicate to the old pointer that we handled everything by returning same value
+                return scrollDistance
+            }
+
+            override fun onStop(velocity: Float) {
+                // for simplicity we won't fling ourselves, but we need to respect nested scroll
+                // dispatch pre fling
+                val velocity2d = Velocity(Offset(x = 0f, y = velocity))
+                val consumed = nestedScrollDispatcher.dispatchPreFling(velocity2d)
+                // now, since we don't fling, we consume 0 (Offset.Zero).
+                // Adjust what's left after prefling and dispatch post fling
+                val left = velocity2d - consumed
+                nestedScrollDispatcher.dispatchPostFling(Velocity.Zero, left)
+            }
+        }
+    }
+
+    // we also want to participate in the nested scrolling, not only dispatching. create connection
+    val connection = remember {
+        object : NestedScrollConnection {
+            // let's assume we want to consume children's delta before them if we can
+            // we should do it in pre scroll
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                // calculate how much we can take from child
+                val oldValue = value
+                val newTotal = value + available.y
+                val newValue = newTotal.coerceIn(minValue, maxValue)
+                val toConsume = newValue - oldValue
+                // consume what we want and report back co children can adjust
+                value += toConsume
+                return Offset(x = 0f, y = toConsume)
+            }
+        }
+    }
+
+    // scrollable parent to which we will dispatch our nested scroll events
+    // Since we properly support scrolling above, this parent will scroll even if we scroll inner
+    // box (with White background)
+    LazyColumn(Modifier.background(Color.Red)) {
+        // our box we constructed
+        item {
+            Box(
+                Modifier
+                    .size(width = 300.dp, height = 100.dp)
+                    .background(Color.White)
+                    // add scrolling listening and dispatching
+                    .scrollGestureFilter(orientation = orientation, scrollCallback = scrollCallback)
+                    // connect self connection and dispatcher to the nested scrolling system
+                    .nestedScroll(connection, dispatcher = nestedScrollDispatcher)
+            ) {
+                // hypothetical scrollable child which we will listen in connection above
+                LazyColumn {
+                    items(listOf(1, 2, 3, 4, 5)) {
+                        Text(
+                            "Magenta text above will change first when you scroll me",
+                            modifier = Modifier.padding(5.dp)
+                        )
+                    }
+                }
+                // simply show our value. It will change when we scroll child list above, taking
+                // child's scroll delta until we reach maxValue or minValue
+                Text(
+                    text = value.roundToInt().toString(),
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .background(Color.Magenta)
+                )
+            }
+        }
+        repeat(100) {
+            item {
+                Text(
+                    "Outer scroll items are Yellow on Red parent",
+                    modifier = Modifier.background(Color.Yellow).padding(5.dp)
+                )
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index 40ac0f4..d4fa383 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -27,6 +27,7 @@
 import android.view.accessibility.AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
 import android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_SELECTION
 import android.view.accessibility.AccessibilityNodeProvider
+import android.view.accessibility.AccessibilityRecord
 import androidx.activity.ComponentActivity
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.layout.Box
@@ -34,6 +35,7 @@
 import androidx.compose.foundation.selection.toggleable
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -58,7 +60,6 @@
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.input.TextFieldValue
-import androidx.core.os.BuildCompat
 import androidx.core.view.ViewCompat
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -66,7 +67,6 @@
 import androidx.test.filters.SdkSuppress
 import androidx.test.platform.app.InstrumentationRegistry
 import com.nhaarman.mockitokotlin2.argThat
-import com.nhaarman.mockitokotlin2.atLeast
 import com.nhaarman.mockitokotlin2.atLeastOnce
 import com.nhaarman.mockitokotlin2.doReturn
 import com.nhaarman.mockitokotlin2.eq
@@ -91,6 +91,7 @@
 @RunWith(AndroidJUnit4::class)
 @OptIn(
     ExperimentalFoundationApi::class,
+    ExperimentalComposeApi::class
 )
 class AndroidAccessibilityTest {
     @get:Rule
@@ -107,8 +108,11 @@
     private var textFieldSelectionOneLatch = CountDownLatch(1)
 
     companion object {
+        private const val TopColTag = "topColumn"
         private const val ToggleableTag = "toggleable"
+        private const val DisabledToggleableTag = "disabledToggleable"
         private const val TextFieldTag = "textField"
+        private const val TextNodeTag = "textNode"
         private const val InputText = "hello"
         private const val InitialText = "h"
     }
@@ -132,11 +136,25 @@
             container.setContent {
                 var checked by remember { mutableStateOf(true) }
                 var value by remember { mutableStateOf(TextFieldValue(InitialText)) }
-                Column {
+                Column(Modifier.testTag(TopColTag)) {
                     Box(
                         Modifier
                             .toggleable(value = checked, onValueChange = { checked = it })
-                            .testTag(ToggleableTag),
+                            .testTag(ToggleableTag)
+                    ) {
+                        BasicText("ToggleableText")
+                        Box {
+                            BasicText("TextNode", Modifier.testTag(TextNodeTag))
+                        }
+                    }
+                    Box(
+                        Modifier
+                            .toggleable(
+                                value = checked,
+                                enabled = false,
+                                onValueChange = { checked = it }
+                            )
+                            .testTag(DisabledToggleableTag),
                         content = {
                             BasicText("ToggleableText")
                         }
@@ -162,6 +180,7 @@
             androidComposeView = container.getChildAt(0) as AndroidComposeView
             delegate = ViewCompat.getAccessibilityDelegate(androidComposeView) as
                 AndroidComposeViewAccessibilityDelegateCompat
+            delegate.accessibilityForceEnabledForTesting = true
             provider = delegate.getAccessibilityNodeProvider(androidComposeView).provider
                 as AccessibilityNodeProvider
         }
@@ -174,7 +193,7 @@
         var accessibilityNodeInfo = provider.createAccessibilityNodeInfo(toggleableNode.id)
         assertEquals("android.view.View", accessibilityNodeInfo.className)
         val stateDescription = when {
-            BuildCompat.isAtLeastR() -> {
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
                 accessibilityNodeInfo.stateDescription
             }
             Build.VERSION.SDK_INT >= 19 -> {
@@ -243,11 +262,13 @@
     }
 
     @Test
-    fun testPerformAction() {
+    fun testPerformAction_succeedOnEnabledNodes() {
+        rule.onNodeWithTag(ToggleableTag)
+            .assertIsOn()
         val toggleableNode = rule.onNodeWithTag(ToggleableTag)
             .fetchSemanticsNode("couldn't find node with tag $ToggleableTag")
         rule.runOnUiThread {
-            provider.performAction(toggleableNode.id, ACTION_CLICK, null)
+            assertTrue(provider.performAction(toggleableNode.id, ACTION_CLICK, null))
         }
         rule.onNodeWithTag(ToggleableTag)
             .assertIsOff()
@@ -255,7 +276,7 @@
         val textFieldNode = rule.onNodeWithTag(TextFieldTag)
             .fetchSemanticsNode("couldn't find node with tag $TextFieldTag")
         rule.runOnUiThread {
-            provider.performAction(textFieldNode.id, ACTION_CLICK, null)
+            assertTrue(provider.performAction(textFieldNode.id, ACTION_CLICK, null))
         }
         rule.onNodeWithTag(TextFieldTag)
             .assert(SemanticsMatcher.expectValue(SemanticsProperties.Focused, true))
@@ -264,7 +285,7 @@
         argument.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 1)
         rule.runOnUiThread {
             textFieldSelectionOneLatch = CountDownLatch(1)
-            provider.performAction(textFieldNode.id, ACTION_SET_SELECTION, argument)
+            assertTrue(provider.performAction(textFieldNode.id, ACTION_SET_SELECTION, argument))
         }
         if (!textFieldSelectionOneLatch.await(5, TimeUnit.SECONDS)) {
             throw AssertionError("Failed to wait for text selection change.")
@@ -279,6 +300,19 @@
     }
 
     @Test
+    fun testPerformAction_failOnDisabledNodes() {
+        rule.onNodeWithTag(DisabledToggleableTag)
+            .assertIsOn()
+        val toggleableNode = rule.onNodeWithTag(DisabledToggleableTag)
+            .fetchSemanticsNode("couldn't find node with tag $DisabledToggleableTag")
+        rule.runOnUiThread {
+            assertFalse(provider.performAction(toggleableNode.id, ACTION_CLICK, null))
+        }
+        rule.onNodeWithTag(DisabledToggleableTag)
+            .assertIsOn()
+    }
+
+    @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testAddExtraDataToAccessibilityNodeInfo() {
         val textFieldNode = rule.onNodeWithTag(TextFieldTag)
@@ -374,20 +408,36 @@
 
     @Test
     fun sendSubtreeChangeEvents_whenNodeRemoved() {
+        val topColumn = rule.onNodeWithTag(TopColTag)
+            .fetchSemanticsNode("couldn't find node with tag $TopColTag")
         rule.onNodeWithTag(TextFieldTag)
             .assertExists()
-        // TextField is removed compared to setup.
-        isTextFieldVisible = false
-        rule.onNodeWithTag(TextFieldTag)
-            .assertDoesNotExist()
-
-        rule.runOnIdle {
-            // One from initialization and one from text field removal.
-            verify(container, atLeast(2)).requestSendAccessibilityEvent(
+        // wait for the subtree change events from initialization to send
+        waitForSubtreeEventToSendAndVerify {
+            verify(container, atLeastOnce()).requestSendAccessibilityEvent(
                 eq(androidComposeView),
                 argThat(
                     ArgumentMatcher {
-                        it.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
+                        getAccessibilityEventSourceSemanticsNodeId(it) == topColumn.id &&
+                            it.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
+                            it.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
+                    }
+                )
+            )
+        }
+
+        // TextField is removed compared to setup.
+        isTextFieldVisible = false
+
+        rule.onNodeWithTag(TextFieldTag)
+            .assertDoesNotExist()
+        waitForSubtreeEventToSendAndVerify {
+            verify(container, atLeastOnce()).requestSendAccessibilityEvent(
+                eq(androidComposeView),
+                argThat(
+                    ArgumentMatcher {
+                        getAccessibilityEventSourceSemanticsNodeId(it) == topColumn.id &&
+                            it.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
                             it.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
                     }
                 )
@@ -449,6 +499,90 @@
         }
     }
 
+    @Test
+    fun semanticsNodeBeingMergedLayoutChange_sendThrottledSubtreeEventsForMergedSemanticsNode() {
+        val toggleableNode = rule.onNodeWithTag(ToggleableTag)
+            .fetchSemanticsNode("couldn't find node with tag $ToggleableTag")
+        val textNode = rule.onNodeWithTag(TextNodeTag, useUnmergedTree = true)
+            .fetchSemanticsNode("couldn't find node with tag $TextNodeTag")
+        // wait for the subtree change events from initialization to send
+        waitForSubtreeEventToSendAndVerify {
+            verify(container, atLeastOnce()).requestSendAccessibilityEvent(
+                eq(androidComposeView),
+                argThat(
+                    ArgumentMatcher {
+                        getAccessibilityEventSourceSemanticsNodeId(it) == toggleableNode.id &&
+                            it.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
+                            it.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
+                    }
+                )
+            )
+        }
+
+        rule.runOnUiThread {
+            // Directly call onLayoutChange because this guarantees short time.
+            for (i in 1..10) {
+                delegate.onLayoutChange(textNode.layoutNode)
+            }
+        }
+
+        waitForSubtreeEventToSendAndVerify {
+            verify(container, atLeastOnce()).requestSendAccessibilityEvent(
+                eq(androidComposeView),
+                argThat(
+                    ArgumentMatcher {
+                        getAccessibilityEventSourceSemanticsNodeId(it) == toggleableNode.id &&
+                            it.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
+                            it.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
+                    }
+                )
+            )
+        }
+    }
+
+    @Test
+    fun layoutNodeWithoutSemanticsLayoutChange_sendThrottledSubtreeEventsForMergedSemanticsNode() {
+        val toggleableNode = rule.onNodeWithTag(ToggleableTag)
+            .fetchSemanticsNode("couldn't find node with tag $ToggleableTag")
+        val textNode = rule.onNodeWithTag(TextNodeTag, useUnmergedTree = true)
+            .fetchSemanticsNode("couldn't find node with tag $TextNodeTag")
+        // wait for the subtree change events from initialization to send
+        waitForSubtreeEventToSendAndVerify {
+            verify(container, atLeastOnce()).requestSendAccessibilityEvent(
+                eq(androidComposeView),
+                argThat(
+                    ArgumentMatcher {
+                        getAccessibilityEventSourceSemanticsNodeId(it) == toggleableNode.id &&
+                            it.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
+                            it.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
+                    }
+                )
+            )
+        }
+
+        rule.runOnUiThread {
+            // Directly call onLayoutChange because this guarantees short time.
+            for (i in 1..10) {
+                // layout change for the parent box node
+                delegate.onLayoutChange(textNode.layoutNode.parent!!)
+            }
+        }
+
+        waitForSubtreeEventToSendAndVerify {
+            // One from initialization and one from layout changes.
+            verify(container, atLeastOnce()).requestSendAccessibilityEvent(
+                eq(androidComposeView),
+                argThat(
+                    ArgumentMatcher {
+                        getAccessibilityEventSourceSemanticsNodeId(it) == toggleableNode.id &&
+                            it.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
+                            it.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
+                    }
+                )
+            )
+        }
+    }
+
     private fun eventIndex(list: List<AccessibilityEvent>, event: AccessibilityEvent): Int {
         for (i in list.indices) {
             if (ReflectionEquals(list[i], null).matches(event)) {
@@ -461,4 +595,17 @@
     private fun containsEvent(list: List<AccessibilityEvent>, event: AccessibilityEvent): Boolean {
         return eventIndex(list, event) != -1
     }
+
+    private fun getAccessibilityEventSourceSemanticsNodeId(event: AccessibilityEvent): Int {
+        val getSourceNodeIdMethod = AccessibilityRecord::class.java
+            .getDeclaredMethod("getSourceNodeId")
+        getSourceNodeIdMethod.isAccessible = true
+        return (getSourceNodeIdMethod.invoke(event) as Long shr 32).toInt()
+    }
+
+    private fun waitForSubtreeEventToSendAndVerify(verify: () -> Unit) {
+        // TODO(aelias): Make this wait after the 100ms delay to check the second batch is also correct
+        rule.waitForIdle()
+        verify()
+    }
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
index 480bae1..27ac730 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
@@ -34,12 +34,17 @@
 import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.semantics.SemanticsPropertyReceiver
 import androidx.compose.ui.semantics.SemanticsWrapper
-import androidx.compose.ui.semantics.accessibilityValue
-import androidx.compose.ui.semantics.accessibilityValueRange
+import androidx.compose.ui.semantics.copyText
+import androidx.compose.ui.semantics.cutText
+import androidx.compose.ui.semantics.disabled
+import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.semantics.stateDescriptionRange
 import androidx.compose.ui.semantics.dismiss
 import androidx.compose.ui.semantics.focused
 import androidx.compose.ui.semantics.getTextLayoutResult
+import androidx.compose.ui.semantics.horizontalAccessibilityScrollState
 import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.onLongClick
 import androidx.compose.ui.semantics.pasteText
 import androidx.compose.ui.semantics.setProgress
 import androidx.compose.ui.semantics.setSelection
@@ -50,7 +55,6 @@
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextRange
-import androidx.core.os.BuildCompat
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -100,6 +104,7 @@
             accessibilityDelegate = AndroidComposeViewAccessibilityDelegateCompat(
                 androidComposeView
             )
+            accessibilityDelegate.accessibilityForceEnabledForTesting = true
         }
         rule.setContent {
             AmbientClipboardManager.current.setText(AnnotatedString("test"))
@@ -111,9 +116,9 @@
         val info = AccessibilityNodeInfoCompat.obtain()
         val clickActionLabel = "click"
         val dismissActionLabel = "dismiss"
-        val accessibilityValue = "checked"
+        val stateDescription = "checked"
         val semanticsModifier = SemanticsModifierCore(1, true, false) {
-            this.accessibilityValue = accessibilityValue
+            this.stateDescription = stateDescription
             onClick(clickActionLabel) { true }
             dismiss(dismissActionLabel) { true }
         }
@@ -141,8 +146,8 @@
                 )
             )
         )
-        val stateDescription = when {
-            BuildCompat.isAtLeastR() -> {
+        val stateDescriptionResult = when {
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
                 info.unwrap().stateDescription
             }
             Build.VERSION.SDK_INT >= 19 -> {
@@ -154,17 +159,100 @@
                 null
             }
         }
-        assertEquals(accessibilityValue, stateDescription)
+        assertEquals(stateDescription, stateDescriptionResult)
         assertTrue(info.isClickable)
         assertTrue(info.isVisibleToUser)
     }
 
     @Test
+    fun testPopulateAccessibilityNodeInfoProperties_disabled() {
+        val info = AccessibilityNodeInfoCompat.obtain()
+        val semanticsNode = createSemanticsNodeWithProperties(1, true) {
+            disabled()
+            text = AnnotatedString("text")
+            horizontalAccessibilityScrollState = AccessibilityScrollState(0f, 5f)
+            onClick { true }
+            onLongClick { true }
+            copyText { true }
+            pasteText { true }
+            cutText { true }
+            setText { true }
+            setSelection { _, _, _ -> true }
+            dismiss { true }
+        }
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(1, info, semanticsNode)
+        assertTrue(info.isClickable)
+        assertFalse(
+            containsAction(
+                info,
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK
+            )
+        )
+        assertTrue(info.isLongClickable)
+        assertFalse(
+            containsAction(
+                info,
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK
+            )
+        )
+        assertTrue(
+            containsAction(
+                info,
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_COPY
+            )
+        )
+        assertFalse(
+            containsAction(
+                info,
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_PASTE
+            )
+        )
+        assertFalse(
+            containsAction(
+                info,
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CUT
+            )
+        )
+        assertFalse(
+            containsAction(
+                info,
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_TEXT
+            )
+        )
+        // This is the default ACTION_SET_SELECTION.
+        assertTrue(
+            containsAction(
+                info,
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_SELECTION
+            )
+        )
+        assertFalse(
+            containsAction(
+                info,
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_DISMISS
+            )
+        )
+        assertFalse(
+            containsAction(
+                info,
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_FORWARD
+            )
+        )
+        assertFalse(
+            containsAction(
+                info,
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_RIGHT
+            )
+        )
+        info.recycle()
+    }
+
+    @Test
     fun testPopulateAccessibilityNodeInfoProperties_SeekBar() {
         val info = AccessibilityNodeInfoCompat.obtain()
         val setProgressActionLabel = "setProgress"
         val semanticsModifier = SemanticsModifierCore(1, true, false) {
-            accessibilityValueRange = AccessibilityRangeInfo(0.5f, 0f..1f, 6)
+            stateDescriptionRange = AccessibilityRangeInfo(0.5f, 0f..1f, 6)
             setProgress(setProgressActionLabel) { true }
         }
         val semanticsNode = SemanticsNode(
@@ -332,15 +420,6 @@
                 }
             )
         )
-        verify(container, times(1)).requestSendAccessibilityEvent(
-            eq(androidComposeView),
-            argThat(
-                ArgumentMatcher {
-                    it.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
-                        it.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
-                }
-            )
-        )
     }
 
     @Test
@@ -361,15 +440,6 @@
             eq(androidComposeView),
             argThat(
                 ArgumentMatcher {
-                    it.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
-                        it.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
-                }
-            )
-        )
-        verify(container, times(1)).requestSendAccessibilityEvent(
-            eq(androidComposeView),
-            argThat(
-                ArgumentMatcher {
                     it.eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED && it.scrollY == 2 &&
                         it.maxScrollY == 5 &&
                         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
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 46ca6ca..cd7dad9 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
@@ -91,6 +91,7 @@
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.RenderNodeApi23
 import androidx.compose.ui.platform.RenderNodeApi29
+import androidx.compose.ui.platform.ViewCompositionStrategy
 import androidx.compose.ui.platform.ViewLayer
 import androidx.compose.ui.platform.ViewLayerContainer
 import androidx.compose.ui.platform.setContent
@@ -3039,6 +3040,9 @@
         lateinit var view: ComposeView
         activityTestRule.runOnUiThread {
             view = ComposeView(activity)
+            view.setViewCompositionStrategy(
+                ViewCompositionStrategy.DisposeOnLifecycleDestroyed(activity)
+            )
             view.setContent {
                 with(AmbientDensity.current) {
                     Box(
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/MemoryLeakTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/MemoryLeakTest.kt
index b234a6d..f596611 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/MemoryLeakTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/MemoryLeakTest.kt
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.dispatch.AndroidUiDispatcher
 import androidx.compose.testutils.ComposeTestCase
 import androidx.compose.testutils.createAndroidComposeBenchmarkRunner
@@ -29,6 +30,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.yield
@@ -51,6 +53,9 @@
         class SimpleTestCase : ComposeTestCase {
             @Composable
             override fun Content() {
+                // The following line adds coverage for delayed coroutine memory leaks.
+                LaunchedEffect(Unit) { delay(10000) }
+
                 Column {
                     repeat(3) {
                         Box {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/autofill/AndroidAutoFillTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/autofill/AndroidAutoFillTest.kt
index f5aa880..8119ae9 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/autofill/AndroidAutoFillTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/autofill/AndroidAutoFillTest.kt
@@ -22,6 +22,7 @@
 import android.view.autofill.AutofillValue
 import androidx.autofill.HintConstants.AUTOFILL_HINT_PERSON_NAME
 import androidx.compose.testutils.fake.FakeViewStructure
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.platform.AmbientAutofill
 import androidx.compose.ui.platform.AmbientAutofillTree
@@ -36,6 +37,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalComposeUiApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class AndroidAutoFillTest {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
index 96b153b..c2a98e8 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
@@ -20,11 +20,13 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.FixedSize
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Padding
-import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.background
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
@@ -32,9 +34,10 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Outline
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.graphics.asAndroidBitmap
-import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.boundsInRoot
 import androidx.compose.ui.layout.globalBounds
@@ -366,4 +369,26 @@
             assertEquals(0f, bounds.height)
         }
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun invalidateWhenWeHaveSemanticModifierAfterLayer() {
+        var color by mutableStateOf(Color.Red)
+        rule.setContent {
+            FixedSize(
+                5,
+                Modifier.graphicsLayer().testTag("tag").background(color)
+            )
+        }
+
+        rule.runOnIdle {
+            color = Color.Green
+        }
+
+        rule.onNodeWithTag("tag")
+            .captureToImage()
+            .assertPixels {
+                color
+            }
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CaptureFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CaptureFocusTest.kt
index 4dfe5bb..d73924fc 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CaptureFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CaptureFocusTest.kt
@@ -17,10 +17,7 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.foundation.layout.Box
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -30,7 +27,6 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
 class CaptureFocusTest {
     @get:Rule
@@ -40,19 +36,19 @@
     fun active_captureFocus_changesStateToCaptured() {
         // Arrange.
         lateinit var focusState: FocusState
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(FocusState.Active))
             )
         }
 
         // Act.
         val success = rule.runOnIdle {
-            focusRequester.captureFocus()
+            focusReference.captureFocus()
         }
 
         // Assert.
@@ -66,19 +62,19 @@
     fun activeParent_captureFocus_retainsStateAsActiveParent() {
         // Arrange.
         var focusState: FocusState = FocusState.ActiveParent
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         // Act.
         val success = rule.runOnIdle {
-            focusRequester.captureFocus()
+            focusReference.captureFocus()
         }
 
         // Assert.
@@ -92,19 +88,19 @@
     fun captured_captureFocus_retainsStateAsCaptured() {
         // Arrange.
         var focusState = FocusState.Captured
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         // Act.
         val success = rule.runOnIdle {
-            focusRequester.captureFocus()
+            focusReference.captureFocus()
         }
 
         // Assert.
@@ -118,19 +114,19 @@
     fun disabled_captureFocus_retainsStateAsDisabled() {
         // Arrange.
         var focusState = FocusState.Disabled
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         // Act.
         val success = rule.runOnIdle {
-            focusRequester.captureFocus()
+            focusReference.captureFocus()
         }
 
         // Assert.
@@ -144,19 +140,19 @@
     fun inactive_captureFocus_retainsStateAsInactive() {
         // Arrange.
         var focusState = FocusState.Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         // Act.
         val success = rule.runOnIdle {
-            focusRequester.captureFocus()
+            focusReference.captureFocus()
         }
 
         // Assert.
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ClearFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ClearFocusTest.kt
index 382c8fa..99bb23c 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ClearFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ClearFocusTest.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.foundation.layout.Box
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.ActiveParent
 import androidx.compose.ui.focus.FocusState.Captured
@@ -32,7 +31,6 @@
 import org.junit.runners.Parameterized
 
 @SmallTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(Parameterized::class)
 class ClearFocusTest(val forcedClear: Boolean) {
     @get:Rule
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindFocusableChildrenTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindFocusableChildrenTest.kt
index ff6adc0..b4ece14 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindFocusableChildrenTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindFocusableChildrenTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.focus.FocusState.Inactive
 import androidx.compose.ui.graphics.Color.Companion.Red
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -30,7 +29,6 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
 class FindFocusableChildrenTest {
     @get:Rule
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindParentFocusNodeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindParentFocusNodeTest.kt
index 24f8ee1..e148d1d 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindParentFocusNodeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindParentFocusNodeTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.focus.FocusState.Inactive
 import androidx.compose.ui.graphics.Color.Companion.Red
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -30,7 +29,6 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
 class FindParentFocusNodeTest {
     @get:Rule
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusChangedCountTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusChangedCountTest.kt
new file mode 100644
index 0000000..7acc6e8
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusChangedCountTest.kt
@@ -0,0 +1,215 @@
+/*
+ * 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.focus
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusState.Inactive
+import androidx.compose.ui.focus.FocusState.Active
+import androidx.compose.ui.platform.AmbientFocusManager
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class FocusChangedCountTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun initially_focusChangedIsCalledOnce() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusReference = FocusReference()
+        rule.setFocusableContent {
+            Box(
+                modifier = Modifier
+                    .onFocusChanged { focusStates.add(it) }
+                    .focusReference(focusReference)
+                    .focusModifier()
+            )
+        }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).containsExactly(Inactive) }
+    }
+
+    @Test
+    fun initiallyNoFocusModifier_onFocusChangedIsCalledOnce() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        rule.setFocusableContent {
+            Box(modifier = Modifier.onFocusChanged { focusStates.add(it) })
+        }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).containsExactly(Inactive) }
+    }
+
+    @Test
+    fun whenFocusIsGained_focusChangedIsCalledOnce() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusReference = FocusReference()
+        rule.setFocusableContent {
+            Box(
+                modifier = Modifier
+                    .onFocusChanged { focusStates.add(it) }
+                    .focusReference(focusReference)
+                    .focusModifier()
+            )
+        }
+        rule.runOnIdle { focusStates.clear() }
+
+        // Act.
+        rule.runOnIdle { focusReference.requestFocus() }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).containsExactly(Active) }
+    }
+
+    @Test
+    fun requestingFocusWhenAlreadyFocused_onFocusChangedIsNotCalledAgain() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusReference = FocusReference()
+        rule.setFocusableContent {
+            Box(
+                modifier = Modifier
+                    .onFocusChanged { focusStates.add(it) }
+                    .focusReference(focusReference)
+                    .focusModifier()
+            )
+        }
+        rule.runOnIdle {
+            focusReference.requestFocus()
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle { focusReference.requestFocus() }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).isEmpty() }
+    }
+
+    @Test
+    fun whenFocusIsLost_focusChangedIsCalledOnce() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusReference = FocusReference()
+        lateinit var focusManager: FocusManager
+        rule.setFocusableContent {
+            focusManager = AmbientFocusManager.current
+            Box(
+                modifier = Modifier
+                    .onFocusChanged { focusStates.add(it) }
+                    .focusReference(focusReference)
+                    .focusModifier()
+            )
+        }
+        rule.runOnIdle {
+            focusReference.requestFocus()
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle { focusManager.clearFocus() }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).containsExactly(Inactive) }
+    }
+
+    @Test
+    fun removingActiveFocusNode_onFocusChangedIsCalledOnce() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusReference = FocusReference()
+        lateinit var addFocusModifier: MutableState<Boolean>
+        rule.setFocusableContent {
+            addFocusModifier = remember { mutableStateOf(true) }
+            Box(
+                modifier = Modifier
+                    .onFocusChanged { focusStates.add(it) }
+                    .focusReference(focusReference)
+                    .then(if (addFocusModifier.value) Modifier.focusModifier() else Modifier)
+            )
+        }
+        rule.runOnIdle {
+            focusReference.requestFocus()
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle { addFocusModifier.value = false }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).containsExactly(Inactive) }
+    }
+
+    @Test
+    fun removingInactiveFocusNode_onFocusChangedIsNotCalled() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        lateinit var addFocusModifier: MutableState<Boolean>
+        rule.setFocusableContent {
+            addFocusModifier = remember { mutableStateOf(true) }
+            Box(
+                modifier = Modifier
+                    .onFocusChanged { focusStates.add(it) }
+                    .then(if (addFocusModifier.value) Modifier.focusModifier() else Modifier)
+            )
+        }
+        rule.runOnIdle { focusStates.clear() }
+
+        // Act.
+        rule.runOnIdle { addFocusModifier.value = false }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).isEmpty() }
+    }
+
+    @Test
+    fun addingFocusModifier_onFocusChangedIsNotCalled() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        lateinit var addFocusModifier: MutableState<Boolean>
+        rule.setFocusableContent {
+            addFocusModifier = remember { mutableStateOf(false) }
+            Box(
+                modifier = Modifier
+                    .onFocusChanged { focusStates.add(it) }
+                    .then(if (addFocusModifier.value) Modifier.focusModifier() else Modifier)
+            )
+        }
+        rule.runOnIdle { focusStates.clear() }
+
+        // Act.
+        rule.runOnIdle { addFocusModifier.value = true }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).isEmpty() }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusObserverTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusChangedTest.kt
similarity index 66%
rename from compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusObserverTest.kt
rename to compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusChangedTest.kt
index dee66f9..a365dbe 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusObserverTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusChangedTest.kt
@@ -17,16 +17,12 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.foundation.layout.Box
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.ActiveParent
 import androidx.compose.ui.focus.FocusState.Captured
 import androidx.compose.ui.focus.FocusState.Disabled
 import androidx.compose.ui.focus.FocusState.Inactive
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -36,9 +32,8 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
-class FocusObserverTest {
+class FocusChangedTest {
     @get:Rule
     val rule = createComposeRule()
 
@@ -46,19 +41,19 @@
     fun active_requestFocus() {
         // Arrange.
         lateinit var focusState: FocusState
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(Active))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
@@ -69,30 +64,29 @@
     fun activeParent_requestFocus() {
         // Arrange.
         lateinit var focusState: FocusState
-        val focusRequester = FocusRequester()
-        val childFocusRequester = FocusRequester()
+        val (focusReference, childFocusReference) = FocusReference.createRefs()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             ) {
                 Box(
                     modifier = Modifier
-                        .focusRequester(childFocusRequester)
-                        .focus()
+                        .focusReference(childFocusReference)
+                        .focusModifier()
                 )
             }
         }
         rule.runOnIdle {
-            childFocusRequester.requestFocus()
+            childFocusReference.requestFocus()
             assertThat(focusState).isEqualTo(ActiveParent)
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
@@ -103,19 +97,19 @@
     fun captured_requestFocus() {
         // Arrange.
         lateinit var focusState: FocusState
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(Captured))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Captured)
@@ -126,19 +120,19 @@
     fun disabled_requestFocus() {
         // Arrange.
         lateinit var focusState: FocusState
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(Disabled))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Disabled)
@@ -149,19 +143,19 @@
     fun inactive_requestFocus() {
         // Arrange.
         lateinit var focusState: FocusState
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(Inactive))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
@@ -177,24 +171,24 @@
         lateinit var focusState4: FocusState
         lateinit var focusState5: FocusState
         lateinit var focusState6: FocusState
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState1 = it }
-                    .focusObserver { focusState2 = it }
+                    .onFocusChanged { focusState1 = it }
+                    .onFocusChanged { focusState2 = it }
             ) {
                 Box {
                     Box(
                         modifier = Modifier
-                            .focusObserver { focusState3 = it }
-                            .focusObserver { focusState4 = it }
+                            .onFocusChanged { focusState3 = it }
+                            .onFocusChanged { focusState4 = it }
                     ) {
                         Box(
                             modifier = Modifier
-                                .focusObserver { focusState5 = it }
-                                .focusObserver { focusState6 = it }
-                                .focusRequester(focusRequester)
+                                .onFocusChanged { focusState5 = it }
+                                .onFocusChanged { focusState6 = it }
+                                .focusReference(focusReference)
                                 .then(FocusModifier(Inactive))
                         )
                     }
@@ -204,7 +198,7 @@
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState1).isEqualTo(Active)
@@ -223,36 +217,29 @@
         var focusState2: FocusState? = null
         var focusState3: FocusState? = null
         var focusState4: FocusState? = null
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState1 = it }
-                    .focusObserver { focusState2 = it }
-                    .focus()
-                    .focusObserver { focusState3 = it }
-                    .focusObserver { focusState4 = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState1 = it }
+                    .onFocusChanged { focusState2 = it }
+                    .focusModifier()
+                    .onFocusChanged { focusState3 = it }
+                    .onFocusChanged { focusState4 = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
-        rule.runOnIdle {
-            focusRequester.requestFocus()
-            focusState1 = null
-            focusState2 = null
-            focusState3 = null
-            focusState4 = null
-        }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
-            assertThat(focusState1).isNull()
-            assertThat(focusState2).isNull()
+            assertThat(focusState1).isEqualTo(ActiveParent)
+            assertThat(focusState2).isEqualTo(ActiveParent)
             assertThat(focusState3).isEqualTo(Active)
             assertThat(focusState4).isEqualTo(Active)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusEventCountTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusEventCountTest.kt
new file mode 100644
index 0000000..fc1211f
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusEventCountTest.kt
@@ -0,0 +1,232 @@
+/*
+ * 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.focus
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusState.Inactive
+import androidx.compose.ui.focus.FocusState.Active
+import androidx.compose.ui.platform.AmbientFocusManager
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class FocusEventCountTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun initially_onFocusEventIsCalledThrice() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusReferece = FocusReference()
+        rule.setFocusableContent {
+            Box(
+                modifier = Modifier
+                    .onFocusEvent { focusStates.add(it) }
+                    .focusReference(focusReferece)
+                    .focusModifier()
+            )
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(focusStates).containsExactly(
+                Inactive, // triggered by onFocusEvent node's onModifierChanged().
+                Inactive, // triggered by focus node's onModifierChanged().
+                Inactive, // triggered by focus node's attach().
+            )
+        }
+    }
+
+    @Test
+    fun initiallyNoFocusModifier_onFocusEventIsCalledOnce() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        rule.setFocusableContent {
+            Box(modifier = Modifier.onFocusEvent { focusStates.add(it) })
+        }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).containsExactly(Inactive) }
+    }
+
+    @Test
+    fun whenFocusIsGained_onFocusEventIsCalledOnce() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusReference = FocusReference()
+        rule.setFocusableContent {
+            Box(
+                modifier = Modifier
+                    .onFocusEvent { focusStates.add(it) }
+                    .focusReference(focusReference)
+                    .focusModifier()
+            )
+        }
+        rule.runOnIdle { focusStates.clear() }
+
+        // Act.
+        rule.runOnIdle { focusReference.requestFocus() }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).containsExactly(Active) }
+    }
+
+    @Test
+    fun requestingFocusWhenAlreadyFocused_onFocusEventIsCalledAgain() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusReference = FocusReference()
+        rule.setFocusableContent {
+            Box(
+                modifier = Modifier
+                    .onFocusEvent { focusStates.add(it) }
+                    .focusReference(focusReference)
+                    .focusModifier()
+            )
+        }
+        rule.runOnIdle {
+            focusReference.requestFocus()
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle { focusReference.requestFocus() }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).containsExactly(Active) }
+    }
+
+    @Test
+    fun whenFocusIsLost_onFocusEventIsCalledOnce() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusReference = FocusReference()
+        lateinit var focusManager: FocusManager
+        rule.setFocusableContent {
+            focusManager = AmbientFocusManager.current
+            Box(
+                modifier = Modifier
+                    .onFocusEvent { focusStates.add(it) }
+                    .focusReference(focusReference)
+                    .focusModifier()
+            )
+        }
+        rule.runOnIdle {
+            focusReference.requestFocus()
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle { focusManager.clearFocus() }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).containsExactly(Inactive) }
+    }
+
+    @Test
+    fun removingActiveFocusNode_onFocusEventIsCalledTwice() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusReference = FocusReference()
+        lateinit var addFocusModifier: MutableState<Boolean>
+        rule.setFocusableContent {
+            addFocusModifier = remember { mutableStateOf(true) }
+            Box(
+                modifier = Modifier
+                    .onFocusEvent { focusStates.add(it) }
+                    .focusReference(focusReference)
+                    .then(if (addFocusModifier.value) Modifier.focusModifier() else Modifier)
+            )
+        }
+        rule.runOnIdle {
+            focusReference.requestFocus()
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle { addFocusModifier.value = false }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(focusStates).containsExactly(
+                Inactive, // triggered by focus node's state change.
+                Inactive, // triggered by onFocusEvent node's onModifierChanged().
+            )
+        }
+    }
+
+    @Test
+    fun removingInactiveFocusNode_onFocusEventIsCalledOnce() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        lateinit var addFocusModifier: MutableState<Boolean>
+        rule.setFocusableContent {
+            addFocusModifier = remember { mutableStateOf(true) }
+            Box(
+                modifier = Modifier
+                    .onFocusEvent { focusStates.add(it) }
+                    .then(if (addFocusModifier.value) Modifier.focusModifier() else Modifier)
+            )
+        }
+        rule.runOnIdle { focusStates.clear() }
+
+        // Act.
+        rule.runOnIdle { addFocusModifier.value = false }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).containsExactly(Inactive) }
+    }
+
+    @Test
+    fun addingFocusModifier_onFocusEventIsCalledThrice() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        lateinit var addFocusModifier: MutableState<Boolean>
+        rule.setFocusableContent {
+            addFocusModifier = remember { mutableStateOf(false) }
+            Box(
+                modifier = Modifier
+                    .onFocusEvent { focusStates.add(it) }
+                    .then(if (addFocusModifier.value) Modifier.focusModifier() else Modifier)
+            )
+        }
+        rule.runOnIdle { focusStates.clear() }
+
+        // Act.
+        rule.runOnIdle { addFocusModifier.value = true }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(focusStates).containsExactly(
+                Inactive, // triggered by focus node's attach().
+                Inactive, // triggered by onFocusEvent node's onModifierChanged().
+                Inactive, // triggered by focus node's onModifierChanged().
+            )
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusManagerAmbientTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusManagerAmbientTest.kt
index ccf7260..2d28d79 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusManagerAmbientTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusManagerAmbientTest.kt
@@ -18,12 +18,9 @@
 
 import androidx.compose.foundation.layout.Box
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.ActiveParent
 import androidx.compose.ui.focus.FocusState.Inactive
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.platform.AmbientFocusManager
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -34,7 +31,6 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
 class FocusManagerAmbientTest {
     @get:Rule
@@ -44,20 +40,20 @@
     fun clearFocus_singleLayout() {
         // Arrange.
         lateinit var focusManager: FocusManager
-        lateinit var focusRequester: FocusRequester
+        lateinit var focusReference: FocusReference
         var focusState = Inactive
         rule.setFocusableContent {
             focusManager = AmbientFocusManager.current
-            focusRequester = FocusRequester()
+            focusReference = FocusReference()
             Box(
                 modifier = Modifier
-                    .focusRequester(focusRequester)
-                    .focusObserver { focusState = it }
-                    .focus()
+                    .focusReference(focusReference)
+                    .onFocusChanged { focusState = it }
+                    .focusModifier()
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(focusState).isEqualTo(Active)
         }
 
@@ -72,34 +68,34 @@
     fun clearFocus_entireHierarchyIsCleared() {
         // Arrange.
         lateinit var focusManager: FocusManager
-        lateinit var focusRequester: FocusRequester
+        lateinit var focusReference: FocusReference
         var focusState = Inactive
         var parentFocusState = Inactive
         var grandparentFocusState = Inactive
         rule.setFocusableContent {
             focusManager = AmbientFocusManager.current
-            focusRequester = FocusRequester()
+            focusReference = FocusReference()
             Box(
                 modifier = Modifier
-                    .focusObserver { grandparentFocusState = it }
-                    .focus()
+                    .onFocusChanged { grandparentFocusState = it }
+                    .focusModifier()
             ) {
                 Box(
                     modifier = Modifier
-                        .focusObserver { parentFocusState = it }
-                        .focus()
+                        .onFocusChanged { parentFocusState = it }
+                        .focusModifier()
                 ) {
                     Box(
                         modifier = Modifier
-                            .focusRequester(focusRequester)
-                            .focusObserver { focusState = it }
-                            .focus()
+                            .focusReference(focusReference)
+                            .onFocusChanged { focusState = it }
+                            .focusModifier()
                     )
                 }
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(grandparentFocusState).isEqualTo(ActiveParent)
             assertThat(parentFocusState).isEqualTo(ActiveParent)
             assertThat(focusState).isEqualTo(Active)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusModifierAttachDetachTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusModifierAttachDetachTest.kt
index b8f96cb..8a9de63 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusModifierAttachDetachTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusModifierAttachDetachTest.kt
@@ -21,13 +21,10 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.ActiveParent
 import androidx.compose.ui.focus.FocusState.Captured
 import androidx.compose.ui.focus.FocusState.Inactive
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -37,42 +34,41 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
 class FocusModifierAttachDetachTest {
     @get:Rule
     val rule = createComposeRule()
 
     @Test
-    fun reorderedFocusRequesterModifiers_focusObserverInSameModifierChain() {
+    fun reorderedFocusReferenceModifiers_onFocusChangedInSameModifierChain() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var observingFocusModifier1: MutableState<Boolean>
         rule.setFocusableContent {
-            val focusRequesterModifier = Modifier.focusRequester(focusRequester)
-            val focusObserver = Modifier.focusObserver { focusState = it }
-            val focusModifier1 = Modifier.focus()
-            val focusModifier2 = Modifier.focus()
+            val focusReferenceModifier = Modifier.focusReference(focusReference)
+            val onFocusChanged = Modifier.onFocusChanged { focusState = it }
+            val focusModifier1 = Modifier.focusModifier()
+            val focusModifier2 = Modifier.focusModifier()
             Box {
                 observingFocusModifier1 = remember { mutableStateOf(true) }
                 Box(
                     modifier = if (observingFocusModifier1.value) {
-                        focusObserver
-                            .then(focusRequesterModifier)
+                        onFocusChanged
+                            .then(focusReferenceModifier)
                             .then(focusModifier1)
                             .then(focusModifier2)
                     } else {
                         focusModifier1
-                            .then(focusObserver)
-                            .then(focusRequesterModifier)
+                            .then(onFocusChanged)
+                            .then(focusReferenceModifier)
                             .then(focusModifier2)
                     }
                 )
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(focusState).isEqualTo(Active)
         }
 
@@ -84,37 +80,37 @@
     }
 
     @Test
-    fun removedModifier_focusObserverDoesNotHaveAFocusModifier() {
+    fun removedModifier_onFocusChangedDoesNotHaveAFocusModifier() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
-        lateinit var focusObserverHasFocusModifier: MutableState<Boolean>
+        val focusReference = FocusReference()
+        lateinit var onFocusChangedHasFocusModifier: MutableState<Boolean>
         rule.setFocusableContent {
-            val focusRequesterModifier = Modifier.focusRequester(focusRequester)
-            val focusObserver = Modifier.focusObserver { focusState = it }
-            val focusModifier = Modifier.focus()
+            val focusReferenceModifier = Modifier.focusReference(focusReference)
+            val onFocusChanged = Modifier.onFocusChanged { focusState = it }
+            val focusModifier = Modifier.focusModifier()
             Box {
-                focusObserverHasFocusModifier = remember { mutableStateOf(true) }
+                onFocusChangedHasFocusModifier = remember { mutableStateOf(true) }
                 Box(
-                    modifier = if (focusObserverHasFocusModifier.value) {
-                        focusObserver
-                            .then(focusRequesterModifier)
+                    modifier = if (onFocusChangedHasFocusModifier.value) {
+                        onFocusChanged
+                            .then(focusReferenceModifier)
                             .then(focusModifier)
                     } else {
                         focusModifier
-                            .then(focusObserver)
-                            .then(focusRequesterModifier)
+                            .then(onFocusChanged)
+                            .then(focusReferenceModifier)
                     }
                 )
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(focusState).isEqualTo(Active)
         }
 
         // Act.
-        rule.runOnIdle { focusObserverHasFocusModifier.value = false }
+        rule.runOnIdle { onFocusChangedHasFocusModifier.value = false }
 
         // Assert.
         rule.runOnIdle { assertThat(focusState).isEqualTo(Inactive) }
@@ -124,18 +120,18 @@
     fun removedFocusModifier_withNoNextFocusModifier() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var optionalFocusModifier: MutableState<Boolean>
         rule.setFocusableContent {
             optionalFocusModifier = remember { mutableStateOf(true) }
             Box(
-                modifier = Modifier.focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .then(if (optionalFocusModifier.value) Modifier.focus() else Modifier)
+                modifier = Modifier.onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .then(if (optionalFocusModifier.value) Modifier.focusModifier() else Modifier)
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(focusState).isEqualTo(Active)
         }
 
@@ -150,20 +146,20 @@
     fun removedActiveFocusModifier_pointsToNextFocusModifier() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var optionalFocusModifier: MutableState<Boolean>
         rule.setFocusableContent {
             optionalFocusModifier = remember { mutableStateOf(true) }
             Box(
-                modifier = Modifier.focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .then(if (optionalFocusModifier.value) Modifier.focus() else Modifier)
+                modifier = Modifier.onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .then(if (optionalFocusModifier.value) Modifier.focusModifier() else Modifier)
             ) {
-                Box(modifier = Modifier.focus())
+                Box(modifier = Modifier.focusModifier())
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(focusState).isEqualTo(Active)
         }
 
@@ -178,21 +174,21 @@
     fun removedCapturedFocusModifier_pointsToNextFocusModifier() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var optionalFocusModifier: MutableState<Boolean>
         rule.setFocusableContent {
             optionalFocusModifier = remember { mutableStateOf(true) }
             Box(
-                modifier = Modifier.focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .then(if (optionalFocusModifier.value) Modifier.focus() else Modifier)
+                modifier = Modifier.onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .then(if (optionalFocusModifier.value) Modifier.focusModifier() else Modifier)
             ) {
-                Box(modifier = Modifier.focus())
+                Box(modifier = Modifier.focusModifier())
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
-            focusRequester.captureFocus()
+            focusReference.requestFocus()
+            focusReference.captureFocus()
             assertThat(focusState).isEqualTo(Captured)
         }
 
@@ -207,20 +203,20 @@
     fun removedActiveParentFocusModifier_pointsToNextFocusModifier() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var optionalFocusModifier: MutableState<Boolean>
         rule.setFocusableContent {
             optionalFocusModifier = remember { mutableStateOf(true) }
             Box(
-                modifier = Modifier.focusObserver { focusState = it }
-                    .then(if (optionalFocusModifier.value) Modifier.focus() else Modifier)
-                    .focusRequester(focusRequester)
+                modifier = Modifier.onFocusChanged { focusState = it }
+                    .then(if (optionalFocusModifier.value) Modifier.focusModifier() else Modifier)
+                    .focusReference(focusReference)
             ) {
-                Box(modifier = Modifier.focus())
+                Box(modifier = Modifier.focusModifier())
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(focusState).isEqualTo(ActiveParent)
         }
 
@@ -235,18 +231,18 @@
     fun removedActiveParentFocusModifier_withNoNextFocusModifier() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var optionalFocusModifier: MutableState<Boolean>
         rule.setFocusableContent {
             optionalFocusModifier = remember { mutableStateOf(true) }
             Box(
-                modifier = Modifier.focusObserver { focusState = it }
+                modifier = Modifier.onFocusChanged { focusState = it }
                     .then(
                         if (optionalFocusModifier.value) {
                             Modifier
-                                .focus()
-                                .focusRequester(focusRequester)
-                                .focus()
+                                .focusModifier()
+                                .focusReference(focusReference)
+                                .focusModifier()
                         } else {
                             Modifier
                         }
@@ -254,7 +250,7 @@
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(focusState).isEqualTo(ActiveParent)
         }
 
@@ -270,21 +266,21 @@
         // Arrange.
         var focusState = Inactive
         var parentFocusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var optionalFocusModifiers: MutableState<Boolean>
         rule.setFocusableContent {
             optionalFocusModifiers = remember { mutableStateOf(true) }
             Box(
                 modifier = Modifier
-                    .focusObserver { parentFocusState = it }
-                    .focus()
+                    .onFocusChanged { parentFocusState = it }
+                    .focusModifier()
             ) {
                 Box(
-                    modifier = Modifier.focusObserver { focusState = it }.then(
+                    modifier = Modifier.onFocusChanged { focusState = it }.then(
                         if (optionalFocusModifiers.value) {
-                            Modifier.focus()
-                                .focusRequester(focusRequester)
-                                .focus()
+                            Modifier.focusModifier()
+                                .focusReference(focusReference)
+                                .focusModifier()
                         } else {
                             Modifier
                         }
@@ -293,7 +289,7 @@
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(focusState).isEqualTo(ActiveParent)
             assertThat(parentFocusState).isEqualTo(ActiveParent)
         }
@@ -312,15 +308,15 @@
     fun removedInactiveFocusModifier_pointsToNextFocusModifier() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var optionalFocusModifier: MutableState<Boolean>
         rule.setFocusableContent {
             optionalFocusModifier = remember { mutableStateOf(true) }
             Box(
-                modifier = Modifier.focusObserver { focusState = it }
-                    .then(if (optionalFocusModifier.value) Modifier.focus() else Modifier)
-                    .focusRequester(focusRequester)
-                    .focus()
+                modifier = Modifier.onFocusChanged { focusState = it }
+                    .then(if (optionalFocusModifier.value) Modifier.focusModifier() else Modifier)
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
 
@@ -335,20 +331,20 @@
     fun addedFocusModifier_pointsToTheFocusModifierJustAdded() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var addFocusModifier: MutableState<Boolean>
         rule.setFocusableContent {
             addFocusModifier = remember { mutableStateOf(false) }
             Box(
-                modifier = Modifier.focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .then(if (addFocusModifier.value) Modifier.focus() else Modifier)
+                modifier = Modifier.onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .then(if (addFocusModifier.value) Modifier.focusModifier() else Modifier)
             ) {
-                Box(modifier = Modifier.focus())
+                Box(modifier = Modifier.focusModifier())
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(focusState).isEqualTo(Active)
         }
 
@@ -363,18 +359,18 @@
     fun addedFocusModifier_withNoNextFocusModifier() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var addFocusModifier: MutableState<Boolean>
         rule.setFocusableContent {
             addFocusModifier = remember { mutableStateOf(false) }
             Box(
-                modifier = Modifier.focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .then(if (addFocusModifier.value) Modifier.focus() else Modifier)
+                modifier = Modifier.onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .then(if (addFocusModifier.value) Modifier.focusModifier() else Modifier)
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
             assertThat(focusState).isEqualTo(Inactive)
         }
 
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusRequesterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusReferenceTest.kt
similarity index 68%
rename from compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusRequesterTest.kt
rename to compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusReferenceTest.kt
index 3f04cfa..fc8055b 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusRequesterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusReferenceTest.kt
@@ -20,11 +20,8 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.Inactive
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.platform.AmbientView
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -35,9 +32,8 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
-class FocusRequesterTest {
+class FocusReferenceTest {
     @get:Rule
     val rule = createComposeRule()
 
@@ -45,18 +41,18 @@
     fun requestFocus_noFocusModifierInLayoutNode() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
             )
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Inactive)
@@ -64,22 +60,22 @@
     }
 
     @Test
-    fun requestFocus_focusModifierInLayoutNode_butBeforeFocusRequester() {
+    fun requestFocus_focusModifierInLayoutNode_butBeforeFocusReference() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focus()
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusModifier()
+                    .focusReference(focusReference)
             )
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Inactive)
@@ -90,19 +86,19 @@
     fun requestFocus_focusModifierInLayoutNode() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
@@ -113,20 +109,20 @@
     fun requestFocus_focusModifierInChildLayoutNode() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusRequester(focusRequester)
-                    .focusObserver { focusState = it }
+                    .focusReference(focusReference)
+                    .onFocusChanged { focusState = it }
             ) {
-                Box(modifier = Modifier.focus())
+                Box(modifier = Modifier.focusModifier())
             }
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
@@ -134,25 +130,25 @@
     }
 
     @Test
-    fun requestFocus_focusModifierAndRequesterInChildLayoutNode() {
+    fun requestFocus_focusModifierAndReferenceInChildLayoutNode() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
-                modifier = Modifier.focusObserver { focusState = it }
+                modifier = Modifier.onFocusChanged { focusState = it }
             ) {
                 Box(
                     modifier = Modifier
-                        .focusRequester(focusRequester)
-                        .focus()
+                        .focusReference(focusReference)
+                        .focusModifier()
                 )
             }
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
@@ -163,22 +159,22 @@
     fun requestFocus_focusModifierAndObserverInChildLayoutNode() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
-                modifier = Modifier.focusRequester(focusRequester)
+                modifier = Modifier.focusReference(focusReference)
             ) {
                 Box(
                     modifier = Modifier
-                        .focusObserver { focusState = it }
-                        .focus()
+                        .onFocusChanged { focusState = it }
+                        .focusModifier()
                 )
             }
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
@@ -189,12 +185,12 @@
     fun requestFocus_focusModifierInDistantDescendantLayoutNode() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
             ) {
                 Box {
                     Box {
@@ -202,7 +198,7 @@
                             Box {
                                 Box {
                                     Box(
-                                        modifier = Modifier.focus()
+                                        modifier = Modifier.focusModifier()
                                     )
                                 }
                             }
@@ -214,7 +210,7 @@
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
@@ -226,27 +222,27 @@
         // Arrange.
         var focusState1 = Inactive
         var focusState2 = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Column(
-                modifier = Modifier.focusRequester(focusRequester)
+                modifier = Modifier.focusReference(focusReference)
             ) {
                 Box(
                     modifier = Modifier
-                        .focusObserver { focusState1 = it }
-                        .focus()
+                        .onFocusChanged { focusState1 = it }
+                        .focusModifier()
                 )
                 Box(
                     modifier = Modifier
-                        .focusObserver { focusState2 = it }
-                        .focus()
+                        .onFocusChanged { focusState2 = it }
+                        .focusModifier()
                 )
             }
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState1).isEqualTo(Active)
@@ -255,26 +251,25 @@
     }
 
     @Test
-    fun requestFocusForAnyChild_triggersFocusObserverInParent() {
+    fun requestFocusForAnyChild_triggersonFocusChangedInParent() {
         // Arrange.
         lateinit var hostView: View
         var focusState = Inactive
-        val focusRequester1 = FocusRequester()
-        val focusRequester2 = FocusRequester()
+        val (focusReference1, focusReference2) = FocusReference.createRefs()
         rule.setFocusableContent {
             hostView = AmbientView.current
             Column(
-                modifier = Modifier.focusObserver { focusState = it }
+                modifier = Modifier.onFocusChanged { focusState = it }
             ) {
                 Box(
                     modifier = Modifier
-                        .focusRequester(focusRequester1)
-                        .focus()
+                        .focusReference(focusReference1)
+                        .focusModifier()
                 )
                 Box(
                     modifier = Modifier
-                        .focusRequester(focusRequester2)
-                        .focus()
+                        .focusReference(focusReference2)
+                        .focusModifier()
                 )
             }
         }
@@ -286,7 +281,7 @@
             assertThat(focusState).isEqualTo(Inactive)
 
             // Act.
-            focusRequester1.requestFocus()
+            focusReference1.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
@@ -299,10 +294,10 @@
             assertThat(focusState).isEqualTo(Inactive)
 
             // Act.
-            focusRequester2.requestFocus()
+            focusReference2.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FreeFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FreeFocusTest.kt
index bd92c83..f61f615 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FreeFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FreeFocusTest.kt
@@ -17,15 +17,12 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.foundation.layout.Box
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.ActiveParent
 import androidx.compose.ui.focus.FocusState.Captured
 import androidx.compose.ui.focus.FocusState.Disabled
 import androidx.compose.ui.focus.FocusState.Inactive
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -35,7 +32,6 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
 class FreeFocusTest {
     @get:Rule
@@ -45,19 +41,19 @@
     fun active_freeFocus_retainFocusAsActive() {
         // Arrange.
         var focusState: FocusState = Active
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             Truth.assertThat(success).isTrue()
@@ -69,19 +65,19 @@
     fun activeParent_freeFocus_retainFocusAsActiveParent() {
         // Arrange.
         var focusState: FocusState = ActiveParent
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             Truth.assertThat(success).isFalse()
@@ -93,19 +89,19 @@
     fun captured_freeFocus_changesStateToActive() {
         // Arrange.
         var focusState: FocusState = Captured
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             Truth.assertThat(success).isTrue()
@@ -117,19 +113,19 @@
     fun disabled_freeFocus_retainFocusAsDisabled() {
         // Arrange.
         var focusState: FocusState = Disabled
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             Truth.assertThat(success).isFalse()
@@ -141,23 +137,23 @@
     fun inactive_freeFocus_retainFocusAsInactive() {
         // Arrange.
         var focusState: FocusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             Truth.assertThat(success).isFalse()
             Truth.assertThat(focusState).isEqualTo(Inactive)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/OwnerFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/OwnerFocusTest.kt
index 88a6397..8333c27 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/OwnerFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/OwnerFocusTest.kt
@@ -20,11 +20,8 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.Inactive
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.platform.AmbientView
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -36,7 +33,6 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
 class OwnerFocusTest {
     @get:Rule
@@ -46,19 +42,19 @@
     fun requestFocus_bringsViewInFocus() {
         // Arrange.
         lateinit var ownerView: View
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             ownerView = getOwner()
             Box(
                 modifier = Modifier
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
 
         // Act.
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Assert.
@@ -73,14 +69,14 @@
         // Arrange.
         lateinit var ownerView: View
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             ownerView = getOwner()
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
 
@@ -101,14 +97,14 @@
         // Arrange.
         lateinit var ownerView: View
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             ownerView = getOwner()
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
 
@@ -128,18 +124,18 @@
         // Arrange.
         lateinit var ownerView: View
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             ownerView = getOwner()
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Act.
@@ -158,18 +154,18 @@
         // Arrange.
         lateinit var ownerView: View
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             ownerView = getOwner()
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Act.
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/RequestFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/RequestFocusTest.kt
index 8f92466..c6ce851 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/RequestFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/RequestFocusTest.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.foundation.layout.Box
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.ActiveParent
 import androidx.compose.ui.focus.FocusState.Captured
@@ -32,7 +31,6 @@
 import org.junit.runners.Parameterized
 
 @SmallTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(Parameterized::class)
 class RequestFocusTest(val propagateFocus: Boolean) {
     @get:Rule
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterCaptureFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusReferenceCaptureFocusTest.kt
similarity index 72%
rename from compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterCaptureFocusTest.kt
rename to compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusReferenceCaptureFocusTest.kt
index 6c3630e..ff595b8 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterCaptureFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusReferenceCaptureFocusTest.kt
@@ -17,13 +17,10 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.foundation.layout.Box
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.Captured
 import androidx.compose.ui.focus.FocusState.Inactive
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -33,9 +30,8 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
-class ReusedFocusRequesterCaptureFocusTest {
+class ReusedFocusReferenceCaptureFocusTest {
     @get:Rule
     val rule = createComposeRule()
 
@@ -43,19 +39,19 @@
     fun oneActiveComponent_returnsTrue() {
         // Arrange.
         var focusState = Active
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.captureFocus()
+            val success = focusReference.captureFocus()
 
             // Assert.
             assertThat(success).isTrue()
@@ -67,19 +63,19 @@
     fun oneCapturedComponent_returnsTrue() {
         // Arrange.
         var focusState = Captured
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.captureFocus()
+            val success = focusReference.captureFocus()
 
             // Assert.
             assertThat(success).isTrue()
@@ -91,19 +87,19 @@
     fun oneInactiveComponent_returnsFalse() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.captureFocus()
+            val success = focusReference.captureFocus()
 
             // Assert.
             assertThat(success).isFalse()
@@ -116,25 +112,25 @@
         // Arrange.
         var focusState1 = Inactive
         var focusState2 = Active
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState1 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState1 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState1))
             )
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState2 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState2 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState2))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.captureFocus()
+            val success = focusReference.captureFocus()
 
             // Assert.
             assertThat(success).isTrue()
@@ -148,25 +144,25 @@
         // Arrange.
         var focusState1 = Inactive
         var focusState2 = Captured
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState1 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState1 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState1))
             )
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState2 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState2 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState2))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.captureFocus()
+            val success = focusReference.captureFocus()
 
             // Assert.
             assertThat(success).isTrue()
@@ -180,25 +176,25 @@
         // Arrange.
         var focusState1 = Inactive
         var focusState2 = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState1 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState1 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState1))
             )
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState2 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState2 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState2))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.captureFocus()
+            val success = focusReference.captureFocus()
 
             // Assert.
             assertThat(success).isFalse()
@@ -206,4 +202,4 @@
             assertThat(focusState2).isEqualTo(Inactive)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterFreeFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusReferenceFreeFocusTest.kt
similarity index 72%
rename from compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterFreeFocusTest.kt
rename to compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusReferenceFreeFocusTest.kt
index 8c652a1..67fb2e2 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterFreeFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusReferenceFreeFocusTest.kt
@@ -17,13 +17,10 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.foundation.layout.Box
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.Captured
 import androidx.compose.ui.focus.FocusState.Inactive
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -33,9 +30,8 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
-class ReusedFocusRequesterFreeFocusTest {
+class ReusedFocusReferenceFreeFocusTest {
     @get:Rule
     val rule = createComposeRule()
 
@@ -43,19 +39,19 @@
     fun oneActiveComponent_returnsTrue() {
         // Arrange.
         var focusState = Active
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             assertThat(success).isTrue()
@@ -67,19 +63,19 @@
     fun oneCapturedComponent_returnsTrue() {
         // Arrange.
         var focusState = Captured
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             assertThat(success).isTrue()
@@ -91,19 +87,19 @@
     fun oneInactiveComponent_returnsFalse() {
         // Arrange.
         var focusState = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             assertThat(success).isFalse()
@@ -116,25 +112,25 @@
         // Arrange.
         var focusState1 = Inactive
         var focusState2 = Active
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState1 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState1 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState1))
             )
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState2 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState2 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState2))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             assertThat(success).isTrue()
@@ -148,25 +144,25 @@
         // Arrange.
         var focusState1 = Inactive
         var focusState2 = Captured
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState1 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState1 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState1))
             )
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState2 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState2 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState2))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             assertThat(success).isTrue()
@@ -180,25 +176,25 @@
         // Arrange.
         var focusState1 = Inactive
         var focusState2 = Inactive
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState1 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState1 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState1))
             )
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState2 = it }
-                    .focusRequester(focusRequester)
+                    .onFocusChanged { focusState2 = it }
+                    .focusReference(focusReference)
                     .then(FocusModifier(focusState2))
             )
         }
 
         rule.runOnIdle {
             // Act.
-            val success = focusRequester.freeFocus()
+            val success = focusReference.freeFocus()
 
             // Assert.
             assertThat(success).isFalse()
@@ -206,4 +202,4 @@
             assertThat(focusState2).isEqualTo(Inactive)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusReferenceTest.kt
similarity index 67%
rename from compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterTest.kt
rename to compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusReferenceTest.kt
index 0993a57..96924b9 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusReferenceTest.kt
@@ -18,11 +18,8 @@
 
 import androidx.compose.foundation.layout.Box
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.Inactive
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -32,9 +29,8 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
-class ReusedFocusRequesterTest {
+class ReusedFocusReferenceTest {
     @get:Rule
     val rule = createComposeRule()
 
@@ -42,19 +38,19 @@
     fun oneComponent() {
         // Arrange.
         lateinit var focusState: FocusState
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState).isEqualTo(Active)
@@ -66,25 +62,25 @@
         // Arrange.
         lateinit var focusState1: FocusState
         lateinit var focusState2: FocusState
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState1 = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState1 = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState2 = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState2 = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState1).isEqualTo(Inactive)
@@ -93,36 +89,36 @@
     }
 
     @Test
-    fun focusRequesterUsedWithThreeComponent() {
+    fun focusReferenceUsedWithThreeComponent() {
         // Arrange.
         lateinit var focusState1: FocusState
         lateinit var focusState2: FocusState
         lateinit var focusState3: FocusState
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState1 = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState1 = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState2 = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState2 = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
             Box(
                 modifier = Modifier
-                    .focusObserver { focusState3 = it }
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .onFocusChanged { focusState3 = it }
+                    .focusReference(focusReference)
+                    .focusModifier()
             )
         }
 
         rule.runOnIdle {
             // Act.
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
 
             // Assert.
             assertThat(focusState1).isEqualTo(Inactive)
@@ -130,4 +126,4 @@
             assertThat(focusState3).isEqualTo(Active)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/SetRootFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/SetRootFocusTest.kt
index d6a6633..40bc869 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/SetRootFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/SetRootFocusTest.kt
@@ -20,9 +20,6 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
-import androidx.compose.ui.focusObserver
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -36,7 +33,6 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
 class SetRootFocusTest {
     @get:Rule
@@ -53,17 +49,15 @@
         rule.setContent {
             Column {
                 // TODO(b/163725615): Remove this after clickable is made focusable.
-                val focusRequester = FocusRequester()
+                val focusReference = FocusReference()
                 BasicText(
                     text = "ClickableText",
                     modifier = Modifier
                         .testTag(focusable)
-                        .clickable {
-                            focusRequester.requestFocus()
-                        }
-                        .focusRequester(focusRequester)
-                        .focusObserver { isFocused = it.isFocused }
-                        .focus()
+                        .clickable { focusReference.requestFocus() }
+                        .focusReference(focusReference)
+                        .onFocusChanged { isFocused = it.isFocused }
+                        .focusModifier()
                 )
                 BasicText(
                     text = "Non Clickable Text",
@@ -80,4 +74,4 @@
         // Assert.
         rule.runOnIdle { assertThat(isFocused).isFalse() }
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/nestedscroll/NestedScrollModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/nestedscroll/NestedScrollModifierTest.kt
new file mode 100644
index 0000000..513b0f6
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/nestedscroll/NestedScrollModifierTest.kt
@@ -0,0 +1,1062 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.gesture.nestedscroll
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.minus
+import androidx.compose.ui.unit.plus
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+class NestedScrollModifierTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val preScrollOffset = Offset(120f, 120f)
+    private val scrollOffset = Offset(125f, 125f)
+    private val scrollLeftOffset = Offset(32f, 32f)
+    private val preFling = Velocity(Offset(120f, 120f))
+    private val postFlingConsumed = Velocity(Offset(151f, 63f))
+    private val postFlingLeft = Velocity(Offset(11f, 13f))
+
+    @Test
+    fun nestedScroll_twoNodes_orderTest() {
+        var counter = 0
+        val childConnection = object : NestedScrollConnection {}
+        val parentConnection = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(counter).isEqualTo(1)
+                counter++
+                return Offset.Zero
+            }
+
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(counter).isEqualTo(3)
+                counter++
+                return Offset.Zero
+            }
+
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(counter).isEqualTo(5)
+                counter++
+                return Velocity.Zero
+            }
+
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(counter).isEqualTo(7)
+                counter++
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.size(100.dp).nestedScroll(parentConnection)) {
+                Box(
+                    Modifier.size(100.dp).nestedScroll(childConnection, childDispatcher)
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(counter).isEqualTo(0)
+            counter++
+
+            childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+            assertThat(counter).isEqualTo(2)
+            counter++
+
+            childDispatcher
+                .dispatchPostScroll(scrollOffset, scrollLeftOffset, NestedScrollSource.Drag)
+            assertThat(counter).isEqualTo(4)
+            counter++
+
+            childDispatcher.dispatchPreFling(preFling)
+            assertThat(counter).isEqualTo(6)
+            counter++
+
+            childDispatcher.dispatchPostFling(postFlingConsumed, postFlingLeft)
+            assertThat(counter).isEqualTo(8)
+            counter++
+        }
+    }
+
+    @Test
+    fun nestedScroll_NNodes_orderTest_preScroll() {
+        var counter = 0
+        val childConnection = object : NestedScrollConnection {}
+        val parentConnection = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(counter).isEqualTo(2)
+                counter++
+                return Offset.Zero
+            }
+        }
+        val grandParentConnection = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(counter).isEqualTo(1)
+                counter++
+                return Offset.Zero
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.size(100.dp).nestedScroll(grandParentConnection)) {
+                Box(Modifier.size(100.dp).nestedScroll(parentConnection)) {
+                    Box(
+                        Modifier.size(100.dp).nestedScroll(childConnection, childDispatcher)
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(counter).isEqualTo(0)
+            counter++
+
+            childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+            assertThat(counter).isEqualTo(3)
+            counter++
+        }
+    }
+
+    @Test
+    fun nestedScroll_NNodes_orderTest_scroll() {
+        var counter = 0
+        val childConnection = object : NestedScrollConnection {}
+        val parentConnection = object : NestedScrollConnection {
+
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(counter).isEqualTo(1)
+                counter++
+                return Offset.Zero
+            }
+        }
+        val grandParentConnection = object : NestedScrollConnection {
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(counter).isEqualTo(2)
+                counter++
+                return Offset.Zero
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.size(100.dp).nestedScroll(grandParentConnection)) {
+                Box(Modifier.size(100.dp).nestedScroll(parentConnection)) {
+                    Box(
+                        Modifier.size(100.dp).nestedScroll(childConnection, childDispatcher)
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(counter).isEqualTo(0)
+            counter++
+
+            childDispatcher
+                .dispatchPostScroll(scrollOffset, scrollLeftOffset, NestedScrollSource.Drag)
+            assertThat(counter).isEqualTo(3)
+            counter++
+        }
+    }
+
+    @Test
+    fun nestedScroll_NNodes_orderTest_preFling() {
+        var counter = 0
+        val childConnection = object : NestedScrollConnection {}
+        val parentConnection = object : NestedScrollConnection {
+
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(counter).isEqualTo(2)
+                counter++
+                return Velocity.Zero
+            }
+        }
+        val grandParentConnection = object : NestedScrollConnection {
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(counter).isEqualTo(1)
+                counter++
+                return Velocity.Zero
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.size(100.dp).nestedScroll(grandParentConnection)) {
+                Box(Modifier.size(100.dp).nestedScroll(parentConnection)) {
+                    Box(
+                        Modifier.size(100.dp).nestedScroll(childConnection, childDispatcher)
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(counter).isEqualTo(0)
+            counter++
+
+            childDispatcher.dispatchPreFling(preFling)
+            assertThat(counter).isEqualTo(3)
+            counter++
+        }
+    }
+
+    @Test
+    fun nestedScroll_NNodes_orderTest_fling() {
+        var counter = 0
+        val childConnection = object : NestedScrollConnection {}
+        val parentConnection = object : NestedScrollConnection {
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(counter).isEqualTo(1)
+                counter++
+                onFinished.invoke(Velocity.Zero)
+            }
+        }
+        val grandParentConnection = object : NestedScrollConnection {
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(counter).isEqualTo(2)
+                counter++
+                onFinished.invoke(Velocity.Zero)
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.size(100.dp).nestedScroll(grandParentConnection)) {
+                Box(Modifier.size(100.dp).nestedScroll(parentConnection)) {
+                    Box(
+                        Modifier.size(100.dp).nestedScroll(childConnection, childDispatcher)
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(counter).isEqualTo(0)
+            counter++
+
+            childDispatcher.dispatchPostFling(postFlingConsumed, postFlingLeft)
+
+            assertThat(counter).isEqualTo(3)
+            counter++
+        }
+    }
+
+    @Test
+    fun nestedScroll_twoNodes_hierarchyDispatch() {
+        val preScrollReturn = Offset(60f, 30f)
+        val preFlingReturn = Velocity(Offset(154f, 56f))
+        var currentsource = NestedScrollSource.Drag
+
+        val childConnection = object : NestedScrollConnection {}
+        val parentConnection = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(available).isEqualTo(preScrollOffset)
+                assertThat(source).isEqualTo(currentsource)
+                return preScrollReturn
+            }
+
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(consumed).isEqualTo(scrollOffset)
+                assertThat(available).isEqualTo(scrollLeftOffset)
+                assertThat(source).isEqualTo(currentsource)
+                return Offset.Zero
+            }
+
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(available).isEqualTo(preFling)
+                return preFlingReturn
+            }
+
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(consumed).isEqualTo(postFlingConsumed)
+                assertThat(available).isEqualTo(postFlingLeft)
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.size(100.dp).nestedScroll(parentConnection)) {
+                Box(
+                    Modifier.size(100.dp).nestedScroll(childConnection, childDispatcher)
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            val preRes = childDispatcher.dispatchPreScroll(preScrollOffset, currentsource)
+            assertThat(preRes).isEqualTo(preScrollReturn)
+
+            childDispatcher.dispatchPostScroll(scrollOffset, scrollLeftOffset, currentsource)
+            // flip to fling to test again below
+            currentsource = NestedScrollSource.Fling
+        }
+
+        rule.runOnIdle {
+            val preRes = childDispatcher.dispatchPreScroll(preScrollOffset, currentsource)
+            assertThat(preRes).isEqualTo(preScrollReturn)
+
+            childDispatcher.dispatchPostScroll(scrollOffset, scrollLeftOffset, currentsource)
+        }
+
+        rule.runOnIdle {
+            val preFlingRes = childDispatcher.dispatchPreFling(preFling)
+            assertThat(preFlingRes).isEqualTo(preFlingReturn)
+            childDispatcher.dispatchPostFling(postFlingConsumed, postFlingLeft)
+        }
+    }
+
+    @Test
+    fun nestedScroll_deltaCalculation_preScroll() {
+        val dispatchedPreScroll = Offset(10f, 10f)
+        val grandParentConsumesPreScroll = Offset(2f, 2f)
+        val parentConsumedPreScroll = Offset(1f, 1f)
+
+        val childConnection = object : NestedScrollConnection {}
+        val grandParentConnection = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(available).isEqualTo(dispatchedPreScroll)
+                return grandParentConsumesPreScroll
+            }
+        }
+        val parentConnection = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(available)
+                    .isEqualTo(dispatchedPreScroll - grandParentConsumesPreScroll)
+                return parentConsumedPreScroll
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.size(100.dp).nestedScroll(grandParentConnection)) {
+                Box(Modifier.size(100.dp).nestedScroll(parentConnection)) {
+                    Box(
+                        Modifier.size(100.dp).nestedScroll(childConnection, childDispatcher)
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            val preRes =
+                childDispatcher.dispatchPreScroll(dispatchedPreScroll, NestedScrollSource.Drag)
+            assertThat(preRes).isEqualTo(grandParentConsumesPreScroll + parentConsumedPreScroll)
+        }
+    }
+
+    @Test
+    fun nestedScroll_deltaCalculation_scroll() {
+        val dispatchedConsumedScroll = Offset(4f, 4f)
+        val dispatchedScroll = Offset(10f, 10f)
+        val grandParentConsumedScroll = Offset(2f, 2f)
+        val parentConsumedScroll = Offset(1f, 1f)
+
+        val childConnection = object : NestedScrollConnection {}
+        val grandParentConnection = object : NestedScrollConnection {
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(consumed).isEqualTo(parentConsumedScroll + dispatchedConsumedScroll)
+                assertThat(available).isEqualTo(dispatchedScroll - parentConsumedScroll)
+                return grandParentConsumedScroll
+            }
+        }
+        val parentConnection = object : NestedScrollConnection {
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(consumed).isEqualTo(dispatchedConsumedScroll)
+                assertThat(available).isEqualTo(dispatchedScroll)
+                return parentConsumedScroll
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.size(100.dp).nestedScroll(grandParentConnection)) {
+                Box(Modifier.size(100.dp).nestedScroll(parentConnection)) {
+                    Box(
+                        Modifier.size(100.dp).nestedScroll(childConnection, childDispatcher)
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            childDispatcher.dispatchPostScroll(
+                dispatchedConsumedScroll,
+                dispatchedScroll,
+                NestedScrollSource.Drag
+            )
+        }
+    }
+
+    @Test
+    fun nestedScroll_deltaCalculation_preFling() {
+        val dispatchedVelocity = Velocity(Offset(10f, 10f))
+        val grandParentConsumesPreFling = Velocity(Offset(2f, 2f))
+        val parentConsumedPreFling = Velocity(Offset(1f, 1f))
+
+        val childConnection = object : NestedScrollConnection {}
+        val grandParentConnection = object : NestedScrollConnection {
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(available).isEqualTo(dispatchedVelocity)
+                return grandParentConsumesPreFling
+            }
+        }
+        val parentConnection = object : NestedScrollConnection {
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(available)
+                    .isEqualTo(dispatchedVelocity - grandParentConsumesPreFling)
+                return parentConsumedPreFling
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.size(100.dp).nestedScroll(grandParentConnection)) {
+                Box(Modifier.size(100.dp).nestedScroll(parentConnection)) {
+                    Box(
+                        Modifier.size(100.dp).nestedScroll(childConnection, childDispatcher)
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            val preRes = childDispatcher.dispatchPreFling(dispatchedVelocity)
+            assertThat(preRes).isEqualTo(grandParentConsumesPreFling + parentConsumedPreFling)
+        }
+    }
+
+    @Test
+    fun nestedScroll_deltaCalculation_fling() {
+        val dispatchedConsumedVelocity = Velocity(Offset(4f, 4f))
+        val dispatchedLeftVelocity = Velocity(Offset(10f, 10f))
+        val grandParentConsumedPostFling = Velocity(Offset(2f, 2f))
+        val parentConsumedPostFling = Velocity(Offset(1f, 1f))
+
+        val childConnection = object : NestedScrollConnection {}
+        val grandParentConnection = object : NestedScrollConnection {
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(consumed)
+                    .isEqualTo(parentConsumedPostFling + dispatchedConsumedVelocity)
+                assertThat(available)
+                    .isEqualTo(dispatchedLeftVelocity - parentConsumedPostFling)
+                return onFinished(grandParentConsumedPostFling)
+            }
+        }
+        val parentConnection = object : NestedScrollConnection {
+
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(consumed).isEqualTo(dispatchedConsumedVelocity)
+                assertThat(available).isEqualTo(dispatchedLeftVelocity)
+                onFinished(parentConsumedPostFling)
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.size(100.dp).nestedScroll(grandParentConnection)) {
+                Box(Modifier.size(100.dp).nestedScroll(parentConnection)) {
+                    Box(
+                        Modifier.size(100.dp).nestedScroll(childConnection, childDispatcher)
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            childDispatcher.dispatchPostFling(dispatchedConsumedVelocity, dispatchedLeftVelocity)
+        }
+    }
+
+    @Test
+    fun nestedScroll_twoNodes_flatDispatch() {
+        val preScrollReturn = Offset(60f, 30f)
+        val preFlingReturn = Velocity(Offset(154f, 56f))
+        var currentsource = NestedScrollSource.Drag
+
+        val childConnection = object : NestedScrollConnection {}
+        val parentConnection = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(available).isEqualTo(preScrollOffset)
+                assertThat(source).isEqualTo(currentsource)
+                return preScrollReturn
+            }
+
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(consumed).isEqualTo(scrollOffset)
+                assertThat(available).isEqualTo(scrollLeftOffset)
+                assertThat(source).isEqualTo(currentsource)
+                return Offset.Zero
+            }
+
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(available).isEqualTo(preFling)
+                return preFlingReturn
+            }
+
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(consumed).isEqualTo(postFlingConsumed)
+                assertThat(available).isEqualTo(postFlingLeft)
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(
+                Modifier
+                    .size(100.dp)
+                    .nestedScroll(parentConnection) // parent
+                    .nestedScroll(childConnection, childDispatcher) // child
+            )
+        }
+
+        rule.runOnIdle {
+            val preRes = childDispatcher.dispatchPreScroll(preScrollOffset, currentsource)
+            assertThat(preRes).isEqualTo(preScrollReturn)
+
+            childDispatcher.dispatchPostScroll(scrollOffset, scrollLeftOffset, currentsource)
+            // flip to fling to test again below
+            currentsource = NestedScrollSource.Fling
+        }
+
+        rule.runOnIdle {
+            val preRes = childDispatcher.dispatchPreScroll(preScrollOffset, currentsource)
+            assertThat(preRes).isEqualTo(preScrollReturn)
+
+            childDispatcher.dispatchPostScroll(scrollOffset, scrollLeftOffset, currentsource)
+        }
+
+        rule.runOnIdle {
+            val preFlingRes = childDispatcher.dispatchPreFling(preFling)
+            assertThat(preFlingRes).isEqualTo(preFlingReturn)
+            childDispatcher.dispatchPostFling(postFlingConsumed, postFlingLeft)
+        }
+    }
+
+    @Test
+    fun nestedScroll_shouldNotCalledSelfConnection() {
+        val childConnection = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertWithMessage("self connection shouldn't be called").fail()
+                return Offset.Zero
+            }
+
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertWithMessage("self connection shouldn't be called").fail()
+                return Offset.Zero
+            }
+
+            override fun onPreFling(available: Velocity): Velocity {
+                assertWithMessage("self connection shouldn't be called").fail()
+                return Velocity.Zero
+            }
+
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertWithMessage("self connection shouldn't be called").fail()
+            }
+        }
+        val parentConnection = object : NestedScrollConnection {}
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            Box(Modifier.nestedScroll(parentConnection)) {
+                Box(Modifier.nestedScroll(childConnection, childDispatcher))
+            }
+        }
+
+        rule.runOnIdle {
+            childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+            childDispatcher
+                .dispatchPostScroll(scrollOffset, scrollLeftOffset, NestedScrollSource.Fling)
+        }
+
+        rule.runOnIdle {
+            childDispatcher.dispatchPreFling(preFling)
+            childDispatcher.dispatchPostFling(postFlingConsumed, postFlingLeft)
+        }
+    }
+
+    @Test
+    fun nestedScroll_hierarchyDispatch_rootParentRemoval() {
+        testRootParentAdditionRemoval { root, child ->
+            Box(Modifier.size(100.dp).then(root)) {
+                Box(child)
+            }
+        }
+    }
+
+    @Test
+    fun nestedScroll_flatDispatch_rootParentRemoval() {
+        testRootParentAdditionRemoval { root, child ->
+            Box(Modifier.then(root).then(child))
+        }
+    }
+
+    @Test
+    fun nestedScroll_flatDispatch_longChain_rootParentRemoval() {
+        testRootParentAdditionRemoval { root, child ->
+            // insert a few random modifiers so it's more realistic example of wrapper re-usage
+            Box(Modifier.size(100.dp).then(root).padding(5.dp).size(50.dp).then(child))
+        }
+    }
+
+    @Test
+    fun nestedScroll_hierarchyDispatch_middleParentRemoval() {
+        testMiddleParentAdditionRemoval { rootMod, middleMod, childMod ->
+            // random boxes to emulate nesting
+            Box(Modifier.size(100.dp).then(rootMod)) {
+                Box {
+                    Box(Modifier.size(100.dp).then(middleMod)) {
+                        Box {
+                            Box(childMod)
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun nestedScroll_flatDispatch_middleParentRemoval() {
+        testMiddleParentAdditionRemoval { rootMod, middleMod, childMod ->
+            Box(
+                Modifier
+                    .then(rootMod)
+                    .then(middleMod)
+                    .then(childMod)
+            )
+        }
+    }
+
+    @Test
+    fun nestedScroll_flatDispatch_longChain_middleParentRemoval() {
+        testMiddleParentAdditionRemoval { rootMod, middleMod, childMod ->
+            // insert a few random modifiers so it's more realistic example of wrapper re-usage
+            Box(
+                Modifier
+                    .size(100.dp)
+                    .then(rootMod)
+                    .size(90.dp)
+                    .clipToBounds()
+                    .then(middleMod)
+                    .padding(5.dp)
+                    .then(childMod)
+            )
+        }
+    }
+
+    @Test
+    fun nestedScroll_flatDispatch_runtimeSwapChange_orderTest() {
+        val preScrollReturn = Offset(60f, 30f)
+        val preFlingReturn = Velocity(Offset(154f, 56f))
+        var counter = 0
+
+        val isConnection1Parent = mutableStateOf(true)
+        val childConnection = object : NestedScrollConnection {}
+        val connection1 = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 1 else 2)
+                counter++
+                return preScrollReturn
+            }
+
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 2 else 1)
+                counter++
+                return Offset.Zero
+            }
+
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 1 else 2)
+                counter++
+                return preFlingReturn
+            }
+
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 2 else 1)
+                counter++
+                onFinished.invoke(Velocity.Zero)
+            }
+        }
+        val connection2 = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 2 else 1)
+                counter++
+                return preScrollReturn
+            }
+
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 1 else 2)
+                counter++
+                return Offset.Zero
+            }
+
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 2 else 1)
+                counter++
+                return preFlingReturn
+            }
+
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 1 else 2)
+                counter++
+                onFinished.invoke(Velocity.Zero)
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            val nestedScrollParents = if (isConnection1Parent.value) {
+                Modifier.nestedScroll(connection1).nestedScroll(connection2)
+            } else {
+                Modifier.nestedScroll(connection2).nestedScroll(connection1)
+            }
+            Box(
+                Modifier
+                    .size(100.dp)
+                    .then(nestedScrollParents)
+                    .nestedScroll(childConnection, childDispatcher)
+            )
+        }
+
+        repeat(2) {
+            rule.runOnIdle {
+                counter = 1
+
+                childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+                assertThat(counter).isEqualTo(3)
+                counter = 1
+
+                childDispatcher.dispatchPostScroll(
+                    scrollOffset,
+                    scrollLeftOffset,
+                    NestedScrollSource.Drag
+                )
+                assertThat(counter).isEqualTo(3)
+                counter = 1
+
+                childDispatcher.dispatchPreFling(preFling)
+                assertThat(counter).isEqualTo(3)
+                counter = 1
+
+                childDispatcher.dispatchPostFling(postFlingConsumed, postFlingLeft)
+                assertThat(counter).isEqualTo(3)
+                counter = 1
+
+                isConnection1Parent.value = !isConnection1Parent.value
+            }
+        }
+    }
+
+    @Test
+    fun nestedScroll_hierarchyDispatch_runtimeSwapChange_orderTest() {
+        val preScrollReturn = Offset(60f, 30f)
+        val preFlingReturn = Velocity(Offset(154f, 56f))
+        var counter = 0
+
+        val isConnection1Parent = mutableStateOf(true)
+        val childConnection = object : NestedScrollConnection {}
+        val connection1 = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 1 else 2)
+                counter++
+                return preScrollReturn
+            }
+
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 2 else 1)
+                counter++
+                return Offset.Zero
+            }
+
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 1 else 2)
+                counter++
+                return preFlingReturn
+            }
+
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 2 else 1)
+                counter++
+                onFinished.invoke(Velocity.Zero)
+            }
+        }
+        val connection2 = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 2 else 1)
+                counter++
+                return preScrollReturn
+            }
+
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource
+            ): Offset {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 1 else 2)
+                counter++
+                return Offset.Zero
+            }
+
+            override fun onPreFling(available: Velocity): Velocity {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 2 else 1)
+                counter++
+                return preFlingReturn
+            }
+
+            override fun onPostFling(
+                consumed: Velocity,
+                available: Velocity,
+                onFinished: (Velocity) -> Unit
+            ) {
+                assertThat(counter).isEqualTo(if (isConnection1Parent.value) 1 else 2)
+                counter++
+                onFinished.invoke(Velocity.Zero)
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            val outerBoxConnection = if (isConnection1Parent.value) connection1 else connection2
+            val innerBoxConnection = if (isConnection1Parent.value) connection2 else connection1
+            Box(Modifier.size(100.dp).nestedScroll(outerBoxConnection)) {
+                Box(Modifier.size(100.dp).nestedScroll(innerBoxConnection)) {
+                    Box(Modifier.nestedScroll(childConnection, childDispatcher))
+                }
+            }
+        }
+
+        repeat(2) {
+            rule.runOnIdle {
+                counter = 1
+
+                childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+                assertThat(counter).isEqualTo(3)
+                counter = 1
+
+                childDispatcher.dispatchPostScroll(
+                    scrollOffset,
+                    scrollLeftOffset,
+                    NestedScrollSource.Drag
+                )
+                assertThat(counter).isEqualTo(3)
+                counter = 1
+
+                childDispatcher.dispatchPreFling(preFling)
+                assertThat(counter).isEqualTo(3)
+                counter = 1
+
+                childDispatcher.dispatchPostFling(postFlingConsumed, postFlingLeft)
+                assertThat(counter).isEqualTo(3)
+                counter = 1
+
+                isConnection1Parent.value = !isConnection1Parent.value
+            }
+        }
+    }
+
+    // helper functions
+
+    private fun testMiddleParentAdditionRemoval(
+        content: @Composable (root: Modifier, middle: Modifier, child: Modifier) -> Unit
+    ) {
+        val rootParentPreConsumed = Offset(60f, 30f)
+        val parentToRemovePreConsumed = Offset(21f, 44f)
+
+        val emitNewParent = mutableStateOf(true)
+        val childConnection = object : NestedScrollConnection {}
+        val rootParent = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                return rootParentPreConsumed
+            }
+        }
+        val parentToRemove = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                if (!emitNewParent.value) {
+                    assertWithMessage("Shouldn't be called when not emitted").fail()
+                }
+                return parentToRemovePreConsumed
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            val maybeNestedScroll =
+                if (emitNewParent.value) Modifier.nestedScroll(parentToRemove) else Modifier
+            content.invoke(
+                Modifier.nestedScroll(rootParent),
+                maybeNestedScroll,
+                Modifier.nestedScroll(childConnection, childDispatcher)
+            )
+        }
+
+        rule.runOnIdle {
+            val res =
+                childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+            assertThat(res).isEqualTo(rootParentPreConsumed + parentToRemovePreConsumed)
+
+            emitNewParent.value = false
+        }
+
+        rule.runOnIdle {
+            val res =
+                childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+            assertThat(res).isEqualTo(rootParentPreConsumed)
+
+            emitNewParent.value = true
+        }
+
+        rule.runOnIdle {
+            val res =
+                childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+            assertThat(res).isEqualTo(rootParentPreConsumed + parentToRemovePreConsumed)
+        }
+    }
+
+    private fun testRootParentAdditionRemoval(
+        content: @Composable (root: Modifier, child: Modifier) -> Unit
+    ) {
+        val preScrollReturn = Offset(60f, 30f)
+
+        val emitParentNestedScroll = mutableStateOf(true)
+        val childConnection = object : NestedScrollConnection {}
+        val parent = object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                return preScrollReturn
+            }
+        }
+        val childDispatcher = NestedScrollDispatcher()
+        rule.setContent {
+            val maybeNestedScroll =
+                if (emitParentNestedScroll.value) Modifier.nestedScroll(parent) else Modifier
+            content.invoke(
+                maybeNestedScroll,
+                Modifier.nestedScroll(childConnection, childDispatcher)
+            )
+        }
+
+        rule.runOnIdle {
+            val res = childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+            assertThat(res).isEqualTo(preScrollReturn)
+
+            emitParentNestedScroll.value = false
+        }
+
+        rule.runOnIdle {
+            val res = childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+            assertThat(res).isEqualTo(Offset.Zero)
+            emitParentNestedScroll.value = true
+        }
+
+        rule.runOnIdle {
+            val res = childDispatcher.dispatchPreScroll(preScrollOffset, NestedScrollSource.Drag)
+            assertThat(res).isEqualTo(preScrollReturn)
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt
index 8ed2323..b694d9a 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt
@@ -22,11 +22,10 @@
 import android.view.View
 import androidx.compose.foundation.layout.Box
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusModifier
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.focus.setFocusableContent
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.input.key.Key.Companion.A
 import androidx.compose.ui.input.key.KeyEventType.KeyDown
 import androidx.compose.ui.input.key.KeyEventType.KeyUp
@@ -46,10 +45,6 @@
  */
 @SmallTest
 @RunWith(Parameterized::class)
-@OptIn(
-    ExperimentalFocus::class,
-    ExperimentalKeyInput::class
-)
 class AndroidProcessKeyInputTest(val keyEventAction: Int) {
     @get:Rule
     val rule = createComposeRule()
@@ -65,21 +60,21 @@
         // Arrange.
         lateinit var ownerView: View
         lateinit var receivedKeyEvent: KeyEvent
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         rule.setFocusableContent {
             ownerView = AmbientView.current
             Box(
                 modifier = Modifier
-                    .focusRequester(focusRequester)
-                    .focus()
-                    .keyInputFilter {
+                    .focusReference(focusReference)
+                    .focusModifier()
+                    .onKeyEvent {
                         receivedKeyEvent = it
                         true
                     }
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Act.
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/KeyInputUtil.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/KeyInputUtil.kt
index da95e3d..fdc0120 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/KeyInputUtil.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/KeyInputUtil.kt
@@ -25,7 +25,6 @@
  * The [KeyEvent] is usually created by the system. This function creates an instance of
  * [KeyEvent] that can be used in tests.
  */
-@OptIn(ExperimentalKeyInput::class)
 fun keyEvent(key: Key, keyEventType: KeyEventType, androidMetaKeys: Int = 0): KeyEvent {
     val action = when (keyEventType) {
         KeyEventType.KeyDown -> ACTION_DOWN
@@ -40,7 +39,6 @@
  *  [KeyEventAndroid] inline classes do not allow
  *  overriding the equals() function.  So we use this util function to compare KeyEvents.
  */
-@OptIn(ExperimentalKeyInput::class)
 fun KeyEvent.assertEqualTo(expected: KeyEvent) {
     Truth.assertThat(key).isEqualTo(expected.key)
     Truth.assertThat(type).isEqualTo(expected.type)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/MetaKeyTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/MetaKeyTest.kt
index d5ca9df..ded752b 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/MetaKeyTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/MetaKeyTest.kt
@@ -28,7 +28,6 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalKeyInput::class)
 class MetaKeyTest {
 
     @Test
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/ProcessKeyInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/ProcessKeyInputTest.kt
index a95401c..3f60cd4 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/ProcessKeyInputTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/ProcessKeyInputTest.kt
@@ -18,11 +18,10 @@
 
 import androidx.compose.foundation.layout.Box
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusModifier
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.focus.setFocusableContent
-import androidx.compose.ui.focusRequester
 import androidx.compose.ui.input.key.Key.Companion.A
 import androidx.compose.ui.input.key.KeyEventType.KeyUp
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -38,10 +37,6 @@
 @Suppress("DEPRECATION")
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(
-    ExperimentalFocus::class,
-    ExperimentalKeyInput::class
-)
 class ProcessKeyInputTest {
     @get:Rule
     val rule = createComposeRule()
@@ -61,7 +56,7 @@
     fun noFocusModifier_throwsException() {
         // Arrange.
         rule.setFocusableContent {
-            Box(modifier = Modifier.keyInputFilter { true })
+            Box(modifier = Modifier.onKeyEvent { true })
         }
 
         // Act.
@@ -73,7 +68,7 @@
 
         // Arrange.
         rule.setFocusableContent {
-            Box(modifier = Modifier.focus().keyInputFilter { true })
+            Box(modifier = Modifier.focusModifier().onKeyEvent { true })
         }
 
         // Act.
@@ -83,21 +78,21 @@
     @Test
     fun onKeyEvent_triggered() {
         // Arrange.
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var receivedKeyEvent: KeyEvent
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusRequester(focusRequester)
-                    .focus()
-                    .keyInputFilter {
+                    .focusReference(focusReference)
+                    .focusModifier()
+                    .onKeyEvent {
                         receivedKeyEvent = it
                         true
                     }
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Act.
@@ -113,21 +108,21 @@
     @Test
     fun onPreviewKeyEvent_triggered() {
         // Arrange.
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var receivedKeyEvent: KeyEvent
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusRequester(focusRequester)
-                    .focus()
-                    .previewKeyInputFilter {
+                    .focusReference(focusReference)
+                    .focusModifier()
+                    .onPreviewKeyEvent {
                         receivedKeyEvent = it
                         true
                     }
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Act.
@@ -143,26 +138,26 @@
     @Test
     fun onKeyEventNotTriggered_ifOnPreviewKeyEventConsumesEvent() {
         // Arrange.
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         lateinit var receivedPreviewKeyEvent: KeyEvent
         var receivedKeyEvent: KeyEvent? = null
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusRequester(focusRequester)
-                    .focus()
-                    .keyInputFilter {
+                    .focusReference(focusReference)
+                    .focusModifier()
+                    .onKeyEvent {
                         receivedKeyEvent = it
                         true
                     }
-                    .previewKeyInputFilter {
+                    .onPreviewKeyEvent {
                         receivedPreviewKeyEvent = it
                         true
                     }
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Act.
@@ -178,27 +173,27 @@
     @Test
     fun onKeyEvent_triggeredAfter_onPreviewKeyEvent() {
         // Arrange.
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         var triggerIndex = 1
         var onKeyEventTrigger = 0
         var onPreviewKeyEventTrigger = 0
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focusRequester(focusRequester)
-                    .focus()
-                    .keyInputFilter {
+                    .focusReference(focusReference)
+                    .focusModifier()
+                    .onKeyEvent {
                         onKeyEventTrigger = triggerIndex++
                         true
                     }
-                    .previewKeyInputFilter {
+                    .onPreviewKeyEvent {
                         onPreviewKeyEventTrigger = triggerIndex++
                         false
                     }
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Act.
@@ -214,7 +209,7 @@
     @Test
     fun parent_child() {
         // Arrange.
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         var triggerIndex = 1
         var parentOnKeyEventTrigger = 0
         var parentOnPreviewKeyEventTrigger = 0
@@ -223,25 +218,25 @@
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focus()
-                    .keyInputFilter {
+                    .focusModifier()
+                    .onKeyEvent {
                         parentOnKeyEventTrigger = triggerIndex++
                         false
                     }
-                    .previewKeyInputFilter {
+                    .onPreviewKeyEvent {
                         parentOnPreviewKeyEventTrigger = triggerIndex++
                         false
                     }
             ) {
                 Box(
                     modifier = Modifier
-                        .focusRequester(focusRequester)
-                        .focus()
-                        .keyInputFilter {
+                        .focusReference(focusReference)
+                        .focusModifier()
+                        .onKeyEvent {
                             childOnKeyEventTrigger = triggerIndex++
                             false
                         }
-                        .previewKeyInputFilter {
+                        .onPreviewKeyEvent {
                             childOnPreviewKeyEventTrigger = triggerIndex++
                             false
                         }
@@ -249,7 +244,7 @@
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Act.
@@ -267,7 +262,7 @@
     @Test
     fun parent_child_noFocusModifierForParent() {
         // Arrange.
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         var triggerIndex = 1
         var parentOnKeyEventTrigger = 0
         var parentOnPreviewKeyEventTrigger = 0
@@ -276,24 +271,24 @@
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .keyInputFilter {
+                    .onKeyEvent {
                         parentOnKeyEventTrigger = triggerIndex++
                         false
                     }
-                    .previewKeyInputFilter {
+                    .onPreviewKeyEvent {
                         parentOnPreviewKeyEventTrigger = triggerIndex++
                         false
                     }
             ) {
                 Box(
                     modifier = Modifier
-                        .focusRequester(focusRequester)
-                        .focus()
-                        .keyInputFilter {
+                        .focusReference(focusReference)
+                        .focusModifier()
+                        .onKeyEvent {
                             childOnKeyEventTrigger = triggerIndex++
                             false
                         }
-                        .previewKeyInputFilter {
+                        .onPreviewKeyEvent {
                             childOnPreviewKeyEventTrigger = triggerIndex++
                             false
                         }
@@ -301,7 +296,7 @@
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Act.
@@ -319,7 +314,7 @@
     @Test
     fun grandParent_parent_child() {
         // Arrange.
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         var triggerIndex = 1
         var grandParentOnKeyEventTrigger = 0
         var grandParentOnPreviewKeyEventTrigger = 0
@@ -330,37 +325,37 @@
         rule.setFocusableContent {
             Box(
                 modifier = Modifier
-                    .focus()
-                    .keyInputFilter {
+                    .focusModifier()
+                    .onKeyEvent {
                         grandParentOnKeyEventTrigger = triggerIndex++
                         false
                     }
-                    .previewKeyInputFilter {
+                    .onPreviewKeyEvent {
                         grandParentOnPreviewKeyEventTrigger = triggerIndex++
                         false
                     }
             ) {
                 Box(
                     modifier = Modifier
-                        .focus()
-                        .keyInputFilter {
+                        .focusModifier()
+                        .onKeyEvent {
                             parentOnKeyEventTrigger = triggerIndex++
                             false
                         }
-                        .previewKeyInputFilter {
+                        .onPreviewKeyEvent {
                             parentOnPreviewKeyEventTrigger = triggerIndex++
                             false
                         }
                 ) {
                     Box(
                         modifier = Modifier
-                            .focusRequester(focusRequester)
-                            .focus()
-                            .keyInputFilter {
+                            .focusReference(focusReference)
+                            .focusModifier()
+                            .onKeyEvent {
                                 childOnKeyEventTrigger = triggerIndex++
                                 false
                             }
-                            .previewKeyInputFilter {
+                            .onPreviewKeyEvent {
                                 childOnPreviewKeyEventTrigger = triggerIndex++
                                 false
                             }
@@ -369,7 +364,7 @@
             }
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         // Act.
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
index 091b1d3..6c16ca5 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
@@ -103,8 +103,6 @@
         }
 
         rule.runOnUiThread {
-            androidComposeView = container.getChildAt(0) as AndroidComposeView
-
             val motionEvent = MotionEvent(
                 0,
                 MotionEvent.ACTION_DOWN,
@@ -115,7 +113,7 @@
             )
 
             // Act
-            val actual = androidComposeView.dispatchTouchEvent(motionEvent)
+            val actual = container.dispatchTouchEvent(motionEvent)
 
             // Assert
             assertThat(actual).isFalse()
@@ -140,11 +138,8 @@
         }
 
         rule.runOnUiThread {
-
-            androidComposeView = container.getChildAt(0) as AndroidComposeView
-
             val locationInWindow = IntArray(2).also {
-                androidComposeView.getLocationInWindow(it)
+                container.getLocationInWindow(it)
             }
 
             val motionEvent = MotionEvent(
@@ -157,7 +152,7 @@
             )
 
             // Act
-            val actual = androidComposeView.dispatchTouchEvent(motionEvent)
+            val actual = container.dispatchTouchEvent(motionEvent)
 
             // Assert
             assertThat(actual).isTrue()
@@ -206,7 +201,7 @@
         assertThat(latch.await(1, TimeUnit.SECONDS)).isTrue()
 
         rule.runOnUiThread {
-            androidComposeView = container.getChildAt(0) as AndroidComposeView
+            val androidComposeView = container.getChildAt(0) as AndroidComposeView
 
             // we update size from 10 to 20 pixels
             size.value = 20
@@ -270,11 +265,8 @@
         }
 
         rule.runOnUiThread {
-
-            androidComposeView = container.getChildAt(0) as AndroidComposeView
-
             val locationInWindow = IntArray(2).also {
-                androidComposeView.getLocationInWindow(it)
+                container.getLocationInWindow(it)
             }
 
             val motionEvent = MotionEvent(
@@ -292,7 +284,7 @@
             )
 
             // Act
-            androidComposeView.dispatchTouchEvent(motionEvent)
+            container.dispatchTouchEvent(motionEvent)
 
             // Assert
             assertThat(log).hasSize(1)
@@ -321,10 +313,8 @@
         }
 
         rule.runOnUiThread {
-
-            androidComposeView = container.getChildAt(0) as AndroidComposeView
             val (x, y) = IntArray(2).let { array ->
-                androidComposeView.getLocationInWindow(array)
+                container.getLocationInWindow(array)
                 array.map { item -> item.toFloat() }
             }
 
@@ -346,10 +336,10 @@
                 arrayOf(PointerCoords(x + 1, y))
             )
 
-            androidComposeView.dispatchTouchEvent(down)
+            container.dispatchTouchEvent(down)
 
             // Act
-            androidComposeView.dispatchTouchEvent(move)
+            container.dispatchTouchEvent(move)
 
             // Assert
             if (callsRequestDisallowInterceptTouchEvent) {
@@ -386,16 +376,13 @@
         }
 
         rule.runOnUiThread {
-
-            androidComposeView = container.getChildAt(0) as AndroidComposeView
-
             // Get the current location in window.
             val locationInWindow = IntArray(2).also {
-                androidComposeView.getLocationInWindow(it)
+                container.getLocationInWindow(it)
             }
 
             // Offset the androidComposeView.
-            androidComposeView.offsetTopAndBottom(offset)
+            container.offsetTopAndBottom(offset)
 
             // Create a motion event that is also offset.
             val motionEvent = MotionEvent(
@@ -413,7 +400,7 @@
             )
 
             // Act
-            androidComposeView.dispatchTouchEvent(motionEvent)
+            container.dispatchTouchEvent(motionEvent)
 
             // Assert
             assertThat(log).hasSize(1)
@@ -452,18 +439,16 @@
 
         val locationInWindow = IntArray(2)
         rule.runOnUiThread {
-            androidComposeView = container.getChildAt(0) as AndroidComposeView
-
             // Get the current location in window.
-            androidComposeView.getLocationInWindow(locationInWindow)
+            container.getLocationInWindow(locationInWindow)
 
             val downEvent = createPointerEventAt(0, MotionEvent.ACTION_DOWN, locationInWindow)
-            androidComposeView.dispatchTouchEvent(downEvent)
+            container.dispatchTouchEvent(downEvent)
         }
 
         rule.runOnUiThread {
             val upEvent = createPointerEventAt(200, MotionEvent.ACTION_UP, locationInWindow)
-            androidComposeView.dispatchTouchEvent(upEvent)
+            container.dispatchTouchEvent(upEvent)
         }
 
         assertTrue(tapLatch.await(1, TimeUnit.SECONDS))
@@ -475,7 +460,7 @@
 
         rule.runOnUiThread {
             val downEvent = createPointerEventAt(1000, MotionEvent.ACTION_DOWN, locationInWindow)
-            androidComposeView.dispatchTouchEvent(downEvent)
+            container.dispatchTouchEvent(downEvent)
         }
         // Need to wait for long press timeout (at least)
         rule.runOnUiThread {
@@ -484,7 +469,7 @@
                 MotionEvent.ACTION_UP,
                 locationInWindow
             )
-            androidComposeView.dispatchTouchEvent(upEvent)
+            container.dispatchTouchEvent(upEvent)
         }
         assertTrue(tapLatch2.await(1, TimeUnit.SECONDS))
 
@@ -494,11 +479,11 @@
 
         rule.runOnUiThread {
             val downEvent = createPointerEventAt(2000, MotionEvent.ACTION_DOWN, locationInWindow)
-            androidComposeView.dispatchTouchEvent(downEvent)
+            container.dispatchTouchEvent(downEvent)
         }
         rule.runOnUiThread {
             val upEvent = createPointerEventAt(2200, MotionEvent.ACTION_UP, locationInWindow)
-            androidComposeView.dispatchTouchEvent(upEvent)
+            container.dispatchTouchEvent(upEvent)
         }
         assertTrue(tapLatch.await(1, TimeUnit.SECONDS))
     }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
index f9e3ac6..9e292d3 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
@@ -16,15 +16,14 @@
 
 package androidx.compose.ui.input.pointer
 
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.autofill.Autofill
 import androidx.compose.ui.autofill.AutofillTree
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.hapticfeedback.HapticFeedback
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
@@ -3131,20 +3130,14 @@
     targetRoot: LayoutNode = LayoutNode()
 ): Owner = MockOwner(position, targetRoot)
 
-@OptIn(
-    ExperimentalFocus::class,
-    InternalCoreApi::class
-)
+@OptIn(ExperimentalComposeUiApi::class, InternalCoreApi::class)
 private class MockOwner(
     private val position: IntOffset,
     private val targetRoot: LayoutNode
 ) : Owner {
     override fun calculatePosition(): IntOffset = position
     override fun requestFocus(): Boolean = false
-
-    @ExperimentalKeyInput
     override fun sendKeyEvent(keyEvent: KeyEvent): Boolean = false
-
     override val root: LayoutNode
         get() = targetRoot
     override val hapticFeedBack: HapticFeedback
@@ -3181,8 +3174,6 @@
     override fun onRequestRelayout(layoutNode: LayoutNode) {
     }
 
-    override val hasPendingMeasureOrLayout = false
-
     override fun onAttach(node: LayoutNode) {
     }
 
@@ -3202,6 +3193,9 @@
     override fun onSemanticsChange() {
     }
 
+    override fun onLayoutChange(layoutNode: LayoutNode) {
+    }
+
     override val measureIteration: Long
         get() = 0
 
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
index 4433814..917daa9 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.ValueElement
 import androidx.compose.ui.platform.ViewConfiguration
@@ -48,7 +47,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalPointerInput::class, ExperimentalCoroutinesApi::class)
+@OptIn(ExperimentalCoroutinesApi::class)
 class SuspendingPointerInputFilterTest {
     @After
     fun after() {
@@ -63,7 +62,7 @@
         val result = CompletableDeferred<PointerEvent>()
         launch {
             with(filter) {
-                handlePointerInput {
+                awaitPointerEventScope {
                     result.complete(awaitPointerEvent())
                 }
             }
@@ -91,7 +90,7 @@
         val results = Channel<PointerEvent>(Channel.UNLIMITED)
         launch {
             with(filter) {
-                handlePointerInput {
+                awaitPointerEventScope {
                     repeat(3) {
                         results.offer(awaitPointerEvent())
                     }
@@ -126,7 +125,7 @@
         val results = Channel<PointerEvent>(Channel.UNLIMITED)
         launch {
             with(filter) {
-                handlePointerInput {
+                awaitPointerEventScope {
                     repeat(3) {
                         results.offer(awaitPointerEvent())
                     }
@@ -199,7 +198,7 @@
         val handler = launch {
             with(filter) {
                 try {
-                    handlePointerInput {
+                    awaitPointerEventScope {
                         try {
                             counter.expect(1, "about to call awaitPointerEvent")
                             awaitPointerEvent()
@@ -235,7 +234,6 @@
 
 private fun PointerInputChange.toPointerEvent() = PointerEvent(listOf(this))
 
-@ExperimentalPointerInput
 private val PointerEvent.firstChange get() = changes.first()
 
 private class PointerInputChangeEmitter(id: Int = 0) {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
index baaf1c4..38d987a 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
@@ -21,8 +21,11 @@
 import android.widget.LinearLayout
 import android.widget.ScrollView
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
 import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -43,6 +46,7 @@
 import androidx.compose.ui.unit.Constraints
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotNull
@@ -53,6 +57,7 @@
 import org.junit.runner.RunWith
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
+import kotlin.math.min
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -430,6 +435,226 @@
 
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
     }
+
+    @Test
+    fun simplePadding() {
+        val paddingLeftPx = 100.0f
+        val paddingTopPx = 120.0f
+        var realLeft: Float? = null
+        var realTop: Float? = null
+
+        val positionedLatch = CountDownLatch(1)
+        rule.runOnUiThread {
+            activity.setContent {
+                with(AmbientDensity.current) {
+                    Box(
+                        Modifier.fillMaxSize()
+                            .padding(start = paddingLeftPx.toDp(), top = paddingTopPx.toDp())
+                            .onGloballyPositioned {
+                                realLeft = it.positionInParent.x
+                                realTop = it.positionInParent.y
+                                positionedLatch.countDown()
+                            }
+                    )
+                }
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        assertThat(paddingLeftPx).isEqualTo(realLeft)
+        assertThat(paddingTopPx).isEqualTo(realTop)
+    }
+    @Test
+    fun nestedLayoutCoordinates() {
+        val firstPaddingPx = 10f
+        val secondPaddingPx = 20f
+        val thirdPaddingPx = 30f
+        var gpCoordinates: LayoutCoordinates? = null
+        var childCoordinates: LayoutCoordinates? = null
+
+        val positionedLatch = CountDownLatch(2)
+        rule.runOnUiThread {
+            activity.setContent {
+                with(AmbientDensity.current) {
+                    Box(
+                        Modifier.padding(start = firstPaddingPx.toDp()).then(
+                            Modifier.onGloballyPositioned {
+                                gpCoordinates = it
+                                positionedLatch.countDown()
+                            }
+                        )
+                    ) {
+                        Box(Modifier.padding(start = secondPaddingPx.toDp())) {
+                            Box(
+                                Modifier.fillMaxSize()
+                                    .padding(start = thirdPaddingPx.toDp())
+                                    .onGloballyPositioned {
+                                        childCoordinates = it
+                                        positionedLatch.countDown()
+                                    }
+                            )
+                        }
+                    }
+                }
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        // global position
+        val gPos = childCoordinates!!.localToGlobal(Offset.Zero).x
+        assertThat(gPos).isEqualTo((firstPaddingPx + secondPaddingPx + thirdPaddingPx))
+        // Position in grandparent Px(value=50.0)
+        val gpPos = gpCoordinates!!.childToLocal(childCoordinates!!, Offset.Zero).x
+        assertThat(gpPos).isEqualTo((secondPaddingPx + thirdPaddingPx))
+        // local position
+        assertThat(childCoordinates!!.positionInParent.x).isEqualTo(thirdPaddingPx)
+    }
+
+    @Test
+    fun globalCoordinatesAreInActivityCoordinates() {
+        val padding = 30
+        val localPosition = androidx.compose.ui.geometry.Offset.Zero
+        val framePadding = Offset(padding.toFloat(), padding.toFloat())
+        var realGlobalPosition: Offset? = null
+        var realLocalPosition: Offset? = null
+        var frameGlobalPosition: Offset? = null
+
+        val positionedLatch = CountDownLatch(1)
+        rule.runOnUiThread {
+            val composeView = ComposeView(activity)
+            composeView.setPadding(padding, padding, padding, padding)
+            activity.setContentView(composeView)
+
+            val position = IntArray(2)
+            composeView.getLocationOnScreen(position)
+            frameGlobalPosition = Offset(position[0].toFloat(), position[1].toFloat())
+
+            composeView.setContent {
+                Box(
+                    Modifier.fillMaxSize().onGloballyPositioned {
+                        realGlobalPosition = it.localToGlobal(localPosition)
+                        realLocalPosition = it.globalToLocal(
+                            framePadding + frameGlobalPosition!!
+                        )
+                        positionedLatch.countDown()
+                    }
+                )
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        assertThat(realGlobalPosition).isEqualTo(frameGlobalPosition!! + framePadding)
+        assertThat(realLocalPosition).isEqualTo(localPosition)
+    }
+
+    @Test
+    fun justAddedOnPositionedCallbackFiredWithoutLayoutChanges() {
+        val needCallback = mutableStateOf(false)
+
+        val positionedLatch = CountDownLatch(1)
+        rule.runOnUiThread {
+            activity.setContent {
+                val modifier = if (needCallback.value) {
+                    Modifier.onGloballyPositioned { positionedLatch.countDown() }
+                } else {
+                    Modifier
+                }
+                Box(modifier.fillMaxSize())
+            }
+        }
+
+        rule.runOnUiThread { needCallback.value = true }
+
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+    }
+
+    @Test
+    fun testRepositionTriggersCallback() {
+        val left = mutableStateOf(30)
+        var realLeft: Float? = null
+
+        var positionedLatch = CountDownLatch(1)
+        rule.runOnUiThread {
+            activity.setContent {
+                with(AmbientDensity.current) {
+                    Box {
+                        Box(
+                            Modifier.onGloballyPositioned {
+                                realLeft = it.positionInParent.x
+                                positionedLatch.countDown()
+                            }
+                                .fillMaxSize()
+                                .padding(start = left.value.toDp()),
+                        )
+                    }
+                }
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        positionedLatch = CountDownLatch(1)
+        rule.runOnUiThread { left.value = 40 }
+
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+        assertThat(realLeft).isEqualTo(40)
+    }
+
+    @Test
+    fun testGrandParentRepositionTriggersChildrenCallback() {
+        // when we reposition any parent layout is causes the change in global
+        // position of all the children down the tree(for example during the scrolling).
+        // children should be able to react on this change.
+        val left = mutableStateOf(20)
+        var realLeft: Float? = null
+        var positionedLatch = CountDownLatch(1)
+        rule.runOnUiThread {
+            activity.setContent {
+                with(AmbientDensity.current) {
+                    Box {
+                        Offset(left) {
+                            Box(Modifier.size(10.toDp())) {
+                                Box(Modifier.size(10.toDp())) {
+                                    Box(
+                                        Modifier.onGloballyPositioned {
+                                            realLeft = it.positionInRoot.x
+                                            positionedLatch.countDown()
+                                        }.size(10.toDp())
+                                    )
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+        positionedLatch = CountDownLatch(1)
+        rule.runOnUiThread { left.value = 40 }
+
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+        assertThat(realLeft).isEqualTo(40)
+    }
+
+    @Test
+    fun testAlignmentLinesArePresent() {
+        val latch = CountDownLatch(1)
+        val line = VerticalAlignmentLine(::min)
+        val lineValue = 10
+        rule.runOnUiThread {
+            activity.setContent {
+                val onPositioned = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
+                    assertEquals(1, coordinates.providedAlignmentLines.size)
+                    assertEquals(lineValue, coordinates[line])
+                    latch.countDown()
+                }
+                Layout(modifier = onPositioned, content = { }) { _, _ ->
+                    layout(0, 0, mapOf(line to lineValue)) { }
+                }
+            }
+        }
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+    }
 }
 
 @Composable
@@ -449,4 +674,14 @@
             }
         }
     }
-}
\ No newline at end of file
+}
+
+@Composable
+private fun Offset(sizeModel: State<Int>, content: @Composable () -> Unit) {
+    // simple copy of Padding which doesn't recompose when the size changes
+    Layout(content) { measurables, constraints ->
+        layout(constraints.maxWidth, constraints.maxHeight) {
+            measurables.first().measure(constraints).placeRelative(sizeModel.value, 0)
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
index 14fbc4f..06f92fe 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
@@ -16,8 +16,13 @@
 
 package androidx.compose.ui.layout
 
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.ExperimentalLayout
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
+import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.FixedSize
 import androidx.compose.ui.Modifier
@@ -31,6 +36,7 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import org.junit.Assert
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Before
@@ -191,6 +197,84 @@
         assertTrue(latch.await(1, TimeUnit.SECONDS))
         assertEquals(LayoutDirection.Ltr, actualDirection)
     }
+    @Test
+    fun testModifiedLayoutDirection_inMeasureScope() {
+        val latch = CountDownLatch(1)
+        val resultLayoutDirection = Ref<LayoutDirection>()
+
+        activityTestRule.runOnUiThread {
+            activity.setContent {
+                Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
+                    Layout(content = {}) { _, _ ->
+                        resultLayoutDirection.value = layoutDirection
+                        latch.countDown()
+                        layout(0, 0) {}
+                    }
+                }
+            }
+        }
+
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        assertTrue(LayoutDirection.Rtl == resultLayoutDirection.value)
+    }
+
+    @Test
+    fun testModifiedLayoutDirection_inIntrinsicsMeasure() {
+        val latch = CountDownLatch(1)
+        var resultLayoutDirection: LayoutDirection? = null
+
+        activityTestRule.runOnUiThread {
+            activity.setContent {
+                @OptIn(ExperimentalLayout::class)
+                Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
+                    Layout(
+                        content = {},
+                        modifier = Modifier.preferredWidth(IntrinsicSize.Max),
+                        minIntrinsicWidthMeasureBlock = { _, _ -> 0 },
+                        minIntrinsicHeightMeasureBlock = { _, _ -> 0 },
+                        maxIntrinsicWidthMeasureBlock = { _, _ ->
+                            resultLayoutDirection = this.layoutDirection
+                            latch.countDown()
+                            0
+                        },
+                        maxIntrinsicHeightMeasureBlock = { _, _ -> 0 }
+                    ) { _, _ ->
+                        layout(0, 0) {}
+                    }
+                }
+            }
+        }
+
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        Assert.assertNotNull(resultLayoutDirection)
+        assertTrue(LayoutDirection.Rtl == resultLayoutDirection)
+    }
+
+    @Test
+    fun testRestoreLocaleLayoutDirection() {
+        val latch = CountDownLatch(1)
+        val resultLayoutDirection = Ref<LayoutDirection>()
+
+        activityTestRule.runOnUiThread {
+            activity.setContent {
+                val initialLayoutDirection = AmbientLayoutDirection.current
+                Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
+                    Box {
+                        Providers(AmbientLayoutDirection provides initialLayoutDirection) {
+                            Layout(emptyContent()) { _, _ ->
+                                resultLayoutDirection.value = layoutDirection
+                                latch.countDown()
+                                layout(0, 0) {}
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        assertEquals(LayoutDirection.Ltr, resultLayoutDirection.value)
+    }
 
     @Composable
     private fun CustomLayout(
@@ -199,31 +283,10 @@
     ) {
         Providers(AmbientLayoutDirection provides testLayoutDirection) {
             Layout(
-                content = @Composable {
-                    FixedSize(
-                        size,
-                        modifier = Modifier.saveLayoutInfo(
-                            position[0],
-                            countDownLatch
-                        )
-                    ) {
-                    }
-                    FixedSize(
-                        size,
-                        modifier = Modifier.saveLayoutInfo(
-                            position[1],
-                            countDownLatch
-                        )
-                    ) {
-                    }
-                    FixedSize(
-                        size,
-                        modifier = Modifier.saveLayoutInfo(
-                            position[2],
-                            countDownLatch
-                        )
-                    ) {
-                    }
+                content = {
+                    FixedSize(size, modifier = Modifier.saveLayoutInfo(position[0], countDownLatch))
+                    FixedSize(size, modifier = Modifier.saveLayoutInfo(position[1], countDownLatch))
+                    FixedSize(size, modifier = Modifier.saveLayoutInfo(position[2], countDownLatch))
                 }
             ) { measurables, constraints ->
                 val placeables = measurables.map { it.measure(constraints) }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/HotReloadTests.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/HotReloadTests.kt
index 6fb6d26..d20954b 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/HotReloadTests.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/HotReloadTests.kt
@@ -30,7 +30,7 @@
 import androidx.compose.ui.platform.AmbientContext
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.semantics.accessibilityLabel
+import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.TestActivity
 import androidx.compose.ui.test.assertLabelEquals
@@ -125,7 +125,7 @@
         var value = "First value"
 
         @Composable fun semanticsNode(text: String, id: Int) {
-            Box(Modifier.testTag("text$id").semantics { accessibilityLabel = text }) {
+            Box(Modifier.testTag("text$id").semantics { contentDescription = text }) {
             }
         }
 
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 b06582c..68ac4f8 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
@@ -48,8 +48,8 @@
 import androidx.compose.ui.background
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.layout.AlignmentLine
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.LayoutCoordinates
@@ -63,7 +63,6 @@
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.node.Owner
 import androidx.compose.ui.node.Ref
-import androidx.compose.ui.node.isAttached
 import androidx.compose.ui.test.TestActivity
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.captureToImage
@@ -579,7 +578,15 @@
         rule.setContent {
             if (composeContent) {
                 Box {
-                    AndroidView(::ComposeView) {
+                    AndroidView(
+                        {
+                            ComposeView(it).apply {
+                                setViewCompositionStrategy(
+                                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
+                                )
+                            }
+                        }
+                    ) {
                         it.setContent {
                             emit<LayoutNode, Applier<Any>>(
                                 ctor = LayoutEmitHelper.constructor,
@@ -619,7 +626,7 @@
         assertNotNull(innerAndroidComposeView)
         assertTrue(innerAndroidComposeView!!.isAttachedToWindow)
         assertNotNull(node)
-        assertTrue(node!!.isAttached())
+        assertTrue(node!!.isAttached)
 
         rule.runOnIdle { composeContent = false }
 
@@ -627,7 +634,7 @@
         rule.runOnIdle {
             assertFalse(innerAndroidComposeView!!.isAttachedToWindow)
             // the node stays attached after the compose view is detached
-            assertTrue(node!!.isAttached())
+            assertTrue(node!!.isAttached)
         }
     }
 
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WindowManagerAmbientTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WindowManagerAmbientTest.kt
index f189a3d..d50dc78 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WindowManagerAmbientTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WindowManagerAmbientTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.window.Dialog
 import androidx.compose.ui.window.Popup
@@ -33,7 +32,6 @@
 import java.util.concurrent.TimeUnit.SECONDS
 
 @MediumTest
-@OptIn(ExperimentalFocus::class)
 @RunWith(AndroidJUnit4::class)
 class WindowManagerAmbientTest {
     @get:Rule
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WrapperTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WrapperTest.kt
index 5182721..d83c502 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WrapperTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WrapperTest.kt
@@ -17,9 +17,7 @@
 
 import android.widget.FrameLayout
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Composition
 import androidx.compose.runtime.Providers
-import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.ambientOf
 import androidx.compose.runtime.compositionReference
 import androidx.compose.runtime.invalidate
@@ -91,10 +89,10 @@
         activityScenario.onActivity {
             owner = RegistryOwner()
 
-            val view = FrameLayout(it)
+            val view = ComposeView(it)
             it.setContentView(view)
             ViewTreeLifecycleOwner.set(view, owner)
-            view.setContent(Recomposer.current()) {
+            view.setContent {
                 onDispose {
                     disposeLatch.countDown()
                 }
@@ -118,14 +116,14 @@
         activityScenario.onActivity {
             owner = RegistryOwner()
         }
-        var composition: Composition? = null
         val composedLatch = CountDownLatch(1)
 
+        lateinit var view: ComposeView
         activityScenario.onActivity {
-            val view = FrameLayout(it)
+            view = ComposeView(it)
             it.setContentView(view)
             ViewTreeLifecycleOwner.set(view, owner)
-            composition = view.setContent(Recomposer.current()) {
+            view.setContent {
                 composedLatch.countDown()
             }
         }
@@ -134,11 +132,12 @@
 
         activityScenario.onActivity {
             assertEquals(1, owner.registry.observerCount)
-            composition!!.dispose()
+            view.disposeComposition()
             assertEquals(0, owner.registry.observerCount)
         }
     }
 
+    @Suppress("DEPRECATION")
     @Test
     @Ignore("b/159106722")
     fun compositionLinked_whenParentProvided() {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
index 6dbf1c4..97c6889 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
@@ -19,7 +19,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.InspectableValue
@@ -34,9 +33,9 @@
 import androidx.compose.ui.test.assertTextEquals
 import androidx.compose.ui.test.assertValueEquals
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onAllNodesWithLabel
+import androidx.compose.ui.test.onAllNodesWithContentDescription
 import androidx.compose.ui.test.onAllNodesWithText
-import androidx.compose.ui.test.onNodeWithLabel
+import androidx.compose.ui.test.onNodeWithContentDescription
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.text.AnnotatedString
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -85,7 +84,7 @@
         val recomposeForcer = mutableStateOf(0)
         rule.setContent {
             recomposeForcer.value
-            CountingLayout(Modifier.semantics { accessibilityLabel = "label" }, layoutCounter)
+            CountingLayout(Modifier.semantics { contentDescription = "label" }, layoutCounter)
         }
 
         rule.runOnIdle { assertEquals(1, layoutCounter.count) }
@@ -105,13 +104,13 @@
         rule.setContent {
             SimpleTestLayout(
                 Modifier.testTag(TestTag)
-                    .semantics(mergeDescendants = true) { accessibilityLabel = root }
+                    .semantics(mergeDescendants = true) { contentDescription = root }
             ) {
-                SimpleTestLayout(Modifier.semantics { accessibilityLabel = child1 }) {
-                    SimpleTestLayout(Modifier.semantics { accessibilityLabel = grandchild1 }) { }
-                    SimpleTestLayout(Modifier.semantics { accessibilityLabel = grandchild2 }) { }
+                SimpleTestLayout(Modifier.semantics { contentDescription = child1 }) {
+                    SimpleTestLayout(Modifier.semantics { contentDescription = grandchild1 }) { }
+                    SimpleTestLayout(Modifier.semantics { contentDescription = grandchild2 }) { }
                 }
-                SimpleTestLayout(Modifier.semantics { accessibilityLabel = child2 }) { }
+                SimpleTestLayout(Modifier.semantics { contentDescription = child2 }) { }
             }
         }
 
@@ -128,9 +127,9 @@
         val label2 = "bar"
         rule.setContent {
             SimpleTestLayout(Modifier.semantics(mergeDescendants = true) {}.testTag(tag1)) {
-                SimpleTestLayout(Modifier.semantics { accessibilityLabel = label1 }) { }
+                SimpleTestLayout(Modifier.semantics { contentDescription = label1 }) { }
                 SimpleTestLayout(Modifier.semantics(mergeDescendants = true) {}.testTag(tag2)) {
-                    SimpleTestLayout(Modifier.semantics { accessibilityLabel = label2 }) { }
+                    SimpleTestLayout(Modifier.semantics { contentDescription = label2 }) { }
                 }
             }
         }
@@ -148,12 +147,12 @@
         val label3 = "baz"
         rule.setContent {
             SimpleTestLayout(Modifier.semantics(mergeDescendants = true) {}.testTag(tag1)) {
-                SimpleTestLayout(Modifier.semantics { accessibilityLabel = label1 }) { }
+                SimpleTestLayout(Modifier.semantics { contentDescription = label1 }) { }
                 SimpleTestLayout(Modifier.clearAndSetSemantics {}) {
-                    SimpleTestLayout(Modifier.semantics { accessibilityLabel = label2 }) { }
+                    SimpleTestLayout(Modifier.semantics { contentDescription = label2 }) { }
                 }
-                SimpleTestLayout(Modifier.clearAndSetSemantics { accessibilityLabel = label3 }) {
-                    SimpleTestLayout(Modifier.semantics { accessibilityLabel = label2 }) { }
+                SimpleTestLayout(Modifier.clearAndSetSemantics { contentDescription = label3 }) {
+                    SimpleTestLayout(Modifier.semantics { contentDescription = label2 }) { }
                 }
                 SimpleTestLayout(
                     Modifier.semantics(mergeDescendants = true) {}.testTag(tag2)
@@ -174,7 +173,7 @@
         rule.setContent {
             SimpleTestLayout(Modifier.semantics(mergeDescendants = true) {}.testTag(TestTag)) {
                 if (showSubtree.value) {
-                    SimpleTestLayout(Modifier.semantics { accessibilityLabel = label }) { }
+                    SimpleTestLayout(Modifier.semantics { contentDescription = label }) { }
                 }
             }
         }
@@ -184,7 +183,7 @@
         rule.runOnIdle { showSubtree.value = false }
 
         rule.onNodeWithTag(TestTag)
-            .assertDoesNotHaveProperty(SemanticsProperties.AccessibilityLabel)
+            .assertDoesNotHaveProperty(SemanticsProperties.ContentDescription)
 
         rule.onAllNodesWithText(label).assertCountEquals(0)
     }
@@ -196,16 +195,16 @@
         val showNewNode = mutableStateOf(false)
         rule.setContent {
             SimpleTestLayout(Modifier.semantics(mergeDescendants = true) {}.testTag(TestTag)) {
-                SimpleTestLayout(Modifier.semantics { accessibilityLabel = label }) { }
+                SimpleTestLayout(Modifier.semantics { contentDescription = label }) { }
                 if (showNewNode.value) {
-                    SimpleTestLayout(Modifier.semantics { accessibilityValue = value }) { }
+                    SimpleTestLayout(Modifier.semantics { stateDescription = value }) { }
                 }
             }
         }
 
         rule.onNodeWithTag(TestTag)
             .assertLabelEquals(label)
-            .assertDoesNotHaveProperty(SemanticsProperties.AccessibilityValue)
+            .assertDoesNotHaveProperty(SemanticsProperties.StateDescription)
 
         rule.runOnIdle { showNewNode.value = true }
 
@@ -221,18 +220,18 @@
         rule.setContent {
             SimpleTestLayout(Modifier.testTag(TestTag)) {
                 if (showSubtree.value) {
-                    SimpleTestLayout(Modifier.semantics { accessibilityLabel = label }) { }
+                    SimpleTestLayout(Modifier.semantics { contentDescription = label }) { }
                 }
             }
         }
 
-        rule.onAllNodesWithLabel(label).assertCountEquals(1)
+        rule.onAllNodesWithContentDescription(label).assertCountEquals(1)
 
         rule.runOnIdle {
             showSubtree.value = false
         }
 
-        rule.onAllNodesWithLabel(label).assertCountEquals(0)
+        rule.onAllNodesWithContentDescription(label).assertCountEquals(0)
     }
 
     @Test
@@ -243,7 +242,7 @@
         rule.setContent {
             SimpleTestLayout(
                 Modifier.testTag(TestTag).semantics {
-                    accessibilityLabel = if (isAfter.value) afterLabel else beforeLabel
+                    contentDescription = if (isAfter.value) afterLabel else beforeLabel
                 }
             ) {}
         }
@@ -265,7 +264,7 @@
             SimpleTestLayout(Modifier.testTag("don't care")) {
                 SimpleTestLayout(
                     Modifier.testTag(TestTag).semantics {
-                        accessibilityLabel = if (isAfter.value) afterLabel else beforeLabel
+                        contentDescription = if (isAfter.value) afterLabel else beforeLabel
                     }
                 ) {}
             }
@@ -289,7 +288,7 @@
                 SimpleTestLayout {
                     SimpleTestLayout(
                         Modifier.testTag(TestTag).semantics {
-                            accessibilityLabel = if (isAfter.value) afterLabel else beforeLabel
+                            contentDescription = if (isAfter.value) afterLabel else beforeLabel
                         }
                     ) {}
                 }
@@ -313,7 +312,7 @@
             SimpleTestLayout(Modifier.testTag(TestTag).semantics(mergeDescendants = true) {}) {
                 SimpleTestLayout(
                     Modifier.semantics {
-                        accessibilityLabel = if (isAfter.value) afterLabel else beforeLabel
+                        contentDescription = if (isAfter.value) afterLabel else beforeLabel
                     }
                 ) {}
             }
@@ -332,14 +331,14 @@
         rule.setContent {
             SimpleTestLayout(Modifier.testTag(TestTag)) {
                 SimpleTestLayout(Modifier.semantics(mergeDescendants = true) {}) {
-                    SimpleTestLayout(Modifier.semantics { accessibilityLabel = label }) { }
+                    SimpleTestLayout(Modifier.semantics { contentDescription = label }) { }
                 }
             }
         }
 
         rule.onNodeWithTag(TestTag)
-            .assertDoesNotHaveProperty(SemanticsProperties.AccessibilityLabel)
-        rule.onNodeWithLabel(label) // assert exists
+            .assertDoesNotHaveProperty(SemanticsProperties.ContentDescription)
+        rule.onNodeWithContentDescription(label) // assert exists
     }
 
     @Test
@@ -354,7 +353,7 @@
         rule.setContent {
             SimpleTestLayout(
                 Modifier.testTag(TestTag).semantics {
-                    accessibilityLabel = if (isAfter.value) afterLabel else beforeLabel
+                    contentDescription = if (isAfter.value) afterLabel else beforeLabel
                 }
             ) {
                 SimpleTestLayout(Modifier.semantics { }) { }
@@ -385,7 +384,7 @@
         rule.setContent {
             SimpleTestLayout(
                 Modifier.testTag(TestTag).semantics {
-                    accessibilityLabel = if (isAfter.value) afterLabel else beforeLabel
+                    contentDescription = if (isAfter.value) afterLabel else beforeLabel
                     onClick(
                         action = {
                             if (isAfter.value) afterAction() else beforeAction()
@@ -395,7 +394,7 @@
                 }
             ) {
                 SimpleTestLayout {
-                    remember { nodeCount++ }
+                    nodeCount++
                 }
             }
         }
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 bda5092..951a5e1 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
@@ -40,6 +40,7 @@
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.R
 import androidx.compose.ui.test.captureToImage
@@ -162,6 +163,9 @@
         rule.activityRule.scenario.onActivity { activity ->
             root = FrameLayout(activity)
             composeView = ComposeView(activity)
+            composeView.setViewCompositionStrategy(
+                ViewCompositionStrategy.DisposeOnLifecycleDestroyed(activity)
+            )
             viewInsideCompose = View(activity)
 
             activity.setContentView(root)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/ComposeViewTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/ComposeViewTest.kt
index 7b461bf..7588c13 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/ComposeViewTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/ComposeViewTest.kt
@@ -31,15 +31,13 @@
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.AbstractComposeView
 import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertTextEquals
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.IntSize
 import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.LifecycleRegistry
-import androidx.lifecycle.ViewTreeLifecycleOwner
 import androidx.test.espresso.Espresso
 import androidx.test.espresso.assertion.ViewAssertions.matches
 import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
@@ -48,7 +46,9 @@
 import androidx.test.filters.SmallTest
 import org.hamcrest.CoreMatchers.instanceOf
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertSame
 import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
@@ -102,26 +102,49 @@
         }
 
         rule.onNodeWithTag("text").assertTextEquals("World")
-
-        rule.activityRule.scenario.onActivity { activity ->
-            val composeView: ComposeView = activity.findViewById(id)
-            composeView.disposeComposition()
-        }
-
-        rule.onNodeWithTag("text").assertDoesNotExist()
     }
 
     @Test
-    fun disposeOnLifecycleDestroyed() {
-        val lco = rule.runOnUiThread {
-            TestLifecycleOwner().apply {
-                registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+    fun compositionStrategyDisposed() {
+        rule.activityRule.scenario.onActivity { activity ->
+            var installed = false
+            var disposed = false
+            val testView = TestComposeView(activity)
+            val strategy = object : ViewCompositionStrategy {
+                override fun installFor(view: AbstractComposeView): () -> Unit {
+                    installed = true
+                    assertSame("correct view provided", testView, view)
+                    return { disposed = true }
+                }
             }
+            testView.setViewCompositionStrategy(strategy)
+            assertTrue("strategy should be installed", installed)
+            assertFalse("strategy should not be disposed", disposed)
+            testView.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
+            assertTrue("strategy should be disposed", disposed)
         }
+    }
+
+    @Test
+    fun disposeOnDetachedDefaultStrategy() {
+        rule.activityRule.scenario.onActivity { activity ->
+            val testView = TestComposeView(activity)
+            assertFalse("should not have composition yet", testView.hasComposition)
+            activity.setContentView(testView)
+            assertTrue("composition should be created", testView.hasComposition)
+            activity.setContentView(View(activity))
+            assertFalse("composition should have been disposed on detach", testView.hasComposition)
+        }
+    }
+
+    @Test
+    fun disposeOnLifecycleDestroyedStrategy() {
         var composeViewCapture: ComposeView? = null
         rule.activityRule.scenario.onActivity { activity ->
             val composeView = ComposeView(activity).also {
-                ViewTreeLifecycleOwner.set(it, lco)
+                it.setViewCompositionStrategy(
+                    ViewCompositionStrategy.DisposeOnLifecycleDestroyed(activity)
+                )
                 composeViewCapture = it
             }
             activity.setContentView(composeView)
@@ -132,12 +155,64 @@
 
         rule.onNodeWithTag("text").assertTextEquals("Hello")
 
-        rule.activityRule.scenario.onActivity {
-            lco.registry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+        rule.activityRule.scenario.moveToState(Lifecycle.State.DESTROYED)
+        assertNotNull("composeViewCapture should not be null", composeViewCapture)
+        assertTrue(
+            "ComposeView should not have a composition",
+            composeViewCapture?.hasComposition == false
+        )
+    }
+
+    @Test
+    fun disposeOnViewTreeLifecycleDestroyedStrategy_setBeforeAttached() {
+        var composeViewCapture: ComposeView? = null
+        rule.activityRule.scenario.onActivity { activity ->
+            val composeView = ComposeView(activity).also {
+                it.setViewCompositionStrategy(
+                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
+                )
+                composeViewCapture = it
+            }
+            activity.setContentView(composeView)
+            composeView.setContent {
+                BasicText("Hello", Modifier.testTag("text"))
+            }
         }
 
-        assertNotNull("composeViewCapture", composeViewCapture)
-        assertTrue("ComposeView.isDisposed", composeViewCapture?.isDisposed == true)
+        rule.onNodeWithTag("text").assertTextEquals("Hello")
+
+        rule.activityRule.scenario.moveToState(Lifecycle.State.DESTROYED)
+        assertNotNull("composeViewCapture should not be null", composeViewCapture)
+        assertTrue(
+            "ComposeView should not have a composition",
+            composeViewCapture?.hasComposition == false
+        )
+    }
+
+    @Test
+    fun disposeOnViewTreeLifecycleDestroyedStrategy_setAfterAttached() {
+        var composeViewCapture: ComposeView? = null
+        rule.activityRule.scenario.onActivity { activity ->
+            val composeView = ComposeView(activity)
+            composeViewCapture = composeView
+
+            activity.setContentView(composeView)
+            composeView.setViewCompositionStrategy(
+                ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
+            )
+            composeView.setContent {
+                BasicText("Hello", Modifier.testTag("text"))
+            }
+        }
+
+        rule.onNodeWithTag("text").assertTextEquals("Hello")
+
+        rule.activityRule.scenario.moveToState(Lifecycle.State.DESTROYED)
+        assertNotNull("composeViewCapture should not be null", composeViewCapture)
+        assertTrue(
+            "ComposeView should not have a composition",
+            composeViewCapture?.hasComposition == false
+        )
     }
 
     @Test
@@ -243,12 +318,6 @@
     )
 }
 
-private class TestLifecycleOwner : LifecycleOwner {
-    val registry = LifecycleRegistry(this)
-
-    override fun getLifecycle(): Lifecycle = registry
-}
-
 private class TestComposeView(
     context: Context
 ) : AbstractComposeView(context) {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/EditTextInteropTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/EditTextInteropTest.kt
index 03c512c..5b07d5e 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/EditTextInteropTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/EditTextInteropTest.kt
@@ -20,7 +20,6 @@
 import android.view.View
 import android.widget.EditText
 import androidx.activity.ComponentActivity
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.platform.AmbientView
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.text.InternalTextApi
@@ -32,7 +31,7 @@
 import org.junit.runner.RunWith
 
 @MediumTest
-@OptIn(ExperimentalFocus::class, InternalTextApi::class)
+@OptIn(InternalTextApi::class)
 @RunWith(AndroidJUnit4::class)
 class EditTextInteropTest {
     @get:Rule
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
index df3bb5e..dcdc42e 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
@@ -124,7 +124,7 @@
     private fun isSecureFlagEnabledForDialog(): Boolean {
         val owner = rule
             .onNode(isDialog())
-            .fetchSemanticsNode("").layoutNode.owner as View
+            .fetchSemanticsNode("").owner as View
         return (owner.rootView.layoutParams as WindowManager.LayoutParams).flags and
             WindowManager.LayoutParams.FLAG_SECURE != 0
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofill.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofill.kt
index 2b9b545..9c57132 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofill.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofill.kt
@@ -24,6 +24,7 @@
 import android.view.autofill.AutofillManager
 import android.view.autofill.AutofillValue
 import androidx.annotation.RequiresApi
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.graphics.toAndroidRect
 
 /**
@@ -32,6 +33,7 @@
  * @param view The parent compose view.
  * @param autofillTree The autofill tree. This will be replaced by a semantic tree (b/138604305).
  */
+@ExperimentalComposeUiApi
 @RequiresApi(Build.VERSION_CODES.O)
 internal class AndroidAutofill(val view: View, val autofillTree: AutofillTree) : Autofill {
 
@@ -63,6 +65,7 @@
  *
  * This function populates the view structure using the information in the { AutofillTree }.
  */
+@ExperimentalComposeUiApi
 @RequiresApi(Build.VERSION_CODES.O)
 internal fun AndroidAutofill.populateViewStructure(root: ViewStructure) {
 
@@ -96,6 +99,7 @@
 /**
  * Triggers onFill() in response to a request from the autofill framework.
  */
+@ExperimentalComposeUiApi
 @RequiresApi(Build.VERSION_CODES.O)
 internal fun AndroidAutofill.performAutofill(values: SparseArray<AutofillValue>) {
     for (index in 0 until values.size()) {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillDebugUtils.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillDebugUtils.kt
index 6d50b7e..839587ce 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillDebugUtils.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillDebugUtils.kt
@@ -21,6 +21,7 @@
 import android.view.View
 import android.view.autofill.AutofillManager
 import androidx.annotation.RequiresApi
+import androidx.compose.ui.ExperimentalComposeUiApi
 
 /**
  * Autofill Manager callback.
@@ -58,6 +59,7 @@
 /**
  * Registers the autofill debug callback.
  */
+@ExperimentalComposeUiApi
 @RequiresApi(Build.VERSION_CODES.O)
 internal fun AndroidAutofill.registerCallback() {
     autofillManager.registerCallback(AutofillCallback)
@@ -66,6 +68,7 @@
 /**
  * Unregisters the autofill debug callback.
  */
+@ExperimentalComposeUiApi
 @RequiresApi(Build.VERSION_CODES.O)
 internal fun AndroidAutofill.unregisterCallback() {
     autofillManager.unregisterCallback(AutofillCallback)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillType.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillType.kt
index 5e057b5..94a5b9a 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillType.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillType.kt
@@ -52,6 +52,7 @@
 import androidx.autofill.HintConstants.AUTOFILL_HINT_POSTAL_CODE
 import androidx.autofill.HintConstants.AUTOFILL_HINT_SMS_OTP
 import androidx.autofill.HintConstants.AUTOFILL_HINT_USERNAME
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.autofill.AutofillType.AddressAuxiliaryDetails
 import androidx.compose.ui.autofill.AutofillType.AddressCountry
 import androidx.compose.ui.autofill.AutofillType.AddressLocality
@@ -93,6 +94,7 @@
  * Gets the Android specific [AutofillHint][android.view.ViewStructure.setAutofillHints]
  * corresponding to the current [AutofillType].
  */
+@ExperimentalComposeUiApi
 internal val AutofillType.androidType: String
     get() {
         val androidAutofillType = androidAutofillTypes[this]
@@ -103,6 +105,7 @@
 /**
  * Maps each [AutofillType] to one of the  autofill hints in [androidx.autofill.HintConstants]
  */
+@ExperimentalComposeUiApi
 private val androidAutofillTypes: HashMap<AutofillType, String> = hashMapOf(
     EmailAddress to AUTOFILL_HINT_EMAIL_ADDRESS,
     Username to AUTOFILL_HINT_USERNAME,
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/key/KeyEventAndroid.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/key/KeyEventAndroid.kt
index b5a86d7..1d4d09b 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/key/KeyEventAndroid.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/key/KeyEventAndroid.kt
@@ -18,15 +18,11 @@
 
 import android.view.KeyEvent.ACTION_DOWN
 import android.view.KeyEvent.ACTION_UP
-import android.view.KeyEvent.META_ALT_LEFT_ON
-import android.view.KeyEvent.META_ALT_MASK
-import android.view.KeyEvent.META_ALT_RIGHT_ON
 import androidx.compose.ui.input.key.KeyEventType.KeyDown
 import androidx.compose.ui.input.key.KeyEventType.KeyUp
 import androidx.compose.ui.input.key.KeyEventType.Unknown
 import android.view.KeyEvent as AndroidKeyEvent
 
-@OptIn(ExperimentalKeyInput::class)
 internal inline class KeyEventAndroid(val keyEvent: AndroidKeyEvent) : KeyEvent {
 
     override val key: Key
@@ -52,25 +48,4 @@
 
     override val isShiftPressed: Boolean
         get() = keyEvent.isShiftPressed
-
-    @Suppress("DEPRECATION", "OverridingDeprecatedMember")
-    override val alt: Alt
-        get() = AltAndroid(keyEvent)
-}
-
-@Suppress("DEPRECATION")
-@OptIn(ExperimentalKeyInput::class)
-internal inline class AltAndroid(val keyEvent: AndroidKeyEvent) : Alt {
-    override val isLeftAltPressed
-        get() = (keyEvent.metaState and META_ALT_LEFT_ON) != 0
-
-    override val isRightAltPressed
-        get() = (keyEvent.metaState and META_ALT_RIGHT_ON) != 0
-
-    /**
-     * We override [isPressed] because Android has some synthetic meta states (eg. META_ALT_LOCKED)
-     * and provides a META_ALT_MASK that can be used to check if the Alt key is pressed.
-     */
-    override val isPressed
-        get() = (keyEvent.metaState and META_ALT_MASK) != 0
 }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/node/UiApplier.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/node/UiApplier.kt
index c1132f7..92a0968 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/node/UiApplier.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/node/UiApplier.kt
@@ -131,4 +131,24 @@
             else -> invalidNode(root)
         }
     }
+
+    override fun onEndChanges() {
+        super.onEndChanges()
+        if (root is ViewGroup) {
+            clearInvalidObservations(root)
+        } else if (root is LayoutNode) {
+            (root.owner as? AndroidComposeView)?.clearInvalidObservations()
+        }
+    }
+
+    private fun clearInvalidObservations(viewGroup: ViewGroup) {
+        for (i in 0 until viewGroup.childCount) {
+            val child = viewGroup.getChildAt(i)
+            if (child is AndroidComposeView) {
+                child.clearInvalidObservations()
+            } else if (child is ViewGroup) {
+                clearInvalidObservations(child)
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/node/ViewInterop.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/node/ViewInterop.kt
index 4cc5cf7..45698e4 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/node/ViewInterop.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/node/ViewInterop.kt
@@ -29,7 +29,7 @@
 import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.platform.AndroidOwner
+import androidx.compose.ui.platform.AndroidComposeView
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.util.fastFirstOrNull
 import androidx.compose.ui.util.fastForEach
@@ -106,7 +106,7 @@
         .pointerInteropFilter(this)
         .drawBehind {
             drawIntoCanvas { canvas ->
-                (layoutNode.owner as? AndroidOwner)
+                (layoutNode.owner as? AndroidComposeView)
                     ?.drawAndroidView(this@toLayoutNode, canvas.nativeCanvas)
             }
         }.onGloballyPositioned {
@@ -122,11 +122,11 @@
 
     var viewRemovedOnDetach: View? = null
     layoutNode.onAttach = { owner ->
-        (owner as? AndroidOwner)?.addAndroidView(this, layoutNode)
+        (owner as? AndroidComposeView)?.addAndroidView(this, layoutNode)
         if (viewRemovedOnDetach != null) view = viewRemovedOnDetach
     }
     layoutNode.onDetach = { owner ->
-        (owner as? AndroidOwner)?.removeAndroidView(this)
+        (owner as? AndroidComposeView)?.removeAndroidView(this)
         viewRemovedOnDetach = view
         view = null
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AccessibilityIterators.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AccessibilityIterators.kt
index ed51935..a102b98 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AccessibilityIterators.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AccessibilityIterators.kt
@@ -28,7 +28,7 @@
  * This class contains the implementation of text segment iterators
  * for accessibility support.
  *
- * Note: We want to be able to iterator over [SemanticsProperties.AccessibilityLabel] of any
+ * Note: We want to be able to iterator over [SemanticsProperties.ContentDescription] of any
  * component.
  */
 internal class AccessibilityIterators {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt
index 9e28215..083e828 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt
@@ -34,6 +34,7 @@
 import androidx.compose.runtime.savedinstancestate.AmbientUiSavedStateRegistry
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.staticAmbientOf
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.ViewModelStoreOwner
 
@@ -133,9 +134,9 @@
 val AmbientViewModelStoreOwner = staticAmbientOf<ViewModelStoreOwner>()
 
 @Composable
-@OptIn(InternalAnimationApi::class)
-internal fun ProvideAndroidAmbients(owner: AndroidOwner, content: @Composable () -> Unit) {
-    val view = owner.view
+@OptIn(ExperimentalComposeUiApi::class, InternalAnimationApi::class)
+internal fun ProvideAndroidAmbients(owner: AndroidComposeView, content: @Composable () -> Unit) {
+    val view = owner
     val context = view.context
     val scope = rememberCoroutineScope()
     val rootAnimationClock = remember(scope) {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.kt
index 3e63744..f0f87ff 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.kt
@@ -35,6 +35,7 @@
 import android.view.inputmethod.InputConnection
 import androidx.annotation.RequiresApi
 import androidx.compose.runtime.ExperimentalComposeApi
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.autofill.AndroidAutofill
 import androidx.compose.ui.autofill.Autofill
@@ -43,7 +44,6 @@
 import androidx.compose.ui.autofill.populateViewStructure
 import androidx.compose.ui.autofill.registerCallback
 import androidx.compose.ui.autofill.unregisterCallback
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.FOCUS_TAG
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.focus.FocusManagerImpl
@@ -51,7 +51,6 @@
 import androidx.compose.ui.graphics.CanvasHolder
 import androidx.compose.ui.hapticfeedback.AndroidHapticFeedback
 import androidx.compose.ui.hapticfeedback.HapticFeedback
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.input.key.KeyEventAndroid
 import androidx.compose.ui.input.key.KeyInputModifier
@@ -64,6 +63,7 @@
 import androidx.compose.ui.node.LayoutNode.UsageByParent
 import androidx.compose.ui.node.MeasureAndLayoutDelegate
 import androidx.compose.ui.node.OwnedLayer
+import androidx.compose.ui.node.Owner
 import androidx.compose.ui.node.OwnerSnapshotObserver
 import androidx.compose.ui.semantics.SemanticsModifierCore
 import androidx.compose.ui.semantics.SemanticsOwner
@@ -80,20 +80,24 @@
 import androidx.compose.ui.viewinterop.InternalInteropApi
 import androidx.core.os.HandlerCompat
 import androidx.core.view.ViewCompat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.ViewModelStoreOwner
 import androidx.lifecycle.ViewTreeLifecycleOwner
 import androidx.lifecycle.ViewTreeViewModelStoreOwner
+import androidx.savedstate.SavedStateRegistryOwner
 import androidx.savedstate.ViewTreeSavedStateRegistryOwner
 import java.lang.reflect.Method
 import android.view.KeyEvent as AndroidKeyEvent
 
-@SuppressLint("ViewConstructor")
+@SuppressLint("ViewConstructor", "VisibleForTests")
 @OptIn(
     ExperimentalComposeApi::class,
-    ExperimentalFocus::class,
-    ExperimentalKeyInput::class,
+    ExperimentalComposeUiApi::class,
 )
 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
-internal class AndroidComposeView(context: Context) : ViewGroup(context), AndroidOwner {
+internal class AndroidComposeView(context: Context) :
+    ViewGroup(context), Owner, ViewRootForTest {
 
     override val view: View = this
 
@@ -145,10 +149,12 @@
     // TODO(mount): reinstate when coroutines are supported by IR compiler
     // private val ownerScope = CoroutineScope(Dispatchers.Main.immediate + Job())
 
-    // Used for updating the ConfigurationAmbient when configuration changes - consume the
-    // configuration ambient instead of changing this observer if you are writing a component that
-    // adapts to configuration changes.
-    override var configurationChangeObserver: (Configuration) -> Unit = {}
+    /**
+     * Used for updating the ConfigurationAmbient when configuration changes - consume the
+     * configuration ambient instead of changing this observer if you are writing a component
+     * that adapts to configuration changes.
+     */
+    var configurationChangeObserver: (Configuration) -> Unit = {}
 
     private val _autofill = if (autofillSupported()) AndroidAutofill(this, autofillTree) else null
 
@@ -173,13 +179,6 @@
     @OptIn(InternalCoreApi::class)
     override var showLayoutBounds = false
 
-    private val clearInvalidObservations: Runnable = Runnable {
-        if (observationClearRequested) {
-            observationClearRequested = false
-            snapshotObserver.clearInvalidObservations()
-        }
-    }
-
     private var _androidViewsHandler: AndroidViewsHandler? = null
     private val androidViewsHandler: AndroidViewsHandler
         get() {
@@ -228,10 +227,14 @@
     // so that we don't have to continue using try/catch after fails once.
     private var isRenderNodeCompatible = true
 
-    override var viewTreeOwners: AndroidOwner.ViewTreeOwners? = null
+    /**
+     * Current [ViewTreeOwners]. Use [setOnViewTreeOwnersAvailable] if you want to
+     * execute your code when the object will be created.
+     */
+    var viewTreeOwners: ViewTreeOwners? = null
         private set
 
-    private var onViewTreeOwnersAvailable: ((AndroidOwner.ViewTreeOwners) -> Unit)? = null
+    private var onViewTreeOwnersAvailable: ((ViewTreeOwners) -> Unit)? = null
 
     // executed when the layout pass has been finished. as a result of it our view could be moved
     // inside the window (we are interested not only in the event when our parent positioned us
@@ -282,7 +285,7 @@
         isFocusableInTouchMode = true
         clipChildren = false
         ViewCompat.setAccessibilityDelegate(this, accessibilityDelegate)
-        AndroidOwner.onAndroidOwnerCreatedCallback?.invoke(this)
+        ViewRootForTest.onViewCreatedCallback?.invoke(this)
         root.attach(this)
     }
 
@@ -324,27 +327,56 @@
     }
 
     fun requestClearInvalidObservations() {
-        val handler = handler
-        if (!observationClearRequested && handler != null) {
-            observationClearRequested = true
-            handler.postAtFrontOfQueue(clearInvalidObservations)
+        observationClearRequested = true
+    }
+
+    internal fun clearInvalidObservations() {
+        if (observationClearRequested) {
+            snapshotObserver.clearInvalidObservations()
+            observationClearRequested = false
+        }
+        val childAndroidViews = _androidViewsHandler
+        if (childAndroidViews != null) {
+            clearChildInvalidObservations(childAndroidViews)
         }
     }
 
+    private fun clearChildInvalidObservations(viewGroup: ViewGroup) {
+        for (i in 0 until viewGroup.childCount) {
+            val child = viewGroup.getChildAt(i)
+            if (child is AndroidComposeView) {
+                child.clearInvalidObservations()
+            } else if (child is ViewGroup) {
+                clearChildInvalidObservations(child)
+            }
+        }
+    }
+
+    /**
+     * Called to inform the owner that a new Android [View] was [attached][Owner.onAttach]
+     * to the hierarchy.
+     */
     @OptIn(InternalInteropApi::class)
-    override fun addAndroidView(view: AndroidViewHolder, layoutNode: LayoutNode) {
+    fun addAndroidView(view: AndroidViewHolder, layoutNode: LayoutNode) {
         androidViewsHandler.layoutNode[view] = layoutNode
         androidViewsHandler.addView(view)
     }
 
+    /**
+     * Called to inform the owner that an Android [View] was [detached][Owner.onDetach]
+     * from the hierarchy.
+     */
     @OptIn(InternalInteropApi::class)
-    override fun removeAndroidView(view: AndroidViewHolder) {
+    fun removeAndroidView(view: AndroidViewHolder) {
         androidViewsHandler.removeView(view)
         androidViewsHandler.layoutNode.remove(view)
     }
 
+    /**
+     * Called to ask the owner to draw a child Android [View] to [canvas].
+     */
     @OptIn(InternalInteropApi::class)
-    override fun drawAndroidView(view: AndroidViewHolder, canvas: android.graphics.Canvas) {
+    fun drawAndroidView(view: AndroidViewHolder, canvas: android.graphics.Canvas) {
         androidViewsHandler.drawView(view, canvas)
     }
 
@@ -484,6 +516,10 @@
         accessibilityDelegate.onSemanticsChange()
     }
 
+    override fun onLayoutChange(layoutNode: LayoutNode) {
+        accessibilityDelegate.onLayoutChange(layoutNode)
+    }
+
     override fun dispatchDraw(canvas: android.graphics.Canvas) {
         if (!isAttachedToWindow) {
             invalidateLayers(root)
@@ -503,7 +539,11 @@
         }
     }
 
-    override fun setOnViewTreeOwnersAvailable(callback: (AndroidOwner.ViewTreeOwners) -> Unit) {
+    /**
+     * The callback to be executed when [viewTreeOwners] is created and not-null anymore.
+     * Note that this callback will be fired inline when it is already available
+     */
+    fun setOnViewTreeOwnersAvailable(callback: (ViewTreeOwners) -> Unit) {
         val viewTreeOwners = viewTreeOwners
         if (viewTreeOwners != null) {
             callback(viewTreeOwners)
@@ -512,6 +552,22 @@
         }
     }
 
+    suspend fun boundsUpdatesEventLoop() {
+        accessibilityDelegate.boundsUpdatesEventLoop()
+    }
+
+    /**
+     * Android has an issue where calling showSoftwareKeyboard after calling
+     * hideSoftwareKeyboard, it results in keyboard flickering and sometimes the keyboard ends up
+     * being hidden even though the most recent call was to showKeyboard.
+     *
+     * This function starts a suspended function that listens for show/hide commands and only
+     * runs the latest command.
+     */
+    suspend fun keyboardVisibilityEventLoop() {
+        textInputServiceAndroid.keyboardVisibilityEventLoop()
+    }
+
     /**
      * Walks the entire LayoutNode sub-hierarchy and marks all nodes as needing measurement.
      */
@@ -553,7 +609,7 @@
                     "Composed into the View which doesn't propagate" +
                         "ViewTreeSavedStateRegistryOwner!"
                 )
-            val viewTreeOwners = AndroidOwner.ViewTreeOwners(
+            val viewTreeOwners = ViewTreeOwners(
                 lifecycleOwner = lifecycleOwner,
                 viewModelStoreOwner = viewModelStoreOwner,
                 savedStateRegistryOwner = savedStateRegistryOwner
@@ -575,14 +631,6 @@
         }
         viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
         viewTreeObserver.removeOnScrollChangedListener(scrollChangedListener)
-
-        // In case of benchmarks, the handler callbacks will never get executed as benchmarks block
-        // the main thread. However this callback holds references that point to this view which
-        // effectively prevents it from being garbage collected in benchmarks.
-        if (observationClearRequested) {
-            observationClearRequested = false
-            handler.removeCallbacks(clearInvalidObservations)
-        }
     }
 
     override fun onProvideAutofillVirtualStructure(structure: ViewStructure?, flags: Int) {
@@ -645,6 +693,10 @@
         return accessibilityDelegate.dispatchHoverEvent(event)
     }
 
+    override val isLifecycleInResumedState: Boolean
+        get() = viewTreeOwners?.lifecycleOwner
+            ?.lifecycle?.currentState == Lifecycle.State.RESUMED
+
     companion object {
         private var systemPropertiesClass: Class<*>? = null
         private var getBooleanMethod: Method? = null
@@ -665,6 +717,24 @@
             false
         }
     }
+
+    /**
+     * Combines objects populated via ViewTree*Owner
+     */
+    class ViewTreeOwners(
+        /**
+         * The [LifecycleOwner] associated with this owner.
+         */
+        val lifecycleOwner: LifecycleOwner,
+        /**
+         * The [ViewModelStoreOwner] associated with this owner.
+         */
+        val viewModelStoreOwner: ViewModelStoreOwner,
+        /**
+         * The [SavedStateRegistryOwner] associated with this owner.
+         */
+        val savedStateRegistryOwner: SavedStateRegistryOwner
+    )
 }
 
 /**
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.kt
index 4dba953cc..4846d92 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.kt
@@ -35,9 +35,11 @@
 import android.view.accessibility.AccessibilityNodeProvider
 import androidx.annotation.IntRange
 import androidx.annotation.RequiresApi
+import androidx.collection.ArraySet
 import androidx.collection.SparseArrayCompat
 import androidx.compose.ui.R
 import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.semantics.CustomAccessibilityAction
 import androidx.compose.ui.semantics.SemanticsActions
 import androidx.compose.ui.semantics.SemanticsActions.CustomActions
@@ -46,6 +48,7 @@
 import androidx.compose.ui.semantics.findChildById
 import androidx.compose.ui.semantics.getAllSemanticsNodesToMap
 import androidx.compose.ui.semantics.getOrNull
+import androidx.compose.ui.semantics.outerSemantics
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.TextLayoutResult
@@ -57,6 +60,21 @@
 import androidx.core.view.ViewCompat
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
 import androidx.core.view.accessibility.AccessibilityNodeProviderCompat
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+
+private fun LayoutNode.findClosestParentNode(selector: (LayoutNode) -> Boolean): LayoutNode? {
+    var currentParent = this.parent
+    while (currentParent != null) {
+        if (selector(currentParent)) {
+            return currentParent
+        } else {
+            currentParent = currentParent.parent
+        }
+    }
+
+    return null
+}
 
 @OptIn(InternalTextApi::class)
 internal class AndroidComposeViewAccessibilityDelegateCompat(val view: AndroidComposeView) :
@@ -77,6 +95,13 @@
         const val AccessibilityCursorPositionUndefined = -1
         // 20 is taken from AbsSeekbar.java.
         const val AccessibilitySliderStepsCount = 20
+        /**
+         * Delay before dispatching a recurring accessibility event in milliseconds.
+         * This delay guarantees that a recurring event will be send at most once
+         * during the [SendRecurringAccessibilityEventsIntervalMillis] time
+         * frame.
+         */
+        const val SendRecurringAccessibilityEventsIntervalMillis: Long = 100
         private val AccessibilityActionsResourceIds = intArrayOf(
             R.id.accessibility_custom_action_0,
             R.id.accessibility_custom_action_1,
@@ -116,6 +141,11 @@
     private var hoveredVirtualViewId = InvalidId
     private val accessibilityManager: AccessibilityManager =
         view.context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+    internal var accessibilityForceEnabledForTesting = false
+    private val isAccessibilityEnabled
+        get() = accessibilityForceEnabledForTesting ||
+            accessibilityManager.isEnabled &&
+            accessibilityManager.isTouchExplorationEnabled
     private val handler = Handler(Looper.getMainLooper())
     private var nodeProvider: AccessibilityNodeProviderCompat =
         AccessibilityNodeProviderCompat(MyNodeProvider())
@@ -126,6 +156,8 @@
     private var actionIdToLabel = SparseArrayCompat<SparseArrayCompat<CharSequence>>()
     private var labelToActionId = SparseArrayCompat<Map<CharSequence, Int>>()
     private var accessibilityCursorPosition = AccessibilityCursorPositionUndefined
+    private val subtreeChangedLayoutNodes = ArraySet<LayoutNode>()
+    private val boundsUpdateChannel = Channel<Unit>(Channel.CONFLATED)
 
     @VisibleForTesting
     internal class SemanticsNodeCopy(
@@ -236,12 +268,12 @@
             ParcelSafeTextLength
         )
         info.stateDescription =
-            semanticsNode.config.getOrNull(SemanticsProperties.AccessibilityValue)
+            semanticsNode.config.getOrNull(SemanticsProperties.StateDescription)
         info.contentDescription =
-            semanticsNode.config.getOrNull(SemanticsProperties.AccessibilityLabel)
+            semanticsNode.config.getOrNull(SemanticsProperties.ContentDescription)
         // Note editable is not added to semantics properties api.
         info.isEditable = semanticsNode.config.contains(SemanticsActions.SetText)
-        info.isEnabled = (semanticsNode.config.getOrNull(SemanticsProperties.Disabled) == null)
+        info.isEnabled = semanticsNode.enabled()
         info.isFocusable = semanticsNode.config.contains(SemanticsProperties.Focused)
         if (info.isFocusable) {
             info.isFocused = semanticsNode.config[SemanticsProperties.Focused]
@@ -250,41 +282,31 @@
         info.isClickable = false
         semanticsNode.config.getOrNull(SemanticsActions.OnClick)?.let {
             info.isClickable = true
-            info.addAction(
-                AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                    AccessibilityNodeInfoCompat.ACTION_CLICK,
-                    it.label
+            if (semanticsNode.enabled()) {
+                info.addAction(
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                        AccessibilityNodeInfoCompat.ACTION_CLICK,
+                        it.label
+                    )
                 )
-            )
+            }
         }
         info.isLongClickable = false
         semanticsNode.config.getOrNull(SemanticsActions.OnLongClick)?.let {
             info.isLongClickable = true
-            info.addAction(
-                AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                    AccessibilityNodeInfoCompat.ACTION_LONG_CLICK,
-                    it.label
+            if (semanticsNode.enabled()) {
+                info.addAction(
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                        AccessibilityNodeInfoCompat.ACTION_LONG_CLICK,
+                        it.label
+                    )
                 )
-            )
-        }
-        semanticsNode.config.getOrNull(SemanticsActions.SetText)?.let {
-            info.className = "android.widget.EditText"
-            info.addAction(
-                AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                    AccessibilityNodeInfoCompat.ACTION_SET_TEXT,
-                    it.label
-                )
-            )
-        }
-        semanticsNode.config.getOrNull(SemanticsActions.SetSelection)?.let {
-            info.addAction(
-                AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                    AccessibilityNodeInfoCompat.ACTION_SET_SELECTION,
-                    it.label
-                )
-            )
+            }
         }
 
+        semanticsNode.config.getOrNull(SemanticsActions.SetText)?.let {
+            info.className = "android.widget.EditText"
+        }
         // The config will contain this action only if there is a text selection at the moment.
         semanticsNode.config.getOrNull(SemanticsActions.CopyText)?.let {
             info.addAction(
@@ -294,28 +316,46 @@
                 )
             )
         }
-
-        // The config will contain this action only if there is a text selection at the moment.
-        semanticsNode.config.getOrNull(SemanticsActions.CutText)?.let {
-            info.addAction(
-                AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                    AccessibilityNodeInfoCompat.ACTION_CUT,
-                    it.label
-                )
-            )
-        }
-
-        // The config will contain the action anyway, therefore we check the clipboard text to
-        // decide whether to add the action to the node or not.
-        semanticsNode.config.getOrNull(SemanticsActions.PasteText)?.let {
-            if (info.isFocused && view.clipboardManager.hasText()) {
+        if (semanticsNode.enabled()) {
+            semanticsNode.config.getOrNull(SemanticsActions.SetText)?.let {
                 info.addAction(
                     AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_PASTE,
+                        AccessibilityNodeInfoCompat.ACTION_SET_TEXT,
                         it.label
                     )
                 )
             }
+            semanticsNode.config.getOrNull(SemanticsActions.SetSelection)?.let {
+                info.addAction(
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                        AccessibilityNodeInfoCompat.ACTION_SET_SELECTION,
+                        it.label
+                    )
+                )
+            }
+
+            // The config will contain this action only if there is a text selection at the moment.
+            semanticsNode.config.getOrNull(SemanticsActions.CutText)?.let {
+                info.addAction(
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                        AccessibilityNodeInfoCompat.ACTION_CUT,
+                        it.label
+                    )
+                )
+            }
+
+            // The config will contain the action anyway, therefore we check the clipboard text to
+            // decide whether to add the action to the node or not.
+            semanticsNode.config.getOrNull(SemanticsActions.PasteText)?.let {
+                if (info.isFocused && view.clipboardManager.hasText()) {
+                    info.addAction(
+                        AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                            AccessibilityNodeInfoCompat.ACTION_PASTE,
+                            it.label
+                        )
+                    )
+                }
+            }
         }
 
         val text = getIterableTextForAccessibility(semanticsNode)
@@ -324,8 +364,10 @@
                 getAccessibilitySelectionStart(semanticsNode),
                 getAccessibilitySelectionEnd(semanticsNode)
             )
-
-            if (semanticsNode.config.getOrNull(SemanticsActions.SetSelection) == null) {
+            if (semanticsNode.config.getOrNull(SemanticsActions.SetSelection) == null ||
+                !semanticsNode.enabled()
+            ) {
+                // Note this is the default set selection action provided by the framework.
                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SET_SELECTION)
             }
             info.addAction(AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY)
@@ -334,7 +376,7 @@
                 AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER or
                 AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD or
                 AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
-            // We only traverse the text when accessibilityLabel is not set.
+            // We only traverse the text when contentDescription is not set.
             if (info.contentDescription.isNullOrEmpty() &&
                 semanticsNode.config.contains(SemanticsActions.GetTextLayoutResult)
             ) {
@@ -363,7 +405,9 @@
                 rangeInfo.range.endInclusive,
                 rangeInfo.current
             )
-            if (semanticsNode.config.contains(SemanticsActions.SetProgress)) {
+            if (semanticsNode.config.contains(SemanticsActions.SetProgress) &&
+                semanticsNode.enabled()
+            ) {
                 if (rangeInfo.current <
                     rangeInfo.range.endInclusive.coerceAtLeast(rangeInfo.range.start)
                 )
@@ -392,7 +436,7 @@
             if (xScrollState.maxValue > 0f) {
                 info.isScrollable = true
             }
-            if (xScrollState.value < xScrollState.maxValue) {
+            if (semanticsNode.enabled() && xScrollState.value < xScrollState.maxValue) {
                 info.addAction(
                     AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_FORWARD
                 )
@@ -406,7 +450,7 @@
                     )
                 }
             }
-            if (xScrollState.value > 0f) {
+            if (semanticsNode.enabled() && xScrollState.value > 0f) {
                 info.addAction(
                     AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_BACKWARD
                 )
@@ -430,7 +474,7 @@
             if (yScrollState.maxValue > 0f) {
                 info.isScrollable = true
             }
-            if (yScrollState.value < yScrollState.maxValue) {
+            if (semanticsNode.enabled() && yScrollState.value < yScrollState.maxValue) {
                 info.addAction(
                     AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_FORWARD
                 )
@@ -444,7 +488,7 @@
                     )
                 }
             }
-            if (yScrollState.value > 0f) {
+            if (semanticsNode.enabled() && yScrollState.value > 0f) {
                 info.addAction(
                     AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_BACKWARD
                 )
@@ -460,72 +504,74 @@
             }
         }
 
-        semanticsNode.config.getOrNull(SemanticsActions.Dismiss)?.let {
-            info.addAction(
-                AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                    AccessibilityNodeInfoCompat.ACTION_DISMISS,
-                    it.label
-                )
-            )
-        }
-
-        if (semanticsNode.config.contains(CustomActions)) {
-            val customActions = semanticsNode.config[CustomActions]
-            if (customActions.size >= AccessibilityActionsResourceIds.size) {
-                throw IllegalStateException(
-                    "Can't have more than " +
-                        "${AccessibilityActionsResourceIds.size} custom actions for one widget"
+        if (semanticsNode.enabled()) {
+            semanticsNode.config.getOrNull(SemanticsActions.Dismiss)?.let {
+                info.addAction(
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                        AccessibilityNodeInfoCompat.ACTION_DISMISS,
+                        it.label
+                    )
                 )
             }
-            val currentActionIdToLabel = SparseArrayCompat<CharSequence>()
-            val currentLabelToActionId = mutableMapOf<CharSequence, Int>()
-            // If this virtual node had custom action id assignment before, we try to keep the id
-            // unchanged for the same action (identified by action label). This way, we can
-            // minimize the influence of custom action change between custom actions are
-            // presented to the user and actually performed.
-            if (labelToActionId.containsKey(virtualViewId)) {
-                val oldLabelToActionId = labelToActionId[virtualViewId]
-                val availableIds = AccessibilityActionsResourceIds.toMutableList()
-                val unassignedActions = mutableListOf<CustomAccessibilityAction>()
-                for (action in customActions) {
-                    if (oldLabelToActionId!!.contains(action.label)) {
-                        val actionId = oldLabelToActionId[action.label]
-                        currentActionIdToLabel.put(actionId!!, action.label)
+
+            if (semanticsNode.config.contains(CustomActions)) {
+                val customActions = semanticsNode.config[CustomActions]
+                if (customActions.size >= AccessibilityActionsResourceIds.size) {
+                    throw IllegalStateException(
+                        "Can't have more than " +
+                            "${AccessibilityActionsResourceIds.size} custom actions for one widget"
+                    )
+                }
+                val currentActionIdToLabel = SparseArrayCompat<CharSequence>()
+                val currentLabelToActionId = mutableMapOf<CharSequence, Int>()
+                // If this virtual node had custom action id assignment before, we try to keep the id
+                // unchanged for the same action (identified by action label). This way, we can
+                // minimize the influence of custom action change between custom actions are
+                // presented to the user and actually performed.
+                if (labelToActionId.containsKey(virtualViewId)) {
+                    val oldLabelToActionId = labelToActionId[virtualViewId]
+                    val availableIds = AccessibilityActionsResourceIds.toMutableList()
+                    val unassignedActions = mutableListOf<CustomAccessibilityAction>()
+                    for (action in customActions) {
+                        if (oldLabelToActionId!!.contains(action.label)) {
+                            val actionId = oldLabelToActionId[action.label]
+                            currentActionIdToLabel.put(actionId!!, action.label)
+                            currentLabelToActionId[action.label] = actionId
+                            availableIds.remove(actionId)
+                            info.addAction(
+                                AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                                    actionId, action.label
+                                )
+                            )
+                        } else {
+                            unassignedActions.add(action)
+                        }
+                    }
+                    for ((index, action) in unassignedActions.withIndex()) {
+                        val actionId = availableIds[index]
+                        currentActionIdToLabel.put(actionId, action.label)
                         currentLabelToActionId[action.label] = actionId
-                        availableIds.remove(actionId)
                         info.addAction(
                             AccessibilityNodeInfoCompat.AccessibilityActionCompat(
                                 actionId, action.label
                             )
                         )
-                    } else {
-                        unassignedActions.add(action)
+                    }
+                } else {
+                    for ((index, action) in customActions.withIndex()) {
+                        val actionId = AccessibilityActionsResourceIds[index]
+                        currentActionIdToLabel.put(actionId, action.label)
+                        currentLabelToActionId[action.label] = actionId
+                        info.addAction(
+                            AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                                actionId, action.label
+                            )
+                        )
                     }
                 }
-                for ((index, action) in unassignedActions.withIndex()) {
-                    val actionId = availableIds[index]
-                    currentActionIdToLabel.put(actionId, action.label)
-                    currentLabelToActionId[action.label] = actionId
-                    info.addAction(
-                        AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                            actionId, action.label
-                        )
-                    )
-                }
-            } else {
-                for ((index, action) in customActions.withIndex()) {
-                    val actionId = AccessibilityActionsResourceIds[index]
-                    currentActionIdToLabel.put(actionId, action.label)
-                    currentLabelToActionId[action.label] = actionId
-                    info.addAction(
-                        AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                            actionId, action.label
-                        )
-                    )
-                }
+                actionIdToLabel.put(virtualViewId, currentActionIdToLabel)
+                labelToActionId.put(virtualViewId, currentLabelToActionId)
             }
-            actionIdToLabel.put(virtualViewId, currentActionIdToLabel)
-            labelToActionId.put(virtualViewId, currentLabelToActionId)
         }
     }
 
@@ -551,9 +597,7 @@
      * @return Whether this virtual view actually took accessibility focus.
      */
     private fun requestAccessibilityFocus(virtualViewId: Int): Boolean {
-        if (!accessibilityManager.isEnabled ||
-            !accessibilityManager.isTouchExplorationEnabled
-        ) {
+        if (!isAccessibilityEnabled) {
             return false
         }
         // TODO: Check virtual view visibility.
@@ -608,7 +652,7 @@
         contentChangeType: Int? = null,
         contentDescription: CharSequence? = null
     ): Boolean {
-        if ((virtualViewId == InvalidId) || !accessibilityManager.isEnabled) {
+        if (virtualViewId == InvalidId || !isAccessibilityEnabled) {
             return false
         }
 
@@ -632,7 +676,7 @@
      * @return true if the event was sent successfully.
      */
     private fun sendEvent(event: AccessibilityEvent): Boolean {
-        if (!accessibilityManager.isEnabled) {
+        if (!isAccessibilityEnabled) {
             return false
         }
 
@@ -695,11 +739,62 @@
             } else {
                 view.semanticsOwner.rootSemanticsNode.findChildById(virtualViewId) ?: return false
             }
+
+        // Actions can be performed when disabled.
         when (action) {
             AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS ->
                 return requestAccessibilityFocus(virtualViewId)
             AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS ->
                 return clearAccessibilityFocus(virtualViewId)
+            AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
+            AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY -> {
+                if (arguments != null) {
+                    val granularity = arguments.getInt(
+                        AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
+                    )
+                    val extendSelection = arguments.getBoolean(
+                        AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
+                    )
+                    return traverseAtGranularity(
+                        node, granularity,
+                        action == AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
+                        extendSelection
+                    )
+                }
+                return false
+            }
+            AccessibilityNodeInfoCompat.ACTION_SET_SELECTION -> {
+                val start = arguments?.getInt(
+                    AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, -1
+                ) ?: -1
+                val end = arguments?.getInt(
+                    AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, -1
+                ) ?: -1
+                // Note: This is a little different from current android framework implementation.
+                val success = setAccessibilitySelection(node, start, end, false)
+                // Text selection changed event already updates the cache. so this may not be
+                // necessary.
+                if (success) {
+                    sendEventForVirtualView(
+                        semanticsNodeIdToAccessibilityVirtualNodeId(node.id),
+                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED
+                    )
+                }
+                return success
+            }
+            AccessibilityNodeInfoCompat.ACTION_COPY -> {
+                return node.config.getOrNull(SemanticsActions.CopyText)?.let {
+                    it.action()
+                } ?: false
+            }
+        }
+
+        if (!node.enabled()) {
+            return false
+        }
+
+        // Actions can't be performed when disabled.
+        when (action) {
             AccessibilityNodeInfoCompat.ACTION_CLICK -> {
                 return if (node.config.contains(SemanticsActions.OnClick)) {
                     node.config[SemanticsActions.OnClick].action()
@@ -841,42 +936,6 @@
                     false
                 }
             }
-            AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
-            AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY -> {
-                if (arguments != null) {
-                    val granularity = arguments.getInt(
-                        AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
-                    )
-                    val extendSelection = arguments.getBoolean(
-                        AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
-                    )
-                    return traverseAtGranularity(
-                        node, granularity,
-                        action == AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
-                        extendSelection
-                    )
-                }
-                return false
-            }
-            AccessibilityNodeInfoCompat.ACTION_SET_SELECTION -> {
-                val start = arguments?.getInt(
-                    AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, -1
-                ) ?: -1
-                val end = arguments?.getInt(
-                    AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, -1
-                ) ?: -1
-                // Note: This is a little different from current android framework implementation.
-                val success = setAccessibilitySelection(node, start, end, false)
-                // Text selection changed event already updates the cache. so this may not be
-                // necessary.
-                if (success) {
-                    sendEventForVirtualView(
-                        semanticsNodeIdToAccessibilityVirtualNodeId(node.id),
-                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED
-                    )
-                }
-                return success
-            }
             AccessibilityNodeInfoCompat.ACTION_SET_TEXT -> {
                 val text = arguments?.getString(
                     AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
@@ -885,11 +944,6 @@
                     it.action(AnnotatedString(text ?: ""))
                 } ?: false
             }
-            AccessibilityNodeInfoCompat.ACTION_COPY -> {
-                return node.config.getOrNull(SemanticsActions.CopyText)?.let {
-                    it.action()
-                } ?: false
-            }
             AccessibilityNodeInfoCompat.ACTION_PASTE -> {
                 return node.config.getOrNull(SemanticsActions.PasteText)?.let {
                     it.action()
@@ -1027,9 +1081,7 @@
      * @return Whether the hover event was handled.
      */
     fun dispatchHoverEvent(event: MotionEvent): Boolean {
-        if (!accessibilityManager.isEnabled() ||
-            !accessibilityManager.isTouchExplorationEnabled()
-        ) {
+        if (!isAccessibilityEnabled) {
             return false
         }
 
@@ -1147,12 +1199,87 @@
     }
 
     internal fun onSemanticsChange() {
-        if (accessibilityManager.isEnabled && !checkingForSemanticsChanges) {
+        if (isAccessibilityEnabled && !checkingForSemanticsChanges) {
             checkingForSemanticsChanges = true
             handler.post(semanticsChangeChecker)
         }
     }
 
+    /**
+     * This suspend function loops for the entire lifetime of the Compose instance: it consumes
+     * recent layout changes and sends events to the accessibility framework in batches separated
+     * by a 100ms delay.
+     */
+    suspend fun boundsUpdatesEventLoop() {
+        try {
+            val subtreeChangedSemanticsNodesIds = ArraySet<Int>()
+            for (notification in boundsUpdateChannel) {
+                if (isAccessibilityEnabled) {
+                    for (i in subtreeChangedLayoutNodes.indices) {
+                        sendSubtreeChangeAccessibilityEvents(
+                            subtreeChangedLayoutNodes.valueAt(i)!!,
+                            subtreeChangedSemanticsNodesIds
+                        )
+                    }
+                    subtreeChangedSemanticsNodesIds.clear()
+                }
+                subtreeChangedLayoutNodes.clear()
+                delay(SendRecurringAccessibilityEventsIntervalMillis)
+            }
+        } finally {
+            subtreeChangedLayoutNodes.clear()
+        }
+    }
+
+    internal fun onLayoutChange(layoutNode: LayoutNode) {
+        if (!isAccessibilityEnabled) {
+            return
+        }
+
+        // The layout change of a LayoutNode will also affect its children, so even if it doesn't
+        // have semantics attached, we should process it.
+        notifySubtreeAccessibilityStateChangedIfNeeded(layoutNode)
+    }
+
+    private fun notifySubtreeAccessibilityStateChangedIfNeeded(layoutNode: LayoutNode) {
+        if (subtreeChangedLayoutNodes.add(layoutNode)) {
+            boundsUpdateChannel.offer(Unit)
+        }
+    }
+
+    private fun sendSubtreeChangeAccessibilityEvents(
+        layoutNode: LayoutNode,
+        subtreeChangedSemanticsNodesIds: ArraySet<Int>
+    ) {
+        // The node may be no longer available while we were waiting so check
+        // again.
+        if (!layoutNode.isAttached) {
+            return
+        }
+        // When we finally send the event, make sure it is an accessibility-focusable node.
+        var semanticsWrapper = layoutNode.outerSemantics
+            ?: layoutNode.findClosestParentNode { it.outerSemantics != null }
+                ?.outerSemantics ?: return
+        if (!semanticsWrapper.collapsedSemanticsConfiguration().isMergingSemanticsOfDescendants) {
+            layoutNode.findClosestParentNode {
+                it.outerSemantics
+                    ?.collapsedSemanticsConfiguration()
+                    ?.isMergingSemanticsOfDescendants == true
+            }?.outerSemantics?.let { semanticsWrapper = it }
+        }
+        val id = semanticsWrapper.modifier.id
+        if (!subtreeChangedSemanticsNodesIds.add(id)) {
+            return
+        }
+
+        sendEvent(
+            createEvent(
+                semanticsNodeIdToAccessibilityVirtualNodeId(id),
+                AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+            ).also { it.contentChangeTypes = AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE }
+        )
+    }
+
     private fun checkForSemanticsChanges() {
         val newSemanticsNodes = view.semanticsOwner.getAllSemanticsNodesToMap()
 
@@ -1186,13 +1313,13 @@
                     continue
                 }
                 when (entry.key) {
-                    SemanticsProperties.AccessibilityValue ->
+                    SemanticsProperties.StateDescription ->
                         sendEventForVirtualView(
                             semanticsNodeIdToAccessibilityVirtualNodeId(id),
                             AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
                             AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION
                         )
-                    SemanticsProperties.AccessibilityLabel ->
+                    SemanticsProperties.ContentDescription ->
                         sendEventForVirtualView(
                             semanticsNodeIdToAccessibilityVirtualNodeId(id),
                             AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
@@ -1288,12 +1415,7 @@
                         val oldYState = oldNode.config.getOrNull(
                             SemanticsProperties.VerticalAccessibilityScrollState
                         )
-                        sendEventForVirtualView(
-                            semanticsNodeIdToAccessibilityVirtualNodeId(newNode.id),
-                            AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
-                            AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE,
-                            null
-                        )
+                        notifySubtreeAccessibilityStateChangedIfNeeded(newNode.layoutNode)
                         val deltaX = if (newXState != null && oldXState != null) {
                             newXState.value - oldXState.value
                         } else {
@@ -1358,12 +1480,7 @@
         // If any child is added, clear the subtree rooted at this node and return.
         newNode.children.fastForEach { child ->
             if (!oldNode.children.contains(child.id)) {
-                sendEventForVirtualView(
-                    semanticsNodeIdToAccessibilityVirtualNodeId(newNode.id),
-                    AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
-                    AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE,
-                    null
-                )
+                notifySubtreeAccessibilityStateChangedIfNeeded(newNode.layoutNode)
                 return
             }
             newChildren.add(child.id)
@@ -1372,12 +1489,7 @@
         // If any child is deleted, clear the subtree rooted at this node and return.
         for (child in oldNode.children) {
             if (!newChildren.contains(child)) {
-                sendEventForVirtualView(
-                    semanticsNodeIdToAccessibilityVirtualNodeId(newNode.id),
-                    AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
-                    AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE,
-                    null
-                )
+                notifySubtreeAccessibilityStateChangedIfNeeded(newNode.layoutNode)
                 return
             }
         }
@@ -1461,7 +1573,8 @@
     ): Boolean {
         // Any widget which has custom action_set_selection needs to provide cursor
         // positions, so events will be sent when cursor position change.
-        if (node.config.contains(SemanticsActions.SetSelection)) {
+        // When the node is disabled, only the default/virtual set selection can performed.
+        if (node.config.contains(SemanticsActions.SetSelection) && node.enabled()) {
             // Hide all selection controllers used for adjusting selection
             // since we are doing so explicitly by other means and these
             // controllers interact with how selection behaves. From TextView.java.
@@ -1489,8 +1602,8 @@
     }
 
     private fun getAccessibilitySelectionStart(node: SemanticsNode): Int {
-        // If there is AccessibilityLabel, it will be used instead of text during traversal.
-        if (!node.config.contains(SemanticsProperties.AccessibilityLabel) &&
+        // If there is ContentDescription, it will be used instead of text during traversal.
+        if (!node.config.contains(SemanticsProperties.ContentDescription) &&
             node.config.contains(SemanticsProperties.TextSelectionRange)
         ) {
             return node.config[SemanticsProperties.TextSelectionRange].start
@@ -1499,8 +1612,8 @@
     }
 
     private fun getAccessibilitySelectionEnd(node: SemanticsNode): Int {
-        // If there is AccessibilityLabel, it will be used instead of text during traversal.
-        if (!node.config.contains(SemanticsProperties.AccessibilityLabel) &&
+        // If there is ContentDescription, it will be used instead of text during traversal.
+        if (!node.config.contains(SemanticsProperties.ContentDescription) &&
             node.config.contains(SemanticsProperties.TextSelectionRange)
         ) {
             return node.config[SemanticsProperties.TextSelectionRange].end
@@ -1510,7 +1623,7 @@
 
     private fun isAccessibilitySelectionExtendable(node: SemanticsNode): Boolean {
         // Currently only TextField is extendable. Static text may become extendable later.
-        return !node.config.contains(SemanticsProperties.AccessibilityLabel) &&
+        return !node.config.contains(SemanticsProperties.ContentDescription) &&
             node.config.contains(SemanticsProperties.Text)
     }
 
@@ -1584,8 +1697,8 @@
         }
         // Note in android framework, TextView set this to its text. This is changed to
         // prioritize content description, even for Text.
-        if (node.config.contains(SemanticsProperties.AccessibilityLabel)) {
-            return node.config[SemanticsProperties.AccessibilityLabel]
+        if (node.config.contains(SemanticsProperties.ContentDescription)) {
+            return node.config[SemanticsProperties.ContentDescription]
         }
         if (node.config.contains(SemanticsProperties.Text)) {
             return node.config[SemanticsProperties.Text].text
@@ -1625,13 +1738,15 @@
                 info: AccessibilityNodeInfoCompat,
                 semanticsNode: SemanticsNode
             ) {
-                semanticsNode.config.getOrNull(SemanticsActions.SetProgress)?.let {
-                    info.addAction(
-                        AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                            android.R.id.accessibilityActionSetProgress,
-                            it.label
+                if (semanticsNode.enabled()) {
+                    semanticsNode.config.getOrNull(SemanticsActions.SetProgress)?.let {
+                        info.addAction(
+                            AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                                android.R.id.accessibilityActionSetProgress,
+                                it.label
+                            )
                         )
-                    )
+                    }
                 }
             }
         }
@@ -1647,3 +1762,5 @@
         }
     }
 }
+
+private fun SemanticsNode.enabled() = (config.getOrNull(SemanticsProperties.Disabled) == null)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidOwner.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidOwner.kt
deleted file mode 100644
index 1e8e470..0000000
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidOwner.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.compose.ui.platform
-
-import android.content.res.Configuration
-import android.graphics.Canvas
-import android.view.View
-import androidx.annotation.RestrictTo
-import androidx.compose.ui.node.LayoutNode
-import androidx.compose.ui.node.Owner
-import androidx.compose.ui.viewinterop.AndroidViewHolder
-import androidx.compose.ui.viewinterop.InternalInteropApi
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.ViewModelStoreOwner
-import androidx.savedstate.SavedStateRegistryOwner
-import org.jetbrains.annotations.TestOnly
-
-/**
- * Interface to be implemented by [Owner]s able to handle Android View specific functionality.
- */
-interface AndroidOwner : Owner {
-
-    /**
-     * The view backing this Owner.
-     */
-    val view: View
-
-    /**
-     * Called to inform the owner that a new Android [View] was [attached][Owner.onAttach]
-     * to the hierarchy.
-     */
-    @OptIn(InternalInteropApi::class)
-    fun addAndroidView(view: AndroidViewHolder, layoutNode: LayoutNode)
-
-    /**
-     * Called to inform the owner that an Android [View] was [detached][Owner.onDetach]
-     * from the hierarchy.
-     */
-    @OptIn(InternalInteropApi::class)
-    fun removeAndroidView(view: AndroidViewHolder)
-
-    /**
-     * Called to ask the owner to draw a child Android [View] to [canvas].
-     */
-    @OptIn(InternalInteropApi::class)
-    fun drawAndroidView(view: AndroidViewHolder, canvas: Canvas)
-
-    /**
-     * Used for updating the ConfigurationAmbient when configuration changes - consume the
-     * configuration ambient instead of changing this observer if you are writing a component
-     * that adapts to configuration changes.
-     */
-    var configurationChangeObserver: (Configuration) -> Unit
-
-    /**
-     * Current [ViewTreeOwners]. Use [setOnViewTreeOwnersAvailable] if you want to
-     * execute your code when the object will be created.
-     */
-    val viewTreeOwners: ViewTreeOwners?
-
-    /**
-     * The callback to be executed when [viewTreeOwners] is created and not-null anymore.
-     * Note that this callback will be fired inline when it is already available
-     */
-    fun setOnViewTreeOwnersAvailable(callback: (ViewTreeOwners) -> Unit)
-
-    /**
-     * Called to invalidate the Android [View] sub-hierarchy handled by this Owner.
-     */
-    fun invalidateDescendants()
-
-    /** @suppress */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    companion object {
-        /**
-         * Called after an [AndroidOwner] is created. Used by AndroidComposeTestRule to keep
-         * track of all attached [AndroidComposeView]s. Not to be set or used by any other
-         * component.
-         */
-        var onAndroidOwnerCreatedCallback: ((AndroidOwner) -> Unit)? = null
-            @TestOnly
-            set
-    }
-
-    /**
-     * Combines objects populated via ViewTree*Owner
-     */
-    class ViewTreeOwners(
-        /**
-         * The [LifecycleOwner] associated with this owner.
-         */
-        val lifecycleOwner: LifecycleOwner,
-        /**
-         * The [ViewModelStoreOwner] associated with this owner.
-         */
-        val viewModelStoreOwner: ViewModelStoreOwner,
-        /**
-         * The [SavedStateRegistryOwner] associated with this owner.
-         */
-        val savedStateRegistryOwner: SavedStateRegistryOwner
-    )
-}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.kt
index 825c0c0..cac514d 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.kt
@@ -22,11 +22,9 @@
 import android.view.ViewGroup
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composition
-import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.emptyContent
+import androidx.compose.runtime.CompositionReference
 import androidx.compose.runtime.mutableStateOf
 import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleEventObserver
 import androidx.lifecycle.ViewTreeLifecycleOwner
 
 /**
@@ -36,7 +34,7 @@
  *
  * This [android.view.View] requires that the window it is attached to contains a
  * [ViewTreeLifecycleOwner]. This [androidx.lifecycle.LifecycleOwner] is used to
- * [dispose][androidx.compose.Composition.dispose] of the underlying composition
+ * [dispose][androidx.compose.runtime.Composition.dispose] of the underlying composition
  * when the host [Lifecycle] is destroyed, permitting the view to be attached and
  * detached repeatedly while preserving the composition. Call [disposeComposition]
  * to dispose of the underlying composition earlier, or if the view is never initially
@@ -51,10 +49,68 @@
 
     init {
         clipChildren = false
+        clipToPadding = false
     }
 
     private var composition: Composition? = null
 
+    private var parentReference: CompositionReference? = null
+        set(value) {
+            if (field !== value) {
+                field = value
+                val old = composition
+                if (old !== null) {
+                    old.dispose()
+                    composition = null
+
+                    // Recreate the composition now if we are attached.
+                    if (isAttachedToWindow) {
+                        ensureCompositionCreated()
+                    }
+                }
+            }
+        }
+
+    /**
+     * Set the [CompositionReference] that should be the parent of this view's composition.
+     * If [parent] is `null` it will be determined automatically from the window the view is
+     * attached to.
+     */
+    fun setParentCompositionReference(parent: CompositionReference?) {
+        parentReference = parent
+    }
+
+    // Leaking `this` during init is generally dangerous, but we know that the implementation of
+    // this particular ViewCompositionStrategy is not going to do something harmful with it.
+    @Suppress("LeakingThis")
+    private var disposeViewCompositionStrategy: (() -> Unit)? =
+        ViewCompositionStrategy.DisposeOnDetachedFromWindow.installFor(this)
+
+    /**
+     * Set the strategy for managing disposal of this View's internal composition.
+     * Defaults to [ViewCompositionStrategy.DisposeOnDetachedFromWindow].
+     *
+     * This View's composition is a live resource that must be disposed to ensure that
+     * long-lived references to it do not persist
+     *
+     * See [ViewCompositionStrategy] for more information.
+     */
+    fun setViewCompositionStrategy(strategy: ViewCompositionStrategy) {
+        disposeViewCompositionStrategy?.invoke()
+        disposeViewCompositionStrategy = strategy.installFor(this)
+    }
+
+    /**
+     * If `true`, this View's composition will be created when it becomes attached to a
+     * window for the first time. Defaults to `true`.
+     *
+     * Subclasses may choose to override this property to prevent this eager initial composition
+     * in cases where the view's content is not yet ready. Initial composition will still occur
+     * when this view is first measured.
+     */
+    protected open val shouldCreateCompositionOnAttachedToWindow: Boolean
+        get() = true
+
     /**
      * The Jetpack Compose UI content for this view.
      * Subclasses must implement this method to provide content. Initial composition will
@@ -64,18 +120,6 @@
     @Composable
     abstract fun Content()
 
-    private object DisposedComposition : Composition {
-        override fun setContent(content: () -> Unit) {
-            // No-op
-        }
-
-        override fun dispose() {
-            // No-op
-        }
-
-        override fun hasInvalidations() = false
-    }
-
     /**
      * Perform initial composition for this view.
      * Once this method is called or the view becomes attached to a window,
@@ -84,12 +128,14 @@
      * properly. (This restriction is temporary.)
      *
      * If this method is called when the composition has already been created it has no effect.
-     * If it is called after the composition is [disposed][disposeComposition] it will throw
-     * [IllegalStateException].
+     *
+     * This method should only be called if this view [isAttachedToWindow] or if a parent
+     * [CompositionReference] has been [set][setParentCompositionReference] explicitly.
      */
     fun createComposition() {
-        check(composition !== DisposedComposition) {
-            "Cannot create composition - composition was already disposed"
+        check(parentReference != null || isAttachedToWindow) {
+            "createComposition requires either a parent reference or the View to be attached" +
+                "to a window. Attach the View or call setParentCompositionReference."
         }
         ensureCompositionCreated()
     }
@@ -104,17 +150,78 @@
         }
     }
 
+    @Suppress("DEPRECATION") // Still using ViewGroup.setContent for now
     private fun ensureCompositionCreated() {
         if (composition == null) {
-            // TODO: Cannot use try/catch here until b/161894067 is fixed.
-            creatingComposition = true
-            composition = setContent(Recomposer.current()) {
-                Content()
+            try {
+                creatingComposition = true
+                composition = setContent(
+                    parentReference ?: findViewTreeCompositionReference() ?: windowRecomposer
+                ) {
+                    Content()
+                }
+            } finally {
+                creatingComposition = false
             }
-            creatingComposition = false
         }
     }
 
+    /**
+     * Dispose of the underlying composition and [requestLayout].
+     * A new composition will be created if [createComposition] is called or when needed to
+     * lay out this view.
+     */
+    fun disposeComposition() {
+        composition?.dispose()
+        composition = null
+        requestLayout()
+    }
+
+    /**
+     * `true` if this View is host to an active Compose UI composition.
+     * An active composition may consume resources.
+     */
+    val hasComposition: Boolean get() = composition != null
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+
+        if (shouldCreateCompositionOnAttachedToWindow) {
+            ensureCompositionCreated()
+        }
+    }
+
+    final override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        ensureCompositionCreated()
+        val child = getChildAt(0)
+        if (child == null) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+            return
+        }
+
+        val width = maxOf(0, MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight)
+        val height = maxOf(0, MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom)
+        child.measure(
+            MeasureSpec.makeMeasureSpec(width, MeasureSpec.getMode(widthMeasureSpec)),
+            MeasureSpec.makeMeasureSpec(height, MeasureSpec.getMode(heightMeasureSpec)),
+        )
+        setMeasuredDimension(
+            child.measuredWidth + paddingLeft + paddingRight,
+            child.measuredHeight + paddingTop + paddingBottom
+        )
+    }
+
+    final override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+        getChildAt(0)?.layout(
+            paddingLeft,
+            paddingTop,
+            right - left - paddingRight,
+            bottom - top - paddingBottom
+        )
+    }
+
+    // Below: enforce restrictions on adding child views to this ViewGroup
+
     override fun addView(child: View?) {
         checkAddView()
         super.addView(child)
@@ -154,72 +261,6 @@
         checkAddView()
         return super.addViewInLayout(child, index, params, preventRequestLayout)
     }
-
-    /**
-     * Dispose of the underlying composition.
-     * The result of this call is permanent; once disposed a ComposeView cannot be used again
-     * and will remain empty.
-     */
-    fun disposeComposition() {
-        composition?.dispose()
-        composition = DisposedComposition
-    }
-
-    /**
-     * `true` if [disposeComposition] has been called, either explicitly or by the host window's
-     * [ViewTreeLifecycleOwner] being destroyed.
-     */
-    val isDisposed: Boolean get() = composition === DisposedComposition
-
-    private var lastLifecycle: Lifecycle? = null
-
-    private val lifecycleObserver = LifecycleEventObserver { _, event ->
-        if (event == Lifecycle.Event.ON_DESTROY) {
-            disposeComposition()
-        }
-    }
-
-    override fun onAttachedToWindow() {
-        super.onAttachedToWindow()
-        val newLifecycleOwner = checkNotNull(ViewTreeLifecycleOwner.get(this)) {
-            "ViewTreeLifecycleOwner is not present in this window. Use ComponentActivity, " +
-                "FragmentActivity or AppCompatActivity to configure ViewTreeLifecycleOwner " +
-                "automatically, or call ViewTreeLifecycleOwner.set() for this View or an " +
-                "ancestor in the same window."
-        }
-        val newLifecycle = newLifecycleOwner.lifecycle
-        if (newLifecycle !== lastLifecycle) {
-            lastLifecycle?.removeObserver(lifecycleObserver)
-            lastLifecycle = newLifecycle
-            newLifecycle.addObserver(lifecycleObserver)
-        }
-        ensureCompositionCreated()
-    }
-
-    final override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
-        val child = checkNotNull(getChildAt(0)) { "Composition view not present for measure!" }
-        val width = maxOf(0, MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight)
-        val height = maxOf(0, MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom)
-        child.measure(
-            MeasureSpec.makeMeasureSpec(width, MeasureSpec.getMode(widthMeasureSpec)),
-            MeasureSpec.makeMeasureSpec(height, MeasureSpec.getMode(heightMeasureSpec)),
-        )
-        setMeasuredDimension(
-            child.measuredWidth + paddingLeft + paddingRight,
-            child.measuredHeight + paddingTop + paddingBottom
-        )
-    }
-
-    final override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
-        val child = checkNotNull(getChildAt(0)) { "Composition view not present for layout!" }
-        child.layout(
-            paddingLeft,
-            paddingTop,
-            right - left - paddingRight,
-            bottom - top - paddingBottom
-        )
-    }
 }
 
 /**
@@ -228,7 +269,7 @@
  *
  * This [android.view.View] requires that the window it is attached to contains a
  * [ViewTreeLifecycleOwner]. This [androidx.lifecycle.LifecycleOwner] is used to
- * [dispose][androidx.compose.Composition.dispose] of the underlying composition
+ * [dispose][androidx.compose.runtime.Composition.dispose] of the underlying composition
  * when the host [Lifecycle] is destroyed, permitting the view to be attached and
  * detached repeatedly while preserving the composition. Call [disposeComposition]
  * to dispose of the underlying composition earlier, or if the view is never initially
@@ -241,16 +282,15 @@
     defStyleAttr: Int = 0
 ) : AbstractComposeView(context, attrs, defStyleAttr) {
 
-    // Note: the call to emptyContent() below instead of a literal {} works around
-    // https://youtrack.jetbrains.com/issue/KT-17467, which causes the compiler to emit classes
-    // named `content` and `Content` (from the Content method's composable update scope)
-    // which causes compilation problems on case-insensitive filesystems.
-    @Suppress("RemoveExplicitTypeArguments")
-    private val content = mutableStateOf<@Composable () -> Unit>(emptyContent())
+    private val content = mutableStateOf<(@Composable () -> Unit)?>(null)
+
+    @Suppress("RedundantVisibilityModifier")
+    protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
+        private set
 
     @Composable
     override fun Content() {
-        content.value()
+        content.value?.invoke()
     }
 
     /**
@@ -259,6 +299,10 @@
      * [createComposition] is called, whichever comes first.
      */
     fun setContent(content: @Composable () -> Unit) {
+        shouldCreateCompositionOnAttachedToWindow = true
         this.content.value = content
+        if (isAttachedToWindow) {
+            createComposition()
+        }
     }
 }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewCompositionStrategy.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewCompositionStrategy.kt
new file mode 100644
index 0000000..7f52296
--- /dev/null
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewCompositionStrategy.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import android.view.View
+import androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnDetachedFromWindow
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.ViewTreeLifecycleOwner
+
+/**
+ * A strategy for managing the underlying Composition of Compose UI [View]s such as
+ * [ComposeView] and [AbstractComposeView]. See [AbstractComposeView.setViewCompositionStrategy].
+ *
+ * Compose views involve ongoing work and registering the composition with external
+ * event sources. These registrations can cause the composition to remain live and
+ * ineligible for garbage collection for long after the host View may have been abandoned.
+ * These resources and registrations can be released manually at any time by calling
+ * [AbstractComposeView.disposeComposition] and a new composition will be created automatically
+ * when needed. A [ViewCompositionStrategy] defines a strategy for disposing the composition
+ * automatically at an appropriate time.
+ *
+ * By default, Compose UI views are configured to [DisposeOnDetachedFromWindow]. The composition
+ * will be disposed automatically when the view is detached from a window. For use cases that
+ * involve frequent remove/add operations such as children of a `RecyclerView` it may be more
+ * appropriate to allow the composition to persist across removals for efficiency.
+ */
+interface ViewCompositionStrategy {
+
+    /**
+     * Install this strategy for [view] and return a function that will uninstall it later.
+     * This function should not be called directly; it is called by
+     * [AbstractComposeView.setViewCompositionStrategy] after uninstalling the previous strategy.
+     */
+    fun installFor(view: AbstractComposeView): () -> Unit
+
+    /**
+     * This companion object may be used to define extension factory functions for other
+     * strategies to aid in discovery via autocomplete. e.g.:
+     * `fun ViewCompositionStrategy.Companion.MyStrategy(): MyStrategy`
+     */
+    companion object
+
+    /**
+     * [ViewCompositionStrategy] that disposes the composition whenever the view becomes detached
+     * from a window. If the user of a Compose UI view never explicitly calls
+     * [AbstractComposeView.createComposition], this strategy is always safe and will always
+     * clean up composition resources with no explicit action required - just use the view like
+     * any other View and let garbage collection do the rest. (If
+     * [AbstractComposeView.createComposition] is called while the view is detached from a window,
+     * [AbstractComposeView.disposeComposition] must be called manually if the view is not later
+     * attached to a window.)
+     *
+     * [DisposeOnDetachedFromWindow] is the default strategy for [AbstractComposeView] and
+     * [ComposeView].
+     */
+    object DisposeOnDetachedFromWindow : ViewCompositionStrategy {
+        override fun installFor(view: AbstractComposeView): () -> Unit {
+            val listener = object : View.OnAttachStateChangeListener {
+                override fun onViewAttachedToWindow(v: View) {}
+
+                override fun onViewDetachedFromWindow(v: View?) {
+                    view.disposeComposition()
+                }
+            }
+            view.addOnAttachStateChangeListener(listener)
+            return { view.removeOnAttachStateChangeListener(listener) }
+        }
+    }
+
+    /**
+     * [ViewCompositionStrategy] that disposes the composition when [lifecycle] is
+     * [destroyed][Lifecycle.Event.ON_DESTROY]. This strategy is appropriate for Compose UI views
+     * that share a 1-1 relationship with a known [LifecycleOwner].
+     */
+    class DisposeOnLifecycleDestroyed(
+        private val lifecycle: Lifecycle
+    ) : ViewCompositionStrategy {
+        constructor(lifecycleOwner: LifecycleOwner) : this(lifecycleOwner.lifecycle)
+
+        override fun installFor(view: AbstractComposeView): () -> Unit =
+            installForLifecycle(view, lifecycle)
+    }
+
+    /**
+     * [ViewCompositionStrategy] that disposes the composition when the [ViewTreeLifecycleOwner]
+     * of the next window the view is attached to is [destroyed][Lifecycle.Event.ON_DESTROY].
+     * This strategy is appropriate for Compose UI views that share a 1-1 relationship with
+     * their closest [ViewTreeLifecycleOwner], such as a Fragment view.
+     */
+    object DisposeOnViewTreeLifecycleDestroyed : ViewCompositionStrategy {
+        override fun installFor(view: AbstractComposeView): () -> Unit {
+            if (view.isAttachedToWindow) {
+                val lco = checkNotNull(ViewTreeLifecycleOwner.get(view)) {
+                    "View tree for $view has no ViewTreeLifecycleOwner"
+                }
+                return installForLifecycle(view, lco.lifecycle)
+            } else {
+                // We change this reference after we successfully attach
+                var disposer: () -> Unit
+                val listener = object : View.OnAttachStateChangeListener {
+                    override fun onViewAttachedToWindow(v: View?) {
+                        val lco = checkNotNull(ViewTreeLifecycleOwner.get(view)) {
+                            "View tree for $view has no ViewTreeLifecycleOwner"
+                        }
+                        disposer = installForLifecycle(view, lco.lifecycle)
+
+                        // Ensure this runs only once
+                        view.removeOnAttachStateChangeListener(this)
+                    }
+
+                    override fun onViewDetachedFromWindow(v: View?) {}
+                }
+                view.addOnAttachStateChangeListener(listener)
+                disposer = { view.removeOnAttachStateChangeListener(listener) }
+                return { disposer() }
+            }
+        }
+    }
+}
+
+private fun installForLifecycle(view: AbstractComposeView, lifecycle: Lifecycle): () -> Unit {
+    check(lifecycle.currentState > Lifecycle.State.DESTROYED) {
+        "Cannot configure $view to disposeComposition at Lifecycle ON_DESTROY: $lifecycle" +
+            "is already destroyed"
+    }
+    val observer = LifecycleEventObserver { _, event ->
+        if (event == Lifecycle.Event.ON_DESTROY) {
+            view.disposeComposition()
+        }
+    }
+    lifecycle.addObserver(observer)
+    return { lifecycle.removeObserver(observer) }
+}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewRootForTest.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewRootForTest.kt
new file mode 100644
index 0000000..b5c4e4e
--- /dev/null
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewRootForTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import android.view.View
+import androidx.compose.ui.node.Owner
+import androidx.compose.ui.util.annotation.VisibleForTesting
+
+/**
+ * The marker interface to be implemented by the [View] backing the composition.
+ * To be used in tests.
+ */
+@VisibleForTesting
+// TODO(b/174747742) Introduce RootForTest and extend it instead of Owner
+interface ViewRootForTest : Owner {
+
+    /**
+     * The view backing this Owner.
+     */
+    val view: View
+
+    /**
+     * Returns true when the associated LifecycleOwner is in the resumed state
+     */
+    val isLifecycleInResumedState: Boolean
+
+    /**
+     * Whether the Owner has pending layout work.
+     */
+    val hasPendingMeasureOrLayout: Boolean
+
+    /**
+     * Called to invalidate the Android [View] sub-hierarchy handled by this [View].
+     */
+    fun invalidateDescendants()
+
+    companion object {
+        /**
+         * Called after an View implementing [ViewRootForTest] is created. Used by
+         * AndroidComposeTestRule to keep track of all attached ComposeViews. Not to be
+         * set or used by any other component.
+         */
+        @VisibleForTesting
+        var onViewCreatedCallback: ((ViewRootForTest) -> Unit)? = null
+    }
+}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.kt
new file mode 100644
index 0000000..d33b92b
--- /dev/null
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import android.view.View
+import android.view.ViewParent
+import androidx.compose.runtime.CompositionReference
+import androidx.compose.runtime.PausableMonotonicFrameClock
+import androidx.compose.runtime.Recomposer
+import androidx.compose.runtime.dispatch.AndroidUiDispatcher
+import androidx.compose.runtime.dispatch.MonotonicFrameClock
+import androidx.compose.ui.R
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.ViewTreeLifecycleOwner
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlin.coroutines.EmptyCoroutineContext
+
+/**
+ * The [CompositionReference] that should be used as a parent for compositions at or below
+ * this view in the hierarchy. Set to non-`null` to provide a [CompositionReference]
+ * for compositions created by child views, or `null` to fall back to any [CompositionReference]
+ * provided by ancestor views.
+ *
+ * See [findViewTreeCompositionReference].
+ */
+var View.compositionReference: CompositionReference?
+    get() = getTag(R.id.androidx_compose_ui_view_composition_reference) as? CompositionReference
+    set(value) {
+        setTag(R.id.androidx_compose_ui_view_composition_reference, value)
+    }
+
+/**
+ * Returns the parent [CompositionReference] for this point in the view hierarchy, or `null`
+ * if none can be found.
+ *
+ * See [compositionReference] to get or set the parent [CompositionReference] for
+ * a specific view.
+ */
+fun View.findViewTreeCompositionReference(): CompositionReference? {
+    var found: CompositionReference? = compositionReference
+    if (found != null) return found
+    var parent: ViewParent? = parent
+    while (found == null && parent is View) {
+        found = parent.compositionReference
+        parent = parent.getParent()
+    }
+    return found
+}
+
+// Flag for temporarily keeping compatibility with existing testing code that relies on
+// Recomposer.current() to perform synchronization
+internal var UseRecomposerCurrentAsWindowRecomposer = true
+
+/**
+ * Get or lazily create a [Recomposer] for this view's window. The view must be attached
+ * to a window with a [ViewTreeLifecycleOwner] registered at the root to access this property.
+ */
+internal val View.windowRecomposer: Recomposer
+    get() {
+        if (UseRecomposerCurrentAsWindowRecomposer) {
+            return Recomposer.current()
+        }
+        check(isAttachedToWindow) {
+            "Cannot locate windowRecomposer; View $this is not attached to a window"
+        }
+        val rootView = rootView
+        return when (val rootParentRef = rootView.compositionReference) {
+            null -> rootView.createViewTreeRecomposer()
+            is Recomposer -> rootParentRef
+            else -> error("root viewTreeParentCompositionReference is not a Recomposer")
+        }
+    }
+
+@OptIn(ExperimentalCoroutinesApi::class)
+private fun View.createViewTreeRecomposer(): Recomposer {
+    val currentThreadContext = AndroidUiDispatcher.CurrentThread
+    val pausableClock = currentThreadContext[MonotonicFrameClock]?.let {
+        PausableMonotonicFrameClock(it).apply { pause() }
+    }
+    val contextWithClock = currentThreadContext + (pausableClock ?: EmptyCoroutineContext)
+    val recomposer = Recomposer(contextWithClock)
+    val runRecomposeScope = CoroutineScope(contextWithClock)
+    val viewTreeLifecycleOwner = checkNotNull(ViewTreeLifecycleOwner.get(this)) {
+        "ViewTreeLifecycleOwner not found from $this"
+    }
+    setTag(R.id.androidx_compose_ui_view_composition_reference, recomposer)
+    viewTreeLifecycleOwner.lifecycle.addObserver(
+        LifecycleEventObserver { _, event ->
+            @Suppress("NON_EXHAUSTIVE_WHEN")
+            when (event) {
+                Lifecycle.Event.ON_CREATE ->
+                    // Undispatched launch since we've configured this scope
+                    // to be on the UI thread
+                    runRecomposeScope.launch(start = CoroutineStart.UNDISPATCHED) {
+                        recomposer.runRecomposeAndApplyChanges()
+                    }
+                Lifecycle.Event.ON_START -> pausableClock?.resume()
+                Lifecycle.Event.ON_STOP -> pausableClock?.pause()
+                Lifecycle.Event.ON_DESTROY -> {
+                    recomposer.shutDown()
+                    setTag(R.id.androidx_compose_ui_view_composition_reference, null)
+                }
+            }
+        }
+    )
+    return recomposer
+}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
index 8bd5f11..471e53b 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
@@ -17,6 +17,7 @@
 
 import android.app.Activity
 import android.os.Build
+import android.util.Log
 import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
@@ -24,12 +25,13 @@
 import androidx.annotation.MainThread
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composition
+import androidx.compose.runtime.CompositionData
 import androidx.compose.runtime.CompositionReference
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.InternalComposeApi
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.SlotTable
 import androidx.compose.runtime.compositionFor
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.emptyContent
@@ -43,6 +45,8 @@
 import java.util.Collections
 import java.util.WeakHashMap
 
+private val TAG = "Wrapper"
+
 /**
  * Composes the children of the view with the passed in [composable].
  *
@@ -111,7 +115,7 @@
 // instead.
 @MainThread
 @OptIn(ExperimentalComposeApi::class)
-internal actual fun actualSubcomposeInto(
+internal actual fun subcomposeInto(
     container: LayoutNode,
     parent: CompositionReference,
     composable: @Composable () -> Unit
@@ -139,9 +143,9 @@
     content: @Composable () -> Unit
 ): Composition {
     GlobalSnapshotManager.ensureStarted()
-    val composeView: AndroidOwner = window.decorView
+    val composeView: AndroidComposeView = window.decorView
         .findViewById<ViewGroup>(android.R.id.content)
-        .getChildAt(0) as? AndroidOwner
+        .getChildAt(0) as? AndroidComposeView
         ?: AndroidComposeView(this).also {
             setContentView(it.view, DefaultLayoutParams)
         }
@@ -161,6 +165,7 @@
  * @param parent The [Recomposer] or parent composition reference.
  * @param content Composable that will be the content of the view.
  */
+@Deprecated("Use ComposeView or AbstractComposeView instead.")
 fun ViewGroup.setContent(
     parent: CompositionReference = Recomposer.current(),
     content: @Composable () -> Unit
@@ -168,7 +173,7 @@
     GlobalSnapshotManager.ensureStarted()
     val composeView =
         if (childCount > 0) {
-            getChildAt(0) as? AndroidOwner
+            getChildAt(0) as? AndroidComposeView
         } else {
             removeAllViews(); null
         } ?: AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
@@ -177,14 +182,14 @@
 
 @OptIn(InternalComposeApi::class)
 private fun doSetContent(
-    owner: AndroidOwner,
+    owner: AndroidComposeView,
     parent: CompositionReference,
     content: @Composable () -> Unit
 ): Composition {
     if (inspectionWanted(owner)) {
-        owner.view.setTag(
+        owner.setTag(
             R.id.inspection_slot_table_set,
-            Collections.newSetFromMap(WeakHashMap<SlotTable, Boolean>())
+            Collections.newSetFromMap(WeakHashMap<CompositionData, Boolean>())
         )
         enableDebugInspectorInfo()
     }
@@ -204,15 +209,19 @@
     // assignment. This allows the InspectorInfo lambdas to be stripped from release builds.
     @OptIn(InternalComposeApi::class)
     if (!isDebugInspectorInfoEnabled) {
-        val packageClass = Class.forName("androidx.compose.ui.platform.InspectableValueKt")
-        val field = packageClass.getDeclaredField("isDebugInspectorInfoEnabled")
-        field.isAccessible = true
-        field.setBoolean(null, true)
+        try {
+            val packageClass = Class.forName("androidx.compose.ui.platform.InspectableValueKt")
+            val field = packageClass.getDeclaredField("isDebugInspectorInfoEnabled")
+            field.isAccessible = true
+            field.setBoolean(null, true)
+        } catch (ignored: Exception) {
+            Log.w(TAG, "Could not access isDebugInspectorInfoEnabled. Please set explicitly.")
+        }
     }
 }
 
 private class WrappedComposition(
-    val owner: AndroidOwner,
+    val owner: AndroidComposeView,
     val original: Composition
 ) : Composition, LifecycleEventObserver {
 
@@ -232,11 +241,22 @@
                     lifecycle.addObserver(this)
                 } else if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
                     original.setContent {
+
                         @Suppress("UNCHECKED_CAST")
                         val inspectionTable =
-                            owner.view.getTag(R.id.inspection_slot_table_set) as?
-                                MutableSet<SlotTable>
-                        inspectionTable?.add(currentComposer.slotTable)
+                            owner.getTag(R.id.inspection_slot_table_set) as?
+                                MutableSet<CompositionData>
+                                ?: (owner.parent as? View)?.getTag(R.id.inspection_slot_table_set)
+                                    as? MutableSet<CompositionData>
+                        if (inspectionTable != null) {
+                            @OptIn(InternalComposeApi::class)
+                            inspectionTable.add(currentComposer.compositionData)
+                            currentComposer.collectParameterInformation()
+                        }
+
+                        LaunchedEffect(owner) { owner.keyboardVisibilityEventLoop() }
+                        LaunchedEffect(owner) { owner.boundsUpdatesEventLoop() }
+
                         Providers(InspectionTables provides inspectionTable) {
                             ProvideAndroidAmbients(owner, content)
                         }
@@ -283,6 +303,6 @@
  *
  * Instead check if the attributeSourceResourceMap is not empty.
  */
-private fun inspectionWanted(owner: AndroidOwner): Boolean =
+private fun inspectionWanted(owner: AndroidComposeView): Boolean =
     Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
-        owner.view.attributeSourceResourceMap.isNotEmpty()
+        owner.attributeSourceResourceMap.isNotEmpty()
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
index feaece2..264c0fc 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
@@ -29,6 +29,8 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextRange
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.channels.Channel
 import kotlin.math.roundToInt
 
 /**
@@ -56,6 +58,12 @@
      */
     private lateinit var imm: InputMethodManager
 
+    /**
+     * A channel that is used to send ShowKeyboard/HideKeyboard commands. Send 'true' for
+     * show Keyboard and 'false' to hide keyboard.
+     */
+    private val showKeyboardChannel = Channel<Boolean>(Channel.CONFLATED)
+
     private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
         // focusedRect is null if there is not ongoing text input session. So safe to request
         // latest focused rectangle whenever global layout has changed.
@@ -138,11 +146,26 @@
     }
 
     override fun showSoftwareKeyboard() {
-        imm.showSoftInput(view, 0)
+        showKeyboardChannel.offer(true)
     }
 
     override fun hideSoftwareKeyboard() {
-        imm.hideSoftInputFromWindow(view.windowToken, 0)
+        showKeyboardChannel.offer(false)
+    }
+
+    @OptIn(FlowPreview::class)
+    suspend fun keyboardVisibilityEventLoop() {
+        for (showKeyboard in showKeyboardChannel) {
+            // Even though we are using a conflated channel, and the producers and consumers are
+            // on the same thread, there is a possibility that we have a stale value in the channel
+            // because we start consuming from it before we finish producing all the values. We poll
+            // to make sure that we use the most recent value.
+            if (showKeyboardChannel.poll() ?: showKeyboard) {
+                imm.showSoftInput(view, 0)
+            } else {
+                imm.hideSoftInputFromWindow(view.windowToken, 0)
+            }
+        }
     }
 
     override fun onStateUpdated(oldValue: TextFieldValue?, newValue: TextFieldValue) {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt
index e12e11f..4dc902d 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt
@@ -25,22 +25,25 @@
 import android.view.ViewOutlineProvider
 import android.view.Window
 import android.view.WindowManager
-import android.widget.FrameLayout
 import androidx.appcompat.view.ContextThemeWrapper
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Composition
 import androidx.compose.runtime.CompositionReference
+import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.compositionReference
-import androidx.compose.runtime.onActive
-import androidx.compose.runtime.onCommit
+import androidx.compose.runtime.emptyContent
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.R
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.platform.AbstractComposeView
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.AmbientView
-import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.semantics.dialog
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Density
@@ -92,12 +95,25 @@
 ) {
     val view = AmbientView.current
     val density = AmbientDensity.current
+    val composition = compositionReference()
+    val currentContent by rememberUpdatedState(content)
+    val dialog = remember(view, density) {
+        DialogWrapper(view, density).apply {
+            this.onDismissRequest = onDismissRequest
+            setProperties(properties)
+            setContent(composition) {
+                // TODO(b/159900354): draw a scrim and add margins around the Compose Dialog, and
+                //  consume clicks so they can't pass through to the underlying UI
+                DialogLayout(
+                    Modifier.semantics { dialog() },
+                ) {
+                    currentContent()
+                }
+            }
+        }
+    }
 
-    val dialog = remember(view, density) { DialogWrapper(view, density) }
-    dialog.onDismissRequest = onDismissRequest
-    remember(properties) { dialog.setProperties(properties) }
-
-    onActive {
+    DisposableEffect(dialog) {
         dialog.show()
 
         onDispose {
@@ -106,16 +122,9 @@
         }
     }
 
-    val composition = compositionReference()
-    onCommit {
-        dialog.setContent(composition) {
-            // TODO(b/159900354): draw a scrim and add margins around the Compose Dialog, and
-            //  consume clicks so they can't pass through to the underlying UI
-            DialogLayout(
-                Modifier.semantics { dialog() },
-                content
-            )
-        }
+    SideEffect {
+        dialog.onDismissRequest = onDismissRequest
+        dialog.setProperties(properties)
     }
 }
 
@@ -132,7 +141,25 @@
 private class DialogLayout(
     context: Context,
     override val window: Window
-) : FrameLayout(context), DialogWindowProvider
+) : AbstractComposeView(context), DialogWindowProvider {
+
+    private var content: @Composable () -> Unit by mutableStateOf(emptyContent())
+
+    protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
+        private set
+
+    fun setContent(parent: CompositionReference, content: @Composable () -> Unit) {
+        setParentCompositionReference(parent)
+        this.content = content
+        shouldCreateCompositionOnAttachedToWindow = true
+        createComposition()
+    }
+
+    @Composable
+    override fun Content() {
+        content()
+    }
+}
 
 private class DialogWrapper(
     private val composeView: View,
@@ -147,7 +174,6 @@
     lateinit var onDismissRequest: () -> Unit
 
     private val dialogLayout: DialogLayout
-    private var composition: Composition? = null
     private var properties: AndroidDialogProperties = AndroidDialogProperties()
 
     private val maxSupportedElevation = 30.dp
@@ -201,8 +227,8 @@
 
     // TODO(b/159900354): Make the Android Dialog full screen and the scrim fully transparent
 
-    fun setContent(parentComposition: CompositionReference, content: @Composable () -> Unit) {
-        composition = dialogLayout.setContent(parentComposition, content)
+    fun setContent(parentComposition: CompositionReference, children: @Composable () -> Unit) {
+        dialogLayout.setContent(parentComposition, children)
     }
 
     private fun setSecureFlagEnabled(secureFlagEnabled: Boolean) {
@@ -226,7 +252,7 @@
     }
 
     fun disposeComposition() {
-        composition?.dispose()
+        dialogLayout.disposeComposition()
     }
 
     override fun onTouchEvent(event: MotionEvent): Boolean {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt
index 617ab73..d15f5ae 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt
@@ -26,22 +26,25 @@
 import android.view.View
 import android.view.ViewOutlineProvider
 import android.view.WindowManager
-import android.widget.FrameLayout
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Composition
+import androidx.compose.runtime.CompositionReference
+import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.compositionReference
 import androidx.compose.runtime.emptyContent
-import androidx.compose.runtime.onCommit
-import androidx.compose.runtime.onDispose
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.ui.layout.Layout
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.AbstractComposeView
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.AmbientView
-import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.semantics.popup
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Density
@@ -89,17 +92,48 @@
 ) {
     val view = AmbientView.current
     val density = AmbientDensity.current
+    val testTag = AmbientPopupTestTag.current
+    val parentComposition = compositionReference()
+    val currentContent by rememberUpdatedState(content)
 
-    val popupLayout = remember { PopupLayout(view, density) }
+    val popupLayout = remember {
+        PopupLayout(view, density).apply {
+            this.onDismissRequest = onDismissRequest
+            this.testTag = testTag
+            setPositionProvider(popupPositionProvider)
+            setIsFocusable(isFocusable)
+            setProperties(properties)
+            setContent(parentComposition) {
+                SimpleStack(
+                    Modifier.semantics { this.popup() }.onGloballyPositioned {
+                        // Get the size of the content
+                        popupContentSize = it.size
+                        updatePosition()
+                    }
+                ) {
+                    currentContent()
+                }
+            }
+        }
+    }
 
-    // Refresh anything that might have changed
-    popupLayout.onDismissRequest = onDismissRequest
-    popupLayout.testTag = AmbientPopupTestTag.current
-    remember(popupPositionProvider) { popupLayout.setPositionProvider(popupPositionProvider) }
-    remember(isFocusable) { popupLayout.setIsFocusable(isFocusable) }
-    remember(properties) { popupLayout.setProperties(properties) }
+    DisposableEffect(popupLayout) {
+        onDispose {
+            popupLayout.disposeComposition()
+            // Remove the window
+            popupLayout.dismiss()
+        }
+    }
 
-    var composition: Composition? = null
+    SideEffect {
+        popupLayout.apply {
+            this.onDismissRequest = onDismissRequest
+            this.testTag = testTag
+            setPositionProvider(popupPositionProvider)
+            setIsFocusable(isFocusable)
+            setProperties(properties)
+        }
+    }
 
     // TODO(soboleva): Look at module arrangement so that Box can be
     // used instead of this custom Layout
@@ -120,28 +154,6 @@
         popupLayout.parentLayoutDirection = layoutDirection
         layout(0, 0) {}
     }
-
-    val parentComposition = compositionReference()
-    onCommit {
-        composition = popupLayout.setContent(parentComposition) {
-            SimpleStack(
-                Modifier.semantics { this.popup() }.onGloballyPositioned {
-                    // Get the size of the content
-                    popupLayout.popupContentSize = it.size
-
-                    // Update the popup's position
-                    popupLayout.updatePosition()
-                },
-                content = content
-            )
-        }
-    }
-
-    onDispose {
-        composition?.dispose()
-        // Remove the window
-        popupLayout.dismiss()
-    }
 }
 
 // TODO(soboleva): Look at module dependencies so that we can get code reuse between
@@ -186,8 +198,8 @@
 @SuppressLint("ViewConstructor")
 private class PopupLayout(
     private val composeView: View,
-    private val density: Density
-) : FrameLayout(composeView.context) {
+    density: Density
+) : AbstractComposeView(composeView.context) {
     private val windowManager =
         composeView.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
     private val params = createLayoutParams()
@@ -232,6 +244,23 @@
         }
     }
 
+    private var content: @Composable () -> Unit by mutableStateOf(emptyContent())
+
+    protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
+        private set
+
+    fun setContent(parent: CompositionReference, content: @Composable () -> Unit) {
+        setParentCompositionReference(parent)
+        this.content = content
+        shouldCreateCompositionOnAttachedToWindow = true
+        createComposition()
+    }
+
+    @Composable
+    override fun Content() {
+        content()
+    }
+
     fun setPositionProvider(positionProvider: PopupPositionProvider) {
         val wasProviderSetBefore = this.positionProvider != null
         this.positionProvider = positionProvider
diff --git a/compose/ui/ui/src/androidMain/res/values/ids.xml b/compose/ui/ui/src/androidMain/res/values/ids.xml
index 5aaa401..3f859ba 100644
--- a/compose/ui/ui/src/androidMain/res/values/ids.xml
+++ b/compose/ui/ui/src/androidMain/res/values/ids.xml
@@ -50,4 +50,5 @@
     <item name="accessibility_custom_action_31" type="id"/>
     <item name="wrapped_composition_tag" type="id" />
     <item name="inspection_slot_table_set" type="id" />
+    <item name="androidx_compose_ui_view_composition_reference" type="id" />
 </resources>
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ExperimentalComposeUiApi.kt
similarity index 79%
rename from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
rename to compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ExperimentalComposeUiApi.kt
index f9cb2fe..84259b4 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ExperimentalComposeUiApi.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.focus
+package androidx.compose.ui
 
-@RequiresOptIn("The Focus API is experimental and is likely to change in the future.")
-annotation class ExperimentalFocus
\ No newline at end of file
+@RequiresOptIn("This API is experimental and is likely to change in the future.")
+annotation class ExperimentalComposeUiApi
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusModifier.kt
index a8ac09c..bf7f4b2 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusModifier.kt
@@ -16,44 +16,15 @@
 
 package androidx.compose.ui
 
-import androidx.compose.runtime.remember
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusState
-import androidx.compose.ui.focus.FocusState.Inactive
-import androidx.compose.ui.node.ModifiedFocusNode
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
-import androidx.compose.ui.platform.NoInspectorInfo
-import androidx.compose.ui.platform.debugInspectorInfo
-
-/**
- * A [Modifier.Element] that wraps makes the modifiers on the right into a Focusable. Use a
- * different instance of [FocusModifier] for each focusable component.
- */
-@OptIn(ExperimentalFocus::class)
-internal class FocusModifier(
-    initialFocus: FocusState,
-    // TODO(b/172265016): Make this a required parameter and remove the default value.
-    //  Set this value in AndroidComposeView, and other places where we create a focus modifier
-    //  using this internal constructor.
-    inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo
-) : Modifier.Element, InspectorValueInfo(inspectorInfo) {
-
-    var focusState: FocusState = initialFocus
-        set(value) {
-            field = value
-            focusNode.wrappedBy?.propagateFocusStateChange(value)
-        }
-
-    var focusedChild: ModifiedFocusNode? = null
-
-    lateinit var focusNode: ModifiedFocusNode
-}
-
 /**
  * Add this modifier to a component to make it focusable.
  */
-@ExperimentalFocus
-fun Modifier.focus(): Modifier = composed(inspectorInfo = debugInspectorInfo { name = "focus" }) {
-    remember { FocusModifier(Inactive) }
-}
+@Deprecated(
+    message = "Use Modifier.focusModifier instead",
+    replaceWith = ReplaceWith(
+        "focusModifier()",
+        "androidx.compose.ui.focus.focusModifier"
+    ),
+    level = DeprecationLevel.ERROR
+)
+fun Modifier.focus(): Modifier = this
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusObserverModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusObserverModifier.kt
index 4546434..8719828 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusObserverModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusObserverModifier.kt
@@ -16,16 +16,19 @@
 
 package androidx.compose.ui
 
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.FocusState
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
-import androidx.compose.ui.platform.debugInspectorInfo
 
+// TODO(b/174728671): Remove this deprecated API after Alpha 09
 /**
  * A [modifier][Modifier.Element] that can be used to observe focus state changes.
  */
-@ExperimentalFocus
+@Deprecated(
+    "Please use FocusEventModifier",
+    replaceWith = ReplaceWith(
+        "FocusEventModifier",
+        "androidx.compose.ui.focus.FocusEventModifier"
+    )
+)
 interface FocusObserverModifier : Modifier.Element {
     /**
      * A callback that is called whenever focus state changes.
@@ -33,24 +36,19 @@
     val onFocusChange: (FocusState) -> Unit
 }
 
-@OptIn(ExperimentalFocus::class)
-internal class FocusObserverModifierImpl(
-    override val onFocusChange: (FocusState) -> Unit,
-    inspectorInfo: InspectorInfo.() -> Unit
-) : FocusObserverModifier, InspectorValueInfo(inspectorInfo)
-
+// TODO(b/174728671): Remove this deprecated API after Alpha 09
 /**
  * Add this modifier to a component to observe focus state changes.
  */
-@ExperimentalFocus
-fun Modifier.focusObserver(onFocusChange: (FocusState) -> Unit): Modifier {
-    return this.then(
-        FocusObserverModifierImpl(
-            onFocusChange = onFocusChange,
-            inspectorInfo = debugInspectorInfo {
-                name = "focusObserver"
-                properties["onFocusChange"] = onFocusChange
-            }
-        )
+@Suppress("unused", "UNUSED_PARAMETER")
+@Deprecated(
+    message = "Please use either onFocusChanged or onFocusEvent",
+    level = DeprecationLevel.ERROR,
+    replaceWith = ReplaceWith(
+        "onFocusChanged(onFocusChange)",
+        "androidx.compose.ui.focus.onFocusChanged"
     )
-}
\ No newline at end of file
+)
+fun Modifier.focusObserver(onFocusChange: (FocusState) -> Unit): Modifier {
+    return Modifier
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusRequesterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusRequesterModifier.kt
index bd049b0..d2d13f0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusRequesterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusRequesterModifier.kt
@@ -16,44 +16,39 @@
 
 package androidx.compose.ui
 
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
-import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.focus.FocusReference
 
 /**
- * A [modifier][Modifier.Element] that can be used to pass in a [FocusRequester] that can be used
+ * A [modifier][Modifier.Element] that can be used to pass in a [FocusReference] that can be used
  * to request focus state changes.
  *
- * @see FocusRequester
+ * @see FocusReference
  */
-@ExperimentalFocus
+@Deprecated(
+    message = "Use FocusReferenceModifier instead",
+    replaceWith = ReplaceWith(
+        "FocusReferenceModifier",
+        "androidx.compose.ui.focus.FocusReferenceModifier"
+    ),
+    level = DeprecationLevel.ERROR
+)
 interface FocusRequesterModifier : Modifier.Element {
     /**
-     * An instance of [FocusRequester], that can be used to request focus state changes.
+     * An instance of [FocusReference], that can be used to request focus state changes.
      */
-    val focusRequester: FocusRequester
+    val focusReference: FocusReference
 }
 
-@OptIn(ExperimentalFocus::class)
-internal class FocusRequesterModifierImpl(
-    override val focusRequester: FocusRequester,
-    inspectorInfo: InspectorInfo.() -> Unit
-) : FocusRequesterModifier, InspectorValueInfo(inspectorInfo)
-
 /**
  * Add this modifier to a component to observe changes to focus state.
  */
-@ExperimentalFocus
-fun Modifier.focusRequester(focusRequester: FocusRequester): Modifier {
-    return this.then(
-        FocusRequesterModifierImpl(
-            focusRequester = focusRequester,
-            inspectorInfo = debugInspectorInfo {
-                name = "focusRequester"
-                properties["focusRequester"] = focusRequester
-            }
-        )
-    )
-}
+@Suppress("UNUSED_PARAMETER")
+@Deprecated(
+    message = "Use Modifier.focusReference instead",
+    replaceWith = ReplaceWith(
+        "this.focusReference(focusReference)",
+        "androidx.compose.ui.focus.focusReference"
+    ),
+    level = DeprecationLevel.ERROR
+)
+fun Modifier.focusRequester(focusRequester: Any): Modifier = this
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/Autofill.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/Autofill.kt
index 09f55f3..2fca515 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/Autofill.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/Autofill.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.autofill
 
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.util.annotation.GuardedBy
 
@@ -26,6 +27,7 @@
  * or cancel autofill as required. For instance, the [TextField] can call [requestAutofillForNode]
  * when it gains focus, and [cancelAutofillForNode] when it loses focus.
  */
+@ExperimentalComposeUiApi
 interface Autofill {
 
     /**
@@ -66,6 +68,7 @@
  *
  * @property id A virtual id that is automatically generated for each node.
  */
+@ExperimentalComposeUiApi
 data class AutofillNode(
     val autofillTypes: List<AutofillType> = listOf(),
     var boundingBox: Rect? = null,
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillTree.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillTree.kt
index da01afb..5a8eb84 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillTree.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillTree.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.ui.autofill
 
+import androidx.compose.ui.ExperimentalComposeUiApi
+
 /**
  * The autofill tree is a temporary data structure that is used before the Semantics Tree is
  * implemented. This data structure is used by compose components to set autofill
@@ -27,6 +29,7 @@
  * Since this is a temporary implementation, it is implemented as a list of [children], which is
  * essentially a tree of height = 1
  */
+@ExperimentalComposeUiApi
 class AutofillTree {
     /**
      * A map which contains [AutofillNode]s, where every node represents an autofillable field.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillType.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillType.kt
index 54af0f4..f0e1b16 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillType.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillType.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.ui.autofill
 
+import androidx.compose.ui.ExperimentalComposeUiApi
+
 /**
  * Autofill type information.
  *
@@ -24,6 +26,7 @@
  * to use heuristics to determine the right value to use while
  * autofilling the corresponding field.
  */
+@ExperimentalComposeUiApi
 enum class AutofillType {
     /**
      * Indicates that the associated component can be aufofilled with an email address.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusChangedModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusChangedModifier.kt
new file mode 100644
index 0000000..cc14f53
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusChangedModifier.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.focus
+
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.platform.debugInspectorInfo
+
+/**
+ * Add this modifier to a component to observe focus state events. [onFocusChanged] is invoked
+ * only when the focus state changes.
+ *
+ * If you want to be notified every time the internal focus state is written to (even if it
+ * hasn't changed), use [onFocusEvent] instead.
+ */
+fun Modifier.onFocusChanged(onFocusChanged: (FocusState) -> Unit): Modifier =
+    composed(
+        inspectorInfo = debugInspectorInfo {
+            name = "onFocusChanged"
+            properties["onFocusChanged"] = onFocusChanged
+        }
+    ) {
+        val focusState: MutableState<FocusState?> = remember { mutableStateOf(null) }
+        Modifier.onFocusEvent {
+            if (focusState.value != it) {
+                focusState.value = it
+                onFocusChanged(it)
+            }
+        }
+    }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt
new file mode 100644
index 0000000..87a3749
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.focus
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+
+/**
+ * A [modifier][Modifier.Element] that can be used to observe focus state events.
+ */
+interface FocusEventModifier : Modifier.Element {
+    /**
+     * A callback that is called whenever the focus system raises events.
+     */
+    fun onFocusEvent(focusState: FocusState)
+}
+
+internal class FocusEventModifierImpl(
+    val onFocusEvent: (FocusState) -> Unit,
+    inspectorInfo: InspectorInfo.() -> Unit
+) : FocusEventModifier, InspectorValueInfo(inspectorInfo) {
+    override fun onFocusEvent(focusState: FocusState) {
+        onFocusEvent.invoke(focusState)
+    }
+}
+
+/**
+ * Add this modifier to a component to observe focus state events.
+ */
+fun Modifier.onFocusEvent(onFocusEvent: (FocusState) -> Unit): Modifier {
+    return this.then(
+        FocusEventModifierImpl(
+            onFocusEvent = onFocusEvent,
+            inspectorInfo = debugInspectorInfo {
+                name = "onFocusEvent"
+                properties["onFocusEvent"] = onFocusEvent
+            }
+        )
+    )
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt
index f396c48..a3f1672 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt
@@ -16,14 +16,12 @@
 
 package androidx.compose.ui.focus
 
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.Inactive
 import androidx.compose.ui.gesture.PointerInputModifierImpl
 import androidx.compose.ui.gesture.TapGestureFilter
 
-@ExperimentalFocus
 interface FocusManager {
     /**
      * Call this function to clear focus from the currently focused component, and set the focus to
@@ -41,7 +39,6 @@
  *
  * @param focusModifier The modifier that will be used as the root focus modifier.
  */
-@ExperimentalFocus
 internal class FocusManagerImpl(
     private val focusModifier: FocusModifier = FocusModifier(Inactive)
 ) : FocusManager {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusModifier.kt
new file mode 100644
index 0000000..1b78e52
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusModifier.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.focus
+
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.node.ModifiedFocusNode
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.NoInspectorInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+
+/**
+ * A [Modifier.Element] that wraps makes the modifiers on the right into a Focusable. Use a
+ * different instance of [FocusModifier] for each focusable component.
+ */
+internal class FocusModifier(
+    initialFocus: FocusState,
+    // TODO(b/172265016): Make this a required parameter and remove the default value.
+    //  Set this value in AndroidComposeView, and other places where we create a focus modifier
+    //  using this internal constructor.
+    inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo
+) : Modifier.Element, InspectorValueInfo(inspectorInfo) {
+
+    var focusState: FocusState = initialFocus
+        set(value) {
+            field = value
+            focusNode.wrappedBy?.propagateFocusEvent(value)
+        }
+
+    var focusedChild: ModifiedFocusNode? = null
+
+    lateinit var focusNode: ModifiedFocusNode
+}
+
+/**
+ * Add this modifier to a component to make it focusable.
+ */
+fun Modifier.focusModifier(): Modifier = composed(debugInspectorInfo { name = "focusModifier" }) {
+    remember { FocusModifier(FocusState.Inactive) }
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusReference.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusReference.kt
new file mode 100644
index 0000000..477ef4d
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusReference.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.focus
+
+import androidx.compose.runtime.collection.MutableVector
+import androidx.compose.runtime.collection.mutableVectorOf
+import androidx.compose.ui.node.ModifiedFocusReferenceNode
+
+private val focusReferenceNotInitialized = "FocusReference is not initialized. One reason for " +
+    "this is that you requesting focus changes during composition. Focus references should " +
+    "not be made during composition, but should be made in response to some event."
+
+@Deprecated(
+    message = "Use FocusReference instead",
+    replaceWith = ReplaceWith("FocusReference", "androidx.compose.ui.focus.FocusReference"),
+    level = DeprecationLevel.ERROR
+)
+class FocusRequester {
+    fun requestFocus() {}
+    fun captureFocus(): Boolean = false
+    fun freeFocus(): Boolean = false
+}
+
+/**
+ * The [FocusReference] is used in conjunction with
+ * [Modifier.focusReference][androidx.compose.ui.focus.focusReference] to send requests for focus
+ * state change.
+ *
+ * @see androidx.compose.ui.focus.focusReference
+ */
+class FocusReference {
+
+    internal val focusReferenceNodes: MutableVector<ModifiedFocusReferenceNode> = mutableVectorOf()
+
+    /**
+     * Use this function to request focus. If the system grants focus to a component associated
+     * with this [FocusReference], its [state][FocusState] will be set to
+     * [Active][FocusState.Active].
+     */
+    fun requestFocus() {
+        check(focusReferenceNodes.isNotEmpty()) { focusReferenceNotInitialized }
+        focusReferenceNodes.forEach { it.findFocusNode()?.requestFocus(propagateFocus = false) }
+    }
+
+    /**
+     * Deny requests to clear focus.
+     *
+     * Use this function to send a request to capture the focus. If a component is captured,
+     * its [state][FocusState] will be set to [Captured][FocusState.Captured]. When a
+     * component is in this state, it holds onto focus until [freeFocus] is called. When a
+     * component is in the [Captured][FocusState.Captured] state, all focus requests from
+     * other components are declined.
+     *
+     * @return true if the focus was successfully captured by one of the
+     * [focus][androidx.compose.ui.focus] modifiers associated with this [FocusReference].
+     * false otherwise.
+     */
+    fun captureFocus(): Boolean {
+        check(focusReferenceNodes.isNotEmpty()) { focusReferenceNotInitialized }
+        var success = false
+        focusReferenceNodes.forEach {
+            it.findFocusNode()?.apply {
+                if (captureFocus()) {
+                    success = true
+                }
+            }
+        }
+        return success
+    }
+
+    /**
+     * Use this function to send a request to release focus when one of the components associated
+     * with this [FocusReference] is in a [Captured][FocusState.Captured] state.
+     *
+     * When the node is in the [Captured][FocusState.Captured] state, it rejects all requests to
+     * clear focus. Calling
+     * [freeFocus] puts the node in the [Active][FocusState.Active] state, where it is no longer
+     * preventing other
+     * nodes from requesting focus.
+     *
+     * @return true if the focus was successfully released. i.e. At the end of this operation,
+     * one of the components associated with this
+     * [focusReference][androidx.compose.ui.focus.focusReference] is in the
+     * [Active][FocusState.Active] state. false otherwise.
+     */
+    fun freeFocus(): Boolean {
+        check(focusReferenceNodes.isNotEmpty()) { focusReferenceNotInitialized }
+        var success = false
+        focusReferenceNodes.forEach {
+            it.findFocusNode()?.apply {
+                if (freeFocus()) {
+                    success = true
+                }
+            }
+        }
+        return success
+    }
+
+    companion object {
+        /**
+         * Convenient way to create multiple [FocusReference] instances.
+         */
+        object FocusReferenceFactory {
+            operator fun component1() = FocusReference()
+            operator fun component2() = FocusReference()
+            operator fun component3() = FocusReference()
+            operator fun component4() = FocusReference()
+            operator fun component5() = FocusReference()
+            operator fun component6() = FocusReference()
+            operator fun component7() = FocusReference()
+            operator fun component8() = FocusReference()
+            operator fun component9() = FocusReference()
+            operator fun component10() = FocusReference()
+            operator fun component11() = FocusReference()
+            operator fun component12() = FocusReference()
+            operator fun component13() = FocusReference()
+            operator fun component14() = FocusReference()
+            operator fun component15() = FocusReference()
+            operator fun component16() = FocusReference()
+        }
+
+        /**
+         * Convenient way to create multiple [FocusReference]s, which can to be used to request
+         * focus, or to specify a focus traversal order.
+         */
+        fun createRefs() = FocusReferenceFactory
+    }
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusReferenceModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusReferenceModifier.kt
new file mode 100644
index 0000000..fc7c7c9
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusReferenceModifier.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.focus
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+
+/**
+ * A [modifier][Modifier.Element] that can be used to pass in a [FocusReference] that can be used
+ * to request focus state changes.
+ *
+ * @see FocusReference
+ */
+interface FocusReferenceModifier : Modifier.Element {
+    /**
+     * An instance of [FocusReference], that can be used to request focus state changes.
+     */
+    val focusReference: FocusReference
+}
+
+internal class FocusReferenceModifierImpl(
+    override val focusReference: FocusReference,
+    inspectorInfo: InspectorInfo.() -> Unit
+) : FocusReferenceModifier, InspectorValueInfo(inspectorInfo)
+
+/**
+ * Add this modifier to a component to observe changes to focus state.
+ */
+fun Modifier.focusReference(focusReference: FocusReference): Modifier {
+    return this.then(
+        FocusReferenceModifierImpl(
+            focusReference = focusReference,
+            inspectorInfo = debugInspectorInfo {
+                name = "focusReference"
+                properties["focusReference"] = focusReference
+            }
+        )
+    )
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequester.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequester.kt
deleted file mode 100644
index d4a8690..0000000
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequester.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.focus
-
-import androidx.compose.runtime.collection.MutableVector
-import androidx.compose.runtime.collection.mutableVectorOf
-import androidx.compose.ui.node.ModifiedFocusRequesterNode
-
-private val focusRequesterNotInitialized = "FocusRequester is not initialized. One reason for " +
-    "this is that you requesting focus changes during composition. Focus requests should " +
-    "not be made during composition, but should be made in response to some event."
-
-/**
- * The [FocusRequester] is used in conjunction with
- * [Modifier.focusRequester][androidx.compose.ui.focusRequester] to send requests for focus state
- * change.
- *
- * @see androidx.compose.ui.focusRequester
- */
-@ExperimentalFocus
-class FocusRequester {
-
-    internal val focusRequesterNodes: MutableVector<ModifiedFocusRequesterNode> = mutableVectorOf()
-
-    /**
-     * Use this function to request focus. If the system grants focus to a component associated
-     * with this [FocusRequester], its [state][FocusState] will be set to
-     * [Active][FocusState.Active].
-     */
-    fun requestFocus() {
-        check(focusRequesterNodes.isNotEmpty()) { focusRequesterNotInitialized }
-        focusRequesterNodes.forEach { it.findFocusNode()?.requestFocus(propagateFocus = false) }
-    }
-
-    /**
-     * Deny requests to clear focus.
-     *
-     * Use this function to send a request to capture the focus. If a component is captured,
-     * its [state][FocusState] will be set to [Captured][FocusState.Captured]. When a
-     * component is in this state, it holds onto focus until [freeFocus] is called. When a
-     * component is in the [Captured][FocusState.Captured] state, all focus requests from
-     * other components are declined.
-     *
-     * @return true if the focus was successfully captured by one of the
-     * [focus][androidx.compose.ui.focus] modifiers associated with this [FocusRequester].
-     * false otherwise.
-     */
-    fun captureFocus(): Boolean {
-        check(focusRequesterNodes.isNotEmpty()) { focusRequesterNotInitialized }
-        var success = false
-        focusRequesterNodes.forEach {
-            it.findFocusNode()?.apply {
-                if (captureFocus()) {
-                    success = true
-                }
-            }
-        }
-        return success
-    }
-
-    /**
-     * Use this function to send a request to release focus when one of the components associated
-     * with this [FocusRequester] is in a [Captured][FocusState.Captured] state.
-     *
-     * When the node is in the [Captured][FocusState.Captured] state, it rejects all requests to clear focus. Calling
-     * [freeFocus] puts the node in the [Active][FocusState.Active] state, where it is no longer
-     * preventing other
-     * nodes from requesting focus.
-     *
-     * @return true if the focus was successfully released. i.e. At the end of this operation,
-     * one of the components associated with this
-     * [focusRequester][androidx.compose.ui.focusRequester] is in the [Active][FocusState.Active]
-     * state. false otherwise.
-     */
-    fun freeFocus(): Boolean {
-        check(focusRequesterNodes.isNotEmpty()) { focusRequesterNotInitialized }
-        var success = false
-        focusRequesterNodes.forEach {
-            it.findFocusNode()?.apply {
-                if (freeFocus()) {
-                    success = true
-                }
-            }
-        }
-        return success
-    }
-}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusState.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusState.kt
index 0182bd3..68a9f7c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusState.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusState.kt
@@ -20,7 +20,6 @@
  * Different states of the focus system. These are the states used by the Focus Nodes.
  *
  */
-@ExperimentalFocus
 enum class FocusState {
     /**
      * The focusable component is currently active (i.e. it receives key events).
@@ -56,7 +55,6 @@
  *
  * @return true if the component is focused, false otherwise.
  */
-@ExperimentalFocus
 val FocusState.isFocused
     get() = when (this) {
         FocusState.Captured,
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/Constants.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/Constants.kt
index e7d546e..6e04fa1 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/Constants.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/Constants.kt
@@ -102,13 +102,13 @@
  */
 // TODO(shepshapard): Create variants of HorizontalDragGestureFilter et al for
 // paging, which use this constant.
-val PagingTouchSlop = TouchSlop * 2.dp
+val PagingTouchSlop = TouchSlop * 2f
 
 /**
  * The distance a touch has to travel for the framework to be confident that
  * the gesture is a panning gesture.
  */
-val PanSlop = TouchSlop * 2.dp
+val PanSlop = TouchSlop * 2f
 
 /**
  * The absolute cumulative average change in distance of all pointers from the
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 39bb158..5d2b6f4 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
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.ui.gesture
 
 import androidx.compose.runtime.remember
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 5c386f1..141b8ec 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
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.ui.gesture
 
 import androidx.compose.runtime.remember
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ExperimentalPointerInput.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ExperimentalPointerInput.kt
deleted file mode 100644
index 0bfd44c..0000000
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ExperimentalPointerInput.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.gesture
-
-@RequiresOptIn(
-    "This pointer input API is experimental and is likely to change before becoming " +
-        "stable."
-)
-annotation class ExperimentalPointerInput
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawDragGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawDragGestureFilter.kt
index 430326b..2fa2b6b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawDragGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawDragGestureFilter.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.ui.gesture
 
 import androidx.compose.runtime.remember
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/TapGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/TapGestureFilter.kt
index 57e5807..e3aba9e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/TapGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/TapGestureFilter.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.ui.gesture
 
 import androidx.compose.runtime.remember
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/customevents/DelayUpEvent.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/customevents/DelayUpEvent.kt
index 10ea0f1..b2787cd 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/customevents/DelayUpEvent.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/customevents/DelayUpEvent.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.gesture.customevents
 
 import androidx.compose.ui.gesture.DoubleTapGestureFilter
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.gesture.TapGestureFilter
 import androidx.compose.ui.input.pointer.CustomEvent
 import androidx.compose.ui.input.pointer.PointerId
@@ -40,7 +39,6 @@
  * @param pointers The pointers whose up events are being requested to be delayed.
  */
 @Suppress("EqualsOrHashCode")
-@ExperimentalPointerInput
 data class DelayUpEvent(var message: DelayUpMessage, val pointers: Set<PointerId>) : CustomEvent {
 
     // Only generating hash code with immutable property.
@@ -52,7 +50,6 @@
 /**
  * The types of messages that can be dispatched.
  */
-@ExperimentalPointerInput
 enum class DelayUpMessage {
     /**
      * Reports that future "up events" should not result in any normally related callbacks at
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/nestedscroll/NestedScrollDelegatingWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/nestedscroll/NestedScrollDelegatingWrapper.kt
new file mode 100644
index 0000000..6c29a8d
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/nestedscroll/NestedScrollDelegatingWrapper.kt
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.gesture.nestedscroll
+
+import androidx.compose.runtime.collection.MutableVector
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.node.DelegatingLayoutNodeWrapper
+import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.node.LayoutNodeWrapper
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.minus
+import androidx.compose.ui.unit.plus
+
+internal class NestedScrollDelegatingWrapper(
+    wrapped: LayoutNodeWrapper,
+    nestedScrollModifier: NestedScrollModifier
+) : DelegatingLayoutNodeWrapper<NestedScrollModifier>(wrapped, nestedScrollModifier) {
+
+    // reference to the parent connection to properly dispatch or provide to children when detached
+    private var parentConnection: NestedScrollConnection? = null
+        set(value) {
+            modifier.dispatcher.parent = value
+            childScrollConnection.parent = value ?: NoOpConnection
+            field = value
+        }
+
+    // save last modifier until the next onModifierChanged() call to understand if we got new
+    // connection or a new dispatcher, therefore we need to update self and our children
+    private var lastModifier: NestedScrollModifier? = null
+
+    override fun onModifierChanged() {
+        super.onModifierChanged()
+        childScrollConnection.self = modifier.connection
+        modifier.dispatcher.parent = parentConnection
+        refreshSelfIfNeeded()
+    }
+
+    override var modifier: NestedScrollModifier
+        get() = super.modifier
+        set(value) {
+            lastModifier = super.modifier
+            super.modifier = value
+        }
+
+    override fun attach() {
+        super.attach()
+        refreshSelfIfNeeded()
+    }
+
+    override fun detach() {
+        super.detach()
+        refreshChildrenWithParentConnection(parentConnection)
+        lastModifier = null
+    }
+
+    override fun findPreviousNestedScrollWrapper() = this
+
+    override fun findNextNestedScrollWrapper() = this
+
+    private val childScrollConnection = ParentWrapperNestedScrollConnection(
+        parent = parentConnection ?: NoOpConnection,
+        self = nestedScrollModifier.connection
+    )
+
+    private fun refreshSelfIfNeeded() {
+        val localLastModifier = lastModifier
+        val modifierChanged = localLastModifier == null ||
+            localLastModifier.connection !== modifier.connection ||
+            localLastModifier.dispatcher !== modifier.dispatcher
+        if (modifierChanged && isAttached) {
+            parentConnection = super.findPreviousNestedScrollWrapper()?.childScrollConnection
+            refreshChildrenWithParentConnection(childScrollConnection)
+            lastModifier = modifier
+        }
+    }
+
+    /**
+     * Supply new parent connection for children. Initially children can do it themselves, but
+     * after runtime nestedscroll graph changes parents need to update their children.
+     *
+     * This is O(n) operation, so call only when parent really changes (connection changes,
+     * detach, attach, etc)
+     */
+    private fun refreshChildrenWithParentConnection(newParent: NestedScrollConnection?) {
+        nestedScrollChildrenResult.clear()
+        val nextNestedScrollWrapper = wrapped.findNextNestedScrollWrapper()
+        if (nextNestedScrollWrapper != null) {
+            nestedScrollChildrenResult.add(nextNestedScrollWrapper)
+        } else {
+            loopChildrenForNestedScroll(layoutNode._children)
+        }
+        nestedScrollChildrenResult.forEach {
+            it.parentConnection = newParent
+        }
+    }
+
+    private fun loopChildrenForNestedScroll(children: MutableVector<LayoutNode>) {
+        children.forEach { child ->
+            val nestedScrollChild =
+                child.outerLayoutNodeWrapper.findNextNestedScrollWrapper()
+            if (nestedScrollChild != null) {
+                nestedScrollChildrenResult.add(nestedScrollChild)
+            } else {
+                loopChildrenForNestedScroll(child._children)
+            }
+        }
+    }
+
+    // do not use directly, this is only for optimization.
+    // Populated and returned by findNestedScrollChildren.
+    private val nestedScrollChildrenResult = MutableVector<NestedScrollDelegatingWrapper>()
+}
+
+/**
+ * Parent-child binding contract. This wrapper guarantees pre-scroll/scroll/pre-fling/fling call
+ * order in the nested scroll chain.
+ */
+private class ParentWrapperNestedScrollConnection(
+    var parent: NestedScrollConnection,
+    var self: NestedScrollConnection
+) : NestedScrollConnection {
+
+    override fun onPreScroll(
+        available: Offset,
+        source: NestedScrollSource
+    ): Offset {
+        val parentPreConsumed = parent.onPreScroll(available, source)
+        val selfPreConsumed = self.onPreScroll(available - parentPreConsumed, source)
+        return parentPreConsumed + selfPreConsumed
+    }
+
+    override fun onPostScroll(
+        consumed: Offset,
+        available: Offset,
+        source: NestedScrollSource
+    ): Offset {
+        val selfConsumed = self.onPostScroll(consumed, available, source)
+        val parentConsumed =
+            parent.onPostScroll(consumed + selfConsumed, available - selfConsumed, source)
+        return selfConsumed + parentConsumed
+    }
+
+    override fun onPreFling(available: Velocity): Velocity {
+        val parentPreConsumed = parent.onPreFling(available)
+        val selfPreConsumed = self.onPreFling(available - parentPreConsumed)
+        return parentPreConsumed + selfPreConsumed
+    }
+
+    override fun onPostFling(
+        consumed: Velocity,
+        available: Velocity,
+        onFinished: (Velocity) -> Unit
+    ) {
+        val selfEnd = { selfConsumed: Velocity ->
+            val parentEnd = { parentConsumed: Velocity ->
+                onFinished.invoke(selfConsumed + parentConsumed)
+            }
+            parent.onPostFling(
+                consumed + selfConsumed,
+                available - selfConsumed,
+                parentEnd
+            )
+        }
+        self.onPostFling(consumed, available, selfEnd)
+    }
+}
+
+/**
+ * No-op parent that consumed nothing. Should be gone by b/174348612
+ */
+private val NoOpConnection: NestedScrollConnection = object : NestedScrollConnection {
+
+    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset =
+        Offset.Zero
+
+    override fun onPostScroll(
+        consumed: Offset,
+        available: Offset,
+        source: NestedScrollSource
+    ): Offset =
+        Offset.Zero
+
+    override fun onPreFling(available: Velocity): Velocity = Velocity.Zero
+
+    override fun onPostFling(
+        consumed: Velocity,
+        available: Velocity,
+        onFinished: (Velocity) -> Unit
+    ) {
+        onFinished.invoke(Velocity.Zero)
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/nestedscroll/NestedScrollModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/nestedscroll/NestedScrollModifier.kt
new file mode 100644
index 0000000..fbbb177
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/nestedscroll/NestedScrollModifier.kt
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.gesture.nestedscroll
+
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Velocity
+
+/**
+ * A [Modifier.Element] that represents nested scroll node in the hierarchy
+ */
+internal interface NestedScrollModifier : Modifier.Element {
+
+    /**
+     * Nested scroll events dispatcher to notify nested scrolling system about scroll events.
+     * This is to be used by the nodes that are scrollable themselves to notify
+     * [NestedScrollConnection]s in the tree.
+     *
+     * Note: The [connection] passed to the [NestedScrollModifier] doesn't count as an ancestor
+     * since it's the node itself
+     */
+    val dispatcher: NestedScrollDispatcher
+
+    /**
+     * Nested scroll connection to participate in the nested scroll events chain. Implementing
+     * this connection allows to react on the nested scroll related events and influence
+     * scrolling descendants and ascendants
+     */
+    val connection: NestedScrollConnection
+}
+
+/**
+ * Interface to connect to the nested scroll system.
+ *
+ * Pass this connection to the [nestedScroll] modifier to participate in the nested scroll
+ * hierarchy and to receive nested scroll events when they are dispatched by the scrolling child
+ * (scrolling child - the element that actually receives scrolling events and dispatches them via
+ * [NestedScrollDispatcher]).
+ *
+ * @see NestedScrollDispatcher to learn how to dispatch nested scroll events to become a
+ * scrolling child
+ * @see nestedScroll to attach this connection to the nested scroll system
+ */
+interface NestedScrollConnection {
+
+    /**
+     * Pre scroll event chain. Called by children to allow parents to consume a portion of a drag
+     * event beforehand
+     *
+     * @param available the delta available to consume for pre scroll
+     * @param source the source of the scroll event
+     *
+     * @see NestedScrollSource
+     *
+     * @return the amount this connection consumed
+     */
+    fun onPreScroll(available: Offset, source: NestedScrollSource): Offset = Offset.Zero
+
+    /**
+     * Post scroll event pass. This pass occurs when the dispatching (scrolling) descendant made
+     * their consumption and notifies ancestors with what's left for them to consume.
+     *
+     * @param consumed the amount that was consumed by all nested scroll nodes below the hierarchy
+     * @param available the amount of delta available for this connection to consume
+     * @param source source of the scroll
+     *
+     * @see NestedScrollSource
+     *
+     * @return the amount that was consumed by this connection
+     */
+    fun onPostScroll(
+        consumed: Offset,
+        available: Offset,
+        source: NestedScrollSource
+    ): Offset = Offset.Zero
+
+    /**
+     * Pre fling event chain. Called by children when they are about to perform fling to
+     * allow parents to intercept and consume part of the initial velocity
+     *
+     * @param available the velocity which is available to pre consume and with which the child
+     * is about to fling
+     *
+     * @return the amount this connection wants to consume and take from the child
+     */
+    fun onPreFling(available: Velocity): Velocity = Velocity.Zero
+
+    /**
+     * Post fling event chain. Called by the child when it is finished flinging (and sending
+     * [onPreScroll] & [onPostScroll] events)
+     *
+     * @param consumed the amount of velocity consumed by the child
+     * @param available the amount of velocity left for a parent to fling after the child (if
+     * desired)
+     * @param onFinished callback to be called when this connection finished flinging, to
+     * be called with the amount of velocity consumed by the fling operation. This callback is
+     * crucial to be called in order to ensure nodes above will receive their [onPostFling].
+     */
+    // TODO: remove notifySelfFinish when b/174485541
+    fun onPostFling(
+        consumed: Velocity,
+        available: Velocity,
+        onFinished: (Velocity) -> Unit
+    ) {
+        onFinished(Velocity.Zero)
+    }
+}
+
+/**
+ * Nested scroll events dispatcher to notify the nested scroll system about the scrolling events
+ * that are happening on the element.
+ *
+ * If the element/modifier itself is able to receive scroll events (from the touch, fling,
+ * mouse, etc) and it would like to respect nested scrolling by notifying elements above, it should
+ * properly dispatch nested scroll events when being scrolled
+ *
+ * It is important to dispatch these events at the right time, provide valid information to the
+ * parents and react to the feedback received from them in order to provide good user experience
+ * with other nested scrolling nodes.
+ *
+ * @see nestedScroll for the reference of the nested scroll process and more details
+ * @see NestedScrollConnection to connect to the nested scroll system
+ */
+class NestedScrollDispatcher {
+
+    /**
+     * Parent to be set when attached to nested scrolling chain. `null` is valid and means there no
+     * nested scrolling parent above
+     */
+    internal var parent: NestedScrollConnection? = null
+
+    /**
+     * Dispatch pre scroll pass. This triggers [NestedScrollConnection.onPreScroll] on all the
+     * ancestors giving them possibility to pre-consume delta if they desire so.
+     *
+     * @param available the delta arrived from a scroll event
+     * @param source the source of the scroll event
+     *
+     * @return total delta that is pre-consumed by all ancestors in the chain. This delta is
+     * unavailable for this node to consume, so it should adjust the consumption accordingly
+     */
+    fun dispatchPreScroll(available: Offset, source: NestedScrollSource): Offset {
+        return parent?.onPreScroll(available, source) ?: Offset.Zero
+    }
+
+    /**
+     * Dispatch nested post-scrolling pass. This triggers [NestedScrollConnection.onPostScroll] on
+     * all the ancestors giving them possibility to react of the scroll deltas that are left
+     * after the dispatching node itself and other [NestedScrollConnection]s below consumed the
+     * desired amount.
+     *
+     * @param consumed the amount that this node consumed already
+     * @param available the amount of delta left for ancestors
+     * @param source source of the scroll
+     *
+     * @return the amount of scroll that was consumed by all ancestors
+     */
+    fun dispatchPostScroll(
+        consumed: Offset,
+        available: Offset,
+        source: NestedScrollSource
+    ): Offset {
+        return parent?.onPostScroll(consumed, available, source) ?: Offset.Zero
+    }
+
+    /**
+     * Dispatch pre fling pass. This triggers [NestedScrollConnection.onPreFling] on all the
+     * ancestors giving them a possibility to react on the fling that is about to happen and
+     * consume part of the velocity.
+     *
+     * @param available velocity from the scroll evens that this node is about to fling with
+     *
+     * @return total velocity that is pre-consumed by all ancestors in the chain. This velocity is
+     * unavailable for this node to consume, so it should adjust the consumption accordingly
+     */
+    fun dispatchPreFling(available: Velocity): Velocity {
+        return parent?.onPreFling(available) ?: Velocity.Zero
+    }
+
+    /**
+     * Dispatch post fling pass. This triggers [NestedScrollConnection.onPostFling] on all the
+     * ancestors, giving them possibility to react of the velocity that is left after the
+     * dispatching node itself flung with the desired amount.
+     *
+     * @param consumed velocity already consumed by this node
+     * @param available velocity that is left for ancestors to consume
+     */
+    fun dispatchPostFling(consumed: Velocity, available: Velocity) {
+        parent?.onPostFling(consumed, available) {}
+    }
+}
+
+/**
+ * Possible sources of scroll events in the [NestedScrollConnection]
+ */
+enum class NestedScrollSource {
+    /**
+     * Dragging via mouse/touch/etc events
+     */
+    Drag,
+
+    /**
+     * Flinging after the drag has ended with velocity
+     */
+    Fling
+}
+
+/**
+ * Modify element to make it participate in the nested scrolling hierarchy.
+ *
+ * There are two ways to participate in the nested scroll: as a scrolling child by dispatching
+ * scrolling events via [NestedScrollDispatcher] to the nested scroll chain; and as a member of
+ * nested scroll chain by providing [NestedScrollConnection], which will be called when another
+ * nested scrolling child below dispatches scrolling events.
+ *
+ * It's a mandatory to participate as a [NestedScrollConnection] in the chain, but scrolling
+ * events dispatch is optional since there are cases when element wants to participate in the
+ * nested scroll, but not a scrollable thing itself.
+ *
+ * Note: It is recommended to reuse [NestedScrollConnection] and [NestedScrollDispatcher] objects
+ * between recompositions since different object will cause nested scroll graph to be
+ * recalculated unnecessary.
+ *
+ * There are 4 main passes in nested scrolling system:
+ *
+ * 1. Pre-scroll. This callback is triggered when the descendant is about to perform a scroll
+ * operation and gives parent an opportunity to consume part of child's delta beforehand. This
+ * pass should happen every time scrollable components receives delta and dispatches it via
+ * [NestedScrollDispatcher]. Dispatching child should take into account how much all ancestors
+ * above the hierarchy consumed and adjust the consumption accordingly.
+ *
+ * 2. Post-scroll. This callback is triggered when the descendant consumed the delta already
+ * (after taking into account what parents pre-consumed in 1.) and wants to notify the ancestors
+ * with the amount of delta unconsumed. This pass should happen every time scrollable components
+ * receives delta and dispatches it via [NestedScrollDispatcher]. Any parent that receives
+ * [NestedScrollConnection.onPostScroll] should consume no more than `left` and return the amount
+ * consumed.
+ *
+ * 3. Pre-fling. Pass that happens when the scrolling descendant stopped dragging and about to
+ * fling with the some velocity. This callback allows ancestors to consume part of the velocity.
+ * This pass should happen before the fling itself happens. Similar to pre-scroll, parent can
+ * consume part of the velocity and nodes below (including the dispatching child) should adjust
+ * their logic to accommodate only the velocity left.
+ *
+ * 4. Post-fling. Pass that happens after the scrolling descendant stopped flinging and wants to
+ * notify ancestors about that fact, providing velocity left to consume as a part of this. This
+ * pass should happen after the fling itself happens on the scrolling child. Ancestors of the
+ * dispatching node will have opportunity to fling themselves with the `velocityLeft` provided.
+ * Parent must call `notifySelfFinish` callback in order to continue the propagation of the
+ * velocity that is left to ancestors above.
+ *
+ * Example of the nested scrolling interaction where component both dispatches and consumed
+ * children's delta:
+ * @sample androidx.compose.ui.samples.NestedScrollSample
+ *
+ * @param connection connection to the nested scroll system to participate in the event chaining,
+ * receiving events when scrollable descendant is being scrolled.
+ * @param dispatcher object to be attached to the nested scroll system on which `dispatch*`
+ * methods can be called to notify ancestors within nested scroll system about scrolling happening
+ */
+fun Modifier.nestedScroll(
+    connection: NestedScrollConnection,
+    dispatcher: NestedScrollDispatcher? = null
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "nestedScroll"
+        properties["connection"] = connection
+        properties["dispatcher"] = dispatcher
+    }
+) {
+    // provide noop dispatcher if needed
+    val resolvedDispatcher = dispatcher ?: remember { NestedScrollDispatcher() }
+    remember(connection, resolvedDispatcher) {
+        object : NestedScrollModifier {
+            override val dispatcher: NestedScrollDispatcher = resolvedDispatcher
+            override val connection: NestedScrollConnection = connection
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLocker.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLocker.kt
index 7ea0160..8d43c717 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLocker.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLocker.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.gesture.scrollorientationlocking
 
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.input.pointer.CustomEvent
 import androidx.compose.ui.input.pointer.CustomEventDispatcher
 import androidx.compose.ui.input.pointer.PointerEventPass
@@ -46,7 +45,6 @@
  * [onCancel] to use
  * this correctly.
  */
-@ExperimentalPointerInput
 class ScrollOrientationLocker(private val customEventDispatcher: CustomEventDispatcher) {
 
     private var locker: InternalScrollOrientationLocker? = null
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/ExperimentalKeyInput.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/ExperimentalKeyInput.kt
deleted file mode 100644
index f176c30..0000000
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/ExperimentalKeyInput.kt
+++ /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.compose.ui.input.key
-
-@RequiresOptIn("The Key Input API is experimental and is likely to change in the future.")
-annotation class ExperimentalKeyInput
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/Key.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/Key.kt
index fea9b39..69174ed6 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/Key.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/Key.kt
@@ -21,7 +21,6 @@
  *
  * @param keyCode an integer code representing the key pressed.
  */
-@ExperimentalKeyInput
 expect inline class Key(val keyCode: Int) {
     companion object {
         /** Unknown key. */
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyEvent.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyEvent.kt
index 47f001c..b5e4fa0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyEvent.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyEvent.kt
@@ -20,7 +20,6 @@
  * When a user presses a key on a hardware keyboard, a [KeyEvent] is sent to the
  * [KeyInputModifier] that is currently active.
  */
-@ExperimentalKeyInput
 interface KeyEvent {
     /**
      * The key that was pressed.
@@ -69,23 +68,11 @@
      * Indicates whether the Shift key is pressed.
      */
     val isShiftPressed: Boolean
-
-    /**
-     * Indicates the status of the Alt key.
-     */
-    @Suppress("DEPRECATION")
-    @Deprecated(
-        "alt is replaced by isAltPressed",
-        ReplaceWith("isAltPressed"),
-        DeprecationLevel.ERROR
-    )
-    val alt: Alt
 }
 
 /**
  * The type of Key Event.
  */
-@ExperimentalKeyInput
 enum class KeyEventType {
     /**
      * Unknown key event.
@@ -102,29 +89,3 @@
      */
     KeyDown
 }
-
-/**
- * Indicates the status of the Alt key.
- */
-@Deprecated(
-    message = "Alt is replaced by KeyEvent.isAltPressed",
-    level = DeprecationLevel.WARNING
-)
-@ExperimentalKeyInput
-interface Alt {
-    /**
-     * Indicates whether the Alt key is pressed.
-     */
-    val isPressed: Boolean
-        get() = isLeftAltPressed || isRightAltPressed
-
-    /**
-     * Indicates whether the left Alt key is pressed.
-     */
-    val isLeftAltPressed: Boolean
-
-    /**
-     * Indicates whether the right Alt key is pressed.
-     */
-    val isRightAltPressed: Boolean
-}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
index dc18697..89dade5 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
@@ -21,15 +21,23 @@
 import androidx.compose.ui.node.ModifiedKeyInputNode
 import androidx.compose.ui.platform.debugInspectorInfo
 
+// TODO(b/175156387): Remove this modifier after Alpha09
 /**
  * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
  * allow it to intercept hardware key events.
  *
  * @param onKeyEvent This callback is invoked when the user interacts with the hardware keyboard.
  * While implementing this callback, return true to stop propagation of this event. If you return
- * false, the key event will be sent to this [keyInputFilter]'s parent.
+ * false, the key event will be sent to this [KeyInputModifier]'s parent.
  */
-@ExperimentalKeyInput
+@Deprecated(
+    message = "Use Modifier.onKeyEvent() instead",
+    replaceWith = ReplaceWith(
+        "onKeyEvent(onKeyEvent)",
+        "androidx.compose.ui.input.key.onKeyEvent"
+    ),
+    level = DeprecationLevel.ERROR
+)
 fun Modifier.keyInputFilter(onKeyEvent: (KeyEvent) -> Boolean): Modifier = composed(
     inspectorInfo = debugInspectorInfo {
         name = "keyInputFilter"
@@ -39,6 +47,51 @@
     KeyInputModifier(onKeyEvent = onKeyEvent, onPreviewKeyEvent = null)
 }
 
+// TODO(b/175156387): Remove this modifier after Alpha09
+/**
+ * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
+ * allow it to intercept hardware key events.
+ *
+ * @param onPreviewKeyEvent This callback is invoked when the user interacts with the hardware
+ * keyboard. It gives ancestors of a focused component the chance to intercept a [KeyEvent].
+ * Return true to stop propagation of this event. If you return false, the key event will be sent
+ * to this [previewKeyInputFilter]'s child. If none of the children consume the event, it will be
+ * sent back up to the root [KeyInputModifier] using the onKeyEvent callback.
+ */
+@Deprecated(
+    message = "Use Modifier.onPreviewKeyEvent() instead",
+    replaceWith = ReplaceWith(
+        "onPreviewKeyEvent(onPreviewKeyEvent)",
+        "androidx.compose.ui.input.key.onPreviewKeyEvent"
+    ),
+    level = DeprecationLevel.ERROR
+)
+fun Modifier.previewKeyInputFilter(onPreviewKeyEvent: (KeyEvent) -> Boolean): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "previewKeyInputFilter"
+        properties["onPreviewKeyEvent"] = onPreviewKeyEvent
+    }
+) {
+    KeyInputModifier(onKeyEvent = null, onPreviewKeyEvent = onPreviewKeyEvent)
+}
+
+/**
+ * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
+ * allow it to intercept hardware key events.
+ *
+ * @param onKeyEvent This callback is invoked when the user interacts with the hardware keyboard.
+ * While implementing this callback, return true to stop propagation of this event. If you return
+ * false, the key event will be sent to this [onKeyEvent]'s parent.
+ */
+fun Modifier.onKeyEvent(onKeyEvent: (KeyEvent) -> Boolean): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "onKeyEvent"
+        properties["onKeyEvent"] = onKeyEvent
+    }
+) {
+    KeyInputModifier(onKeyEvent = onKeyEvent, onPreviewKeyEvent = null)
+}
+
 /**
  * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
  * allow it to intercept hardware key events.
@@ -46,20 +99,18 @@
  * @param onPreviewKeyEvent This callback is invoked when the user interacts with the hardware
  * keyboard. It gives ancestors of a focused component the chance to intercept a [KeyEvent].
  * Return true to stop propagation of this event. If you return false, the key event will be sent
- * to this [previewKeyInputFilter]'s child. If none of the children consume the event, it will be
- * sent back up to the root [keyInputFilter] using the onKeyEvent callback.
+ * to this [onPreviewKeyEvent]'s child. If none of the children consume the event, it will be
+ * sent back up to the root [KeyInputModifier] using the onKeyEvent callback.
  */
-@ExperimentalKeyInput
-fun Modifier.previewKeyInputFilter(onPreviewKeyEvent: (KeyEvent) -> Boolean): Modifier = composed(
+fun Modifier.onPreviewKeyEvent(onPreviewKeyEvent: (KeyEvent) -> Boolean): Modifier = composed(
     inspectorInfo = debugInspectorInfo {
-        name = "previewKeyInputFilter"
+        name = "onPreviewKeyEvent"
         properties["onPreviewKeyEvent"] = onPreviewKeyEvent
     }
 ) {
     KeyInputModifier(onKeyEvent = null, onPreviewKeyEvent = onPreviewKeyEvent)
 }
 
-@OptIn(ExperimentalKeyInput::class)
 internal class KeyInputModifier(
     val onKeyEvent: ((KeyEvent) -> Boolean)?,
     val onPreviewKeyEvent: ((KeyEvent) -> Boolean)?
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
index 2c19b0f..aac7f19 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
@@ -21,7 +21,6 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.AmbientViewConfiguration
 import androidx.compose.ui.platform.ViewConfiguration
@@ -39,17 +38,19 @@
 import kotlin.coroutines.createCoroutine
 import kotlin.coroutines.resume
 
+@Deprecated("Use AwaitPointerEventScope", ReplaceWith("AwaitPointerEventScope"))
+typealias HandlePointerInputScope = AwaitPointerEventScope
+
 /**
- * Receiver scope for awaiting pointer events in a call to [PointerInputScope.handlePointerInput].
+ * Receiver scope for awaiting pointer events in a call to [PointerInputScope.awaitPointerEventScope].
  *
  * This is a restricted suspension scope. Code in this scope is always called undispatched and
- * may only suspend for calls to [awaitPointerEvent] or [awaitCustomEvent]. These functions
+ * may only suspend for calls to [awaitPointerEvent]. These functions
  * resume synchronously and the caller may mutate the result **before** the next await call to
  * affect the next stage of the input processing pipeline.
  */
-@ExperimentalPointerInput
 @RestrictsSuspension
-interface HandlePointerInputScope : Density {
+interface AwaitPointerEventScope : Density {
     /**
      * The measured size of the pointer input region. Input events will be reported with
      * a coordinate space of (0, 0) to (size.width, size,height) as the input region, with
@@ -80,25 +81,11 @@
     suspend fun awaitPointerEvent(
         pass: PointerEventPass = PointerEventPass.Main
     ): PointerEvent
-
-    /**
-     * Suspend until a [CustomEvent] is reported to the specified input [pass].
-     * [pass] defaults to [PointerEventPass.Main].
-     *
-     * [awaitCustomEvent] resumes **synchronously** in the restricted suspension scope. This
-     * means that callers can react immediately to input after [awaitCustomEvent] returns
-     * and affect both the current frame and the next handler or phase of the input processing
-     * pipeline. Callers should mutate the returned [CustomEvent] before awaiting
-     * another event to consume aspects of the event before the next stage of input processing runs.     */
-    suspend fun awaitCustomEvent(
-        pass: PointerEventPass = PointerEventPass.Main
-    ): CustomEvent
 }
 
 /**
  * Receiver scope for [Modifier.pointerInput] that permits
- * [handling pointer input][handlePointerInput] and
- * [sending custom input events][customEventDispatcher].
+ * [handling pointer input][awaitPointerEventScope].
  */
 // Design note: this interface does _not_ implement CoroutineScope, even though doing so
 // would more easily permit the use of launch {} inside Modifier.pointerInput {} blocks without
@@ -106,7 +93,6 @@
 // gesture detectors as suspending extensions with a PointerInputScope receiver, also making this
 // interface implement CoroutineScope would be an invitation to break structured concurrency in
 // these extensions, leaving other launched coroutines running in the calling scope.
-@ExperimentalPointerInput
 interface PointerInputScope : Density {
     /**
      * The measured size of the pointer input region. Input events will be reported with
@@ -116,40 +102,37 @@
     val size: IntSize
 
     /**
-     * [customEventDispatcher] permits dispatching custom input events to the rest of the UI
-     * in response to handling lower-level pointer input events. Accessing [customEventDispatcher]
-     * before the first pointer input event is reported will throw [IllegalStateException].
-     */
-    val customEventDispatcher: CustomEventDispatcher
-
-    /**
      * The [ViewConfiguration] used to tune gesture detectors.
      */
     val viewConfiguration: ViewConfiguration
 
     /**
-     * Suspend and install a pointer input [handler] that can await input events and respond to
-     * them immediately. A call to [handlePointerInput] will resume with [handler]'s result after
+     * Suspend and install a pointer input [block] that can await input events and respond to
+     * them immediately. A call to [awaitPointerEventScope] will resume with [block]'s result after
      * it completes.
      *
-     * More than one [handlePointerInput] can run concurrently in the same [PointerInputScope] by
-     * using [kotlinx.coroutines.launch]. Handlers are dispatched to in the order in which they
+     * More than one [awaitPointerEventScope] can run concurrently in the same [PointerInputScope] by
+     * using [kotlinx.coroutines.launch]. [block]s are dispatched to in the order in which they
      * were installed.
      */
-    suspend fun <R> handlePointerInput(
-        handler: suspend HandlePointerInputScope.() -> R
+    suspend fun <R> awaitPointerEventScope(
+        block: suspend AwaitPointerEventScope.() -> R
     ): R
+
+    @Deprecated("Use awaitPointerEventScope", ReplaceWith("awaitPointerEventScope(handler)"))
+    suspend fun <R> handlePointerInput(
+        handler: suspend AwaitPointerEventScope.() -> R
+    ): R = awaitPointerEventScope(handler)
 }
 
 /**
  * Create a modifier for processing pointer input within the region of the modified element.
  *
- * [pointerInput] [block]s may call [PointerInputScope.handlePointerInput] to install a pointer
- * input handler that can [HandlePointerInputScope.awaitPointerEvent] to receive and consume
- * pointer input events. Extension functions on [PointerInputScope] or [HandlePointerInputScope]
+ * [pointerInput] [block]s may call [PointerInputScope.awaitPointerEventScope] to install a pointer
+ * input handler that can [AwaitPointerEventScope.awaitPointerEvent] to receive and consume
+ * pointer input events. Extension functions on [PointerInputScope] or [AwaitPointerEventScope]
  * may be defined to perform higher-level gesture detection.
  */
-@ExperimentalPointerInput
 fun Modifier.pointerInput(
     block: suspend PointerInputScope.() -> Unit
 ): Modifier = composed(
@@ -184,7 +167,6 @@
  */
 // TODO: Suppressing deprecation for synchronized; need to move to atomicfu wrapper
 @Suppress("DEPRECATION_ERROR")
-@ExperimentalPointerInput
 internal class SuspendingPointerInputFilter(
     override val viewConfiguration: ViewConfiguration,
     density: Density = Density(1f)
@@ -196,25 +178,13 @@
     override val pointerInputFilter: PointerInputFilter
         get() = this
 
-    private var _customEventDispatcher: CustomEventDispatcher? = null
-
     private var currentEvent: PointerEvent? = null
 
-    /**
-     * TODO: work out whether this is actually a race or not.
-     * It shouldn't be, as we will have attached the [PointerInputModifier] during
-     * composition-apply by the time the [LaunchedEffect] that would access this property
-     * is dispatched and begins running.
-     */
-    override val customEventDispatcher: CustomEventDispatcher
-        get() = _customEventDispatcher ?: error("customEventDispatcher not yet available")
-
     override fun onInit(customEventDispatcher: CustomEventDispatcher) {
-        _customEventDispatcher = customEventDispatcher
     }
 
     /**
-     * Actively registered input handlers from currently ongoing calls to [handlePointerInput].
+     * Actively registered input handlers from currently ongoing calls to [awaitPointerEventScope].
      * Must use `synchronized(pointerHandlers)` to access.
      */
     private val pointerHandlers = mutableVectorOf<PointerEventHandlerCoroutine<*>>()
@@ -328,13 +298,10 @@
     }
 
     override fun onCustomEvent(customEvent: CustomEvent, pass: PointerEventPass) {
-        forEachCurrentPointerHandler(pass) {
-            it.offerCustomEvent(customEvent, pass)
-        }
     }
 
-    override suspend fun <R> handlePointerInput(
-        handler: suspend HandlePointerInputScope.() -> R
+    override suspend fun <R> awaitPointerEventScope(
+        block: suspend AwaitPointerEventScope.() -> R
     ): R = suspendCancellableCoroutine { continuation ->
         val handlerCoroutine = PointerEventHandlerCoroutine(continuation)
         synchronized(pointerHandlers) {
@@ -353,7 +320,7 @@
             // behavior in our restricted suspension scope. This is required so that we can
             // process event-awaits synchronously and affect the next stage in the pipeline
             // without running too late due to dispatch.
-            handler.createCoroutine(handlerCoroutine, handlerCoroutine).resume(Unit)
+            block.createCoroutine(handlerCoroutine, handlerCoroutine).resume(Unit)
         }
 
         // Restricted suspension handler coroutines can't propagate structured job cancellation
@@ -363,17 +330,16 @@
 
     /**
      * Implementation of the inner coroutine created to run a single call to
-     * [handlePointerInput].
+     * [awaitPointerEventScope].
      *
-     * [PointerEventHandlerCoroutine] implements [HandlePointerInputScope] to provide the
+     * [PointerEventHandlerCoroutine] implements [AwaitPointerEventScope] to provide the
      * input handler DSL, and [Continuation] so that it can wrap [completion] and remove the
      * [ContinuationInterceptor] from the calling context and run undispatched.
      */
     private inner class PointerEventHandlerCoroutine<R>(
         private val completion: Continuation<R>,
-    ) : HandlePointerInputScope, Density by this@SuspendingPointerInputFilter, Continuation<R> {
+    ) : AwaitPointerEventScope, Density by this@SuspendingPointerInputFilter, Continuation<R> {
         private var pointerAwaiter: CancellableContinuation<PointerEvent>? = null
-        private var customAwaiter: CancellableContinuation<CustomEvent>? = null
         private var awaitPass: PointerEventPass = PointerEventPass.Main
 
         override val currentEvent: PointerEvent
@@ -394,21 +360,10 @@
             }
         }
 
-        fun offerCustomEvent(event: CustomEvent, pass: PointerEventPass) {
-            if (pass == awaitPass) {
-                customAwaiter?.run {
-                    customAwaiter = null
-                    resume(event)
-                }
-            }
-        }
-
-        // Called to run any finally blocks in the handlePointerInput block
+        // Called to run any finally blocks in the awaitPointerEventScope block
         fun cancel(cause: Throwable?) {
             pointerAwaiter?.cancel(cause)
             pointerAwaiter = null
-            customAwaiter?.cancel(cause)
-            customAwaiter = null
         }
 
         // context must be EmptyCoroutineContext for restricted suspension coroutines
@@ -428,12 +383,5 @@
             awaitPass = pass
             pointerAwaiter = continuation
         }
-
-        override suspend fun awaitCustomEvent(
-            pass: PointerEventPass
-        ): CustomEvent = suspendCancellableCoroutine { continuation ->
-            awaitPass = pass
-            customAwaiter = continuation
-        }
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutInfo.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutInfo.kt
new file mode 100644
index 0000000..14f8ce2
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutInfo.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.compose.ui.layout
+
+import androidx.compose.ui.Modifier
+
+/**
+ * The public information about the layouts used internally as nodes in the Compose UI hierarchy.
+ */
+interface LayoutInfo {
+
+    /**
+     * This returns a new List of [Modifier]s and the coordinates and any extra information
+     * that may be useful. This is used for tooling to retrieve layout modifier and layer
+     * information.
+     */
+    fun getModifierInfo(): List<ModifierInfo>
+
+    /**
+     * The measured width of this layout and all of its modifiers.
+     */
+    val width: Int
+
+    /**
+     * The measured height of this layout and all of its modifiers.
+     */
+    val height: Int
+
+    /**
+     * Coordinates of just the contents of the layout, after being affected by all modifiers.
+     */
+    val coordinates: LayoutCoordinates
+
+    /**
+     * Whether or not this layout and all of its parents have been placed in the hierarchy.
+     */
+    val isPlaced: Boolean
+
+    /**
+     * Parent of this layout.
+     */
+    val parentInfo: LayoutInfo?
+
+    /**
+     * Returns true if this layout is currently a part of the layout tree.
+     */
+    val isAttached: Boolean
+}
+
+/**
+ * Used by tooling to examine the modifiers on a [LayoutInfo].
+ */
+class ModifierInfo(
+    val modifier: Modifier,
+    val coordinates: LayoutCoordinates,
+    val extra: Any? = null
+)
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
index 256ac27eb..8928637 100644
--- 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
@@ -19,8 +19,9 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.isSpecified
+import androidx.compose.ui.util.format
 import androidx.compose.ui.util.packFloats
-import androidx.compose.ui.util.toStringAsFixed
 import androidx.compose.ui.util.unpackFloat1
 import androidx.compose.ui.util.unpackFloat2
 
@@ -95,8 +96,7 @@
     @Stable
     operator fun div(operand: Float) = ScaleFactor(scaleX / operand, scaleY / operand)
 
-    override fun toString() = "ScaleFactor(${scaleX.toStringAsFixed(1)}, " +
-        "${scaleY.toStringAsFixed(1)})"
+    override fun toString() = "ScaleFactor(${"%.1f".format(scaleX)}, ${"%.1f".format(scaleY)})"
 
     companion object {
 
@@ -111,6 +111,27 @@
 }
 
 /**
+ * `false` when this is [ScaleFactor.Unspecified].
+ */
+@Stable
+inline val ScaleFactor.isSpecified: Boolean
+    get() = packedValue != ScaleFactor.Unspecified.packedValue
+
+/**
+ * `true` when this is [ScaleFactor.Unspecified].
+ */
+@Stable
+inline val ScaleFactor.isUnspecified: Boolean
+    get() = packedValue == ScaleFactor.Unspecified.packedValue
+
+/**
+ * If this [ScaleFactor] [isSpecified] then this is returned, otherwise [block] is executed
+ * and its result is returned.
+ */
+inline fun ScaleFactor.takeOrElse(block: () -> ScaleFactor): ScaleFactor =
+    if (isSpecified) this else block()
+
+/**
  * Multiplication operator with [Size].
  *
  * Return a new [Size] with the width and height multiplied by the [ScaleFactor.scaleX] and
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
index 8acf50f..53805c9 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
@@ -33,13 +33,11 @@
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.node.LayoutNode.LayoutState
 import androidx.compose.ui.node.MeasureBlocks
-import androidx.compose.ui.node.isAttached
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.AmbientLayoutDirection
 import androidx.compose.ui.platform.subcomposeInto
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.util.fastForEach
 
 /**
  * Analogue of [Layout] which allows to subcompose the actual content during the measuring stage
@@ -79,8 +77,6 @@
             set(AmbientLayoutDirection.current, LayoutEmitHelper.setLayoutDirection)
         }
     )
-
-    state.subcomposeIfRemeasureNotScheduled()
 }
 
 /**
@@ -159,15 +155,6 @@
         return node.children
     }
 
-    fun subcomposeIfRemeasureNotScheduled() {
-        val root = root!!
-        if (root.layoutState != LayoutState.NeedsRemeasure && root.isAttached()) {
-            root.foldedChildren.fastForEach {
-                subcompose(it, nodeToNodeState.getValue(it))
-            }
-        }
-    }
-
     private fun subcompose(node: LayoutNode, nodeState: NodeState) {
         node.ignoreModelReads {
             val content = nodeState.content
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/TestModifierUpdater.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/TestModifierUpdater.kt
new file mode 100644
index 0000000..ce35d16
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/TestModifierUpdater.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.layout
+
+import androidx.compose.runtime.Applier
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.emit
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.LayoutEmitHelper
+import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.util.annotation.VisibleForTesting
+
+/** @hide */
+@Deprecated(
+    "It is a test API, do not use it in the real applications",
+    level = DeprecationLevel.ERROR
+)
+@VisibleForTesting
+class TestModifierUpdater internal constructor(private val node: LayoutNode) {
+    fun updateModifier(modifier: Modifier) {
+        node.modifier = modifier
+    }
+}
+
+/** @hide */
+@Deprecated(
+    "It is a test API, do not use it in the real applications",
+    level = DeprecationLevel.ERROR
+)
+@VisibleForTesting
+@Composable
+@Suppress("DEPRECATION_ERROR")
+fun TestModifierUpdaterLayout(onAttached: (TestModifierUpdater) -> Unit) {
+    val measureBlocks = MeasuringIntrinsicsMeasureBlocks { _, constraints ->
+        layout(constraints.maxWidth, constraints.maxHeight) {}
+    }
+    emit<LayoutNode, Applier<Any>>(
+        ctor = LayoutEmitHelper.constructor,
+        update = {
+            set(measureBlocks, LayoutEmitHelper.setMeasureBlocks)
+            set(Unit) { onAttached(TestModifierUpdater(this)) }
+        }
+    )
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
index 3289fab..764f286 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
@@ -17,8 +17,6 @@
 package androidx.compose.ui.node
 
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.GraphicsLayerScope
@@ -27,9 +25,9 @@
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.platform.nativeClass
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.util.nativeClass
 
 /**
  * [LayoutNodeWrapper] with default implementations for methods.
@@ -48,6 +46,10 @@
      */
     var isChained = false
 
+    // This is used by LayoutNode to mark LayoutNodeWrappers that are going to be reused
+    // because they match the modifier instance.
+    var toBeReusedForSameModifier = false
+
     init {
         wrapped.wrappedBy = this
     }
@@ -132,10 +134,9 @@
         return lastFocusWrapper
     }
 
-    @OptIn(ExperimentalFocus::class)
-    override fun propagateFocusStateChange(focusState: FocusState) {
-        wrappedBy?.propagateFocusStateChange(focusState)
-    }
+    override fun findPreviousNestedScrollWrapper() = wrappedBy?.findPreviousNestedScrollWrapper()
+
+    override fun findNextNestedScrollWrapper() = wrapped.findNextNestedScrollWrapper()
 
     override fun findPreviousKeyInputWrapper() = wrappedBy?.findPreviousKeyInputWrapper()
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DepthSortedSet.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DepthSortedSet.kt
index 62e25e8..394e4eb 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DepthSortedSet.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DepthSortedSet.kt
@@ -16,9 +16,6 @@
 
 package androidx.compose.ui.node
 
-import androidx.compose.ui.util.TreeSet
-import androidx.compose.ui.util.identityHashCode
-
 /**
  * The set of [LayoutNode]s which orders items by their [LayoutNode.depth] and
  * allows modifications(additions and removals) while we iterate through it via [popEach].
@@ -44,10 +41,10 @@
             if (depthDiff != 0) {
                 return depthDiff
             }
-            return l1.identityHashCode().compareTo(l2.identityHashCode())
+            return l1.hashCode().compareTo(l2.hashCode())
         }
     }
-    private val set = TreeSet<LayoutNode>(DepthComparator)
+    private val set = TreeSet(DepthComparator)
 
     fun contains(node: LayoutNode): Boolean {
         val contains = set.contains(node)
@@ -58,7 +55,7 @@
     }
 
     fun add(node: LayoutNode) {
-        check(node.isAttached())
+        check(node.isAttached)
         if (extraAssertions) {
             val usedDepth = mapOfOriginalDepth[node]
             if (usedDepth == null) {
@@ -71,7 +68,7 @@
     }
 
     fun remove(node: LayoutNode) {
-        check(node.isAttached())
+        check(node.isAttached)
         val contains = set.remove(node)
         if (extraAssertions) {
             val usedDepth = mapOfOriginalDepth.remove(node)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
index f38542b..0bd0279 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
@@ -16,9 +16,8 @@
 
 package androidx.compose.ui.node
 
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollDelegatingWrapper
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.GraphicsLayerScope
@@ -59,13 +58,12 @@
 
     override fun findLastFocusWrapper(): ModifiedFocusNode? = findPreviousFocusWrapper()
 
-    @OptIn(ExperimentalFocus::class)
-    override fun propagateFocusStateChange(focusState: FocusState) {
-        wrappedBy?.propagateFocusStateChange(focusState)
-    }
-
     override fun findPreviousKeyInputWrapper() = wrappedBy?.findPreviousKeyInputWrapper()
 
+    override fun findPreviousNestedScrollWrapper() = wrappedBy?.findPreviousNestedScrollWrapper()
+
+    override fun findNextNestedScrollWrapper(): NestedScrollDelegatingWrapper? = null
+
     override fun findNextKeyInputWrapper(): ModifiedKeyInputNode? = null
 
     override fun findLastKeyInputWrapper(): ModifiedKeyInputNode? = findPreviousKeyInputWrapper()
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
index 107116f..2a65c2e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
@@ -17,13 +17,14 @@
 
 import androidx.compose.runtime.collection.MutableVector
 import androidx.compose.runtime.collection.mutableVectorOf
-import androidx.compose.ui.draw.DrawModifier
-import androidx.compose.ui.FocusModifier
-import androidx.compose.ui.FocusObserverModifier
-import androidx.compose.ui.FocusRequesterModifier
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.ExperimentalFocus
+import androidx.compose.ui.draw.DrawModifier
+import androidx.compose.ui.focus.FocusModifier
+import androidx.compose.ui.focus.FocusReferenceModifier
+import androidx.compose.ui.focus.FocusEventModifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollDelegatingWrapper
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollModifier
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.input.key.KeyInputModifier
 import androidx.compose.ui.input.pointer.PointerInputFilter
@@ -33,10 +34,12 @@
 import androidx.compose.ui.layout.IntrinsicMeasurable
 import androidx.compose.ui.layout.IntrinsicMeasureScope
 import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.LayoutInfo
 import androidx.compose.ui.layout.LayoutModifier
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.ModifierInfo
 import androidx.compose.ui.layout.OnGloballyPositionedModifier
 import androidx.compose.ui.layout.OnRemeasuredModifier
 import androidx.compose.ui.layout.ParentDataModifier
@@ -49,6 +52,7 @@
 import androidx.compose.ui.node.LayoutNode.LayoutState.NeedsRelayout
 import androidx.compose.ui.node.LayoutNode.LayoutState.NeedsRemeasure
 import androidx.compose.ui.node.LayoutNode.LayoutState.Ready
+import androidx.compose.ui.platform.nativeClass
 import androidx.compose.ui.platform.simpleIdentityToString
 import androidx.compose.ui.semantics.SemanticsModifier
 import androidx.compose.ui.semantics.SemanticsWrapper
@@ -58,7 +62,6 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.util.deleteAt
-import androidx.compose.ui.util.nativeClass
 import kotlin.math.roundToInt
 
 /**
@@ -73,10 +76,9 @@
 /**
  * An element in the layout hierarchy, built with compose UI.
  */
-@OptIn(ExperimentalFocus::class)
-class LayoutNode : Measurable, Remeasurement, OwnerScope {
+class LayoutNode : Measurable, Remeasurement, OwnerScope, LayoutInfo {
 
-    constructor() : this(false)
+    internal constructor() : this(false)
 
     internal constructor(isVirtual: Boolean) {
         this.isVirtual = isVirtual
@@ -139,13 +141,13 @@
     /**
      * The children of this LayoutNode, controlled by [insertAt], [move], and [removeAt].
      */
-    val children: List<LayoutNode> get() = _children.asMutableList()
+    internal val children: List<LayoutNode> get() = _children.asMutableList()
 
     /**
      * The parent node in the LayoutNode hierarchy. This is `null` when the [LayoutNode]
      * is not attached attached to a hierarchy or is the root of the hierarchy.
      */
-    var parent: LayoutNode? = null
+    internal var parent: LayoutNode? = null
         get() {
             val parent = field
             return if (parent != null && parent.isVirtual) parent.parent else parent
@@ -155,13 +157,19 @@
     /**
      * The view system [Owner]. This `null` until [attach] is called
      */
-    var owner: Owner? = null
+    internal var owner: Owner? = null
         private set
 
     /**
+     * Returns true if this [LayoutNode] currently has an [LayoutNode.owner].  Semantically,
+     * this means that the LayoutNode is currently a part of a component tree.
+     */
+    override val isAttached: Boolean get() = owner != null
+
+    /**
      * The tree depth of the [LayoutNode]. This is valid only when it is attached to a hierarchy.
      */
-    var depth: Int = 0
+    internal var depth: Int = 0
 
     /**
      * The layout state the node is currently in.
@@ -290,7 +298,7 @@
      * Set the [Owner] of this LayoutNode. This LayoutNode must not already be attached.
      * [owner] must match its [parent].[owner].
      */
-    fun attach(owner: Owner) {
+    internal fun attach(owner: Owner) {
         check(this.owner == null) {
             "Cannot attach $this as it already is attached"
         }
@@ -318,7 +326,6 @@
         parent?.requestRemeasure()
         innerLayoutNodeWrapper.attach()
         forEachDelegate { it.attach() }
-        updateInnerLayerWrapper()
         onAttach?.invoke(owner)
     }
 
@@ -327,7 +334,7 @@
      * and its [parent]'s [owner] must be `null` before calling this. This will also [detach] all
      * children. After executing, the [owner] will be `null`.
      */
-    fun detach() {
+    internal fun detach() {
         val owner = owner
         checkNotNull(owner) {
             "Cannot detach node that is already detached!"
@@ -348,7 +355,6 @@
         }
         owner.onDetach(this)
         this.owner = null
-        _innerLayerWrapper = null
         depth = 0
         _foldedChildren.forEach { child ->
             child.detach()
@@ -380,7 +386,7 @@
         }
 
     override val isValid: Boolean
-        get() = isAttached()
+        get() = isAttached
 
     override fun toString(): String {
         return "${simpleIdentityToString(this, null)} children: ${children.size} " +
@@ -440,7 +446,7 @@
     /**
      * Blocks that define the measurement and intrinsic measurement of the layout.
      */
-    var measureBlocks: MeasureBlocks = ErrorMeasureBlocks
+    internal var measureBlocks: MeasureBlocks = ErrorMeasureBlocks
         set(value) {
             if (field != value) {
                 field = value
@@ -451,7 +457,7 @@
     /**
      * The screen density to be used by this layout.
      */
-    var density: Density = Density(1f)
+    internal var density: Density = Density(1f)
 
     /**
      * The scope used to run the [MeasureBlocks.measure]
@@ -466,7 +472,7 @@
     /**
      * The layout direction of the layout node.
      */
-    var layoutDirection: LayoutDirection = LayoutDirection.Ltr
+    internal var layoutDirection: LayoutDirection = LayoutDirection.Ltr
         set(value) {
             if (field != value) {
                 field = value
@@ -477,12 +483,12 @@
     /**
      * The measured width of this layout and all of its [modifier]s. Shortcut for `size.width`.
      */
-    val width: Int get() = outerMeasurablePlaceable.width
+    override val width: Int get() = outerMeasurablePlaceable.width
 
     /**
      * The measured height of this layout and all of its [modifier]s. Shortcut for `size.height`.
      */
-    val height: Int get() = outerMeasurablePlaceable.height
+    override val height: Int get() = outerMeasurablePlaceable.height
 
     /**
      * The alignment lines of this layout, inherited + intrinsic
@@ -499,7 +505,7 @@
     /**
      * Whether or not this [LayoutNode] and all of its parents have been placed in the hierarchy.
      */
-    var isPlaced: Boolean = false
+    override var isPlaced: Boolean = false
         private set
 
     /**
@@ -557,7 +563,7 @@
     private val previousAlignmentLines = mutableMapOf<AlignmentLine, Int>()
 
     @Deprecated("Temporary API to support ConstraintLayout prototyping.")
-    var canMultiMeasure: Boolean = false
+    internal var canMultiMeasure: Boolean = false
 
     internal val innerLayoutNodeWrapper: LayoutNodeWrapper = InnerPlaceable(this)
     private val outerMeasurablePlaceable = OuterMeasurablePlaceable(this, innerLayoutNodeWrapper)
@@ -576,7 +582,20 @@
      * The inner-most layer wrapper. Used for performance for LayoutNodeWrapper.findLayer().
      */
     private var _innerLayerWrapper: LayoutNodeWrapper? = null
+    internal var innerLayerWrapperIsDirty = true
     internal val innerLayerWrapper: LayoutNodeWrapper? get() {
+        if (innerLayerWrapperIsDirty) {
+            var delegate: LayoutNodeWrapper? = innerLayoutNodeWrapper
+            val final = outerLayoutNodeWrapper.wrappedBy
+            _innerLayerWrapper = null
+            while (delegate != final) {
+                if (delegate?.layer != null) {
+                    _innerLayerWrapper = delegate
+                    break
+                }
+                delegate = delegate?.wrappedBy
+            }
+        }
         val layerWrapper = _innerLayerWrapper
         if (layerWrapper != null) {
             requireNotNull(layerWrapper.layer)
@@ -602,7 +621,7 @@
     /**
      * The [Modifier] currently applied to this node.
      */
-    var modifier: Modifier = Modifier
+    internal var modifier: Modifier = Modifier
         set(value) {
             if (value == field) return
             if (modifier != Modifier) {
@@ -613,10 +632,11 @@
             val invalidateParentLayer = shouldInvalidateParentLayer()
 
             copyWrappersToCache()
+            markReusedModifiers(value)
 
             // Rebuild LayoutNodeWrapper
             val oldOuterWrapper = outerMeasurablePlaceable.outerWrapper
-            if (outerSemantics != null && isAttached()) {
+            if (outerSemantics != null && isAttached) {
                 owner!!.onSemanticsChange()
             }
             val addedCallback = hasNewPositioningCallback()
@@ -651,11 +671,11 @@
                     if (mod is FocusModifier) {
                         wrapper = ModifiedFocusNode(wrapper, mod).assignChained(toWrap)
                     }
-                    if (mod is FocusObserverModifier) {
-                        wrapper = ModifiedFocusObserverNode(wrapper, mod).assignChained(toWrap)
+                    if (mod is FocusEventModifier) {
+                        wrapper = ModifiedFocusEventNode(wrapper, mod).assignChained(toWrap)
                     }
-                    if (mod is FocusRequesterModifier) {
-                        wrapper = ModifiedFocusRequesterNode(wrapper, mod).assignChained(toWrap)
+                    if (mod is FocusReferenceModifier) {
+                        wrapper = ModifiedFocusReferenceNode(wrapper, mod).assignChained(toWrap)
                     }
                     if (mod is KeyInputModifier) {
                         wrapper = ModifiedKeyInputNode(wrapper, mod).assignChained(toWrap)
@@ -663,6 +683,9 @@
                     if (mod is PointerInputModifier) {
                         wrapper = PointerInputDelegatingWrapper(wrapper, mod).assignChained(toWrap)
                     }
+                    if (mod is NestedScrollModifier) {
+                        wrapper = NestedScrollDelegatingWrapper(wrapper, mod).assignChained(toWrap)
+                    }
                     if (mod is LayoutModifier) {
                         wrapper = ModifiedLayoutNode(wrapper, mod).assignChained(toWrap)
                     }
@@ -679,13 +702,10 @@
             outerWrapper.wrappedBy = parent?.innerLayoutNodeWrapper
             outerMeasurablePlaceable.outerWrapper = outerWrapper
 
-            if (isAttached()) {
+            if (isAttached) {
                 // call detach() on all removed LayoutNodeWrappers
                 wrapperCache.forEach {
                     it.detach()
-                    if (_innerLayerWrapper === it) {
-                        _innerLayerWrapper = null
-                    }
                 }
 
                 // attach() all new LayoutNodeWrappers
@@ -727,7 +747,7 @@
     /**
      * Coordinates of just the contents of the [LayoutNode], after being affected by all modifiers.
      */
-    val coordinates: LayoutCoordinates
+    override val coordinates: LayoutCoordinates
         get() = innerLayoutNodeWrapper
 
     /**
@@ -757,7 +777,7 @@
      */
     internal var needsOnPositionedDispatch = false
 
-    fun place(x: Int, y: Int) {
+    internal fun place(x: Int, y: Int) {
         Placeable.PlacementScope.executeWithRtlMirroringValues(
             outerMeasurablePlaceable.measuredWidth,
             layoutDirection
@@ -826,29 +846,11 @@
     }
 
     /**
-     * Find the current inner layer.
-     */
-    private fun updateInnerLayerWrapper() {
-        var delegate: LayoutNodeWrapper? = innerLayoutNodeWrapper
-        val final = outerLayoutNodeWrapper.wrappedBy
-        _innerLayerWrapper = null
-        while (delegate != final) {
-            if (delegate?.layer != null) {
-                _innerLayerWrapper = delegate
-                break
-            }
-            delegate = delegate?.wrappedBy
-        }
-    }
-
-    /**
      * Invoked when the parent placed the node. It will trigger the layout.
      */
     internal fun onNodePlaced() {
         val parent = parent
 
-        updateInnerLayerWrapper()
-
         var newZIndex = innerLayoutNodeWrapper.zIndex
         forEachDelegate {
             newZIndex += it.zIndex
@@ -1113,7 +1115,7 @@
      * that may be useful. This is used for tooling to retrieve layout modifier and layer
      * information.
      */
-    fun getModifierInfo(): List<ModifierInfo> {
+    override fun getModifierInfo(): List<ModifierInfo> {
         val infoList = mutableVectorOf<ModifierInfo>()
         forEachDelegate { wrapper ->
             wrapper as DelegatingLayoutNodeWrapper<*>
@@ -1146,8 +1148,16 @@
         if (wrapperCache.isEmpty()) {
             return null
         }
-        val index = wrapperCache.indexOfLast {
-            it.modifier === modifier || it.modifier.nativeClass() == modifier.nativeClass()
+        // Look for exact match
+        var index = wrapperCache.indexOfLast {
+            it.toBeReusedForSameModifier && it.modifier === modifier
+        }
+
+        if (index < 0) {
+            // Look for class match
+            index = wrapperCache.indexOfLast {
+                !it.toBeReusedForSameModifier && it.modifier.nativeClass() == modifier.nativeClass()
+            }
         }
 
         if (index < 0) {
@@ -1182,6 +1192,17 @@
         }
     }
 
+    private fun markReusedModifiers(modifier: Modifier) {
+        wrapperCache.forEach {
+            it.toBeReusedForSameModifier = false
+        }
+
+        modifier.foldIn(Unit) { _, mod ->
+            val wrapper = wrapperCache.firstOrNull { it.modifier === mod }
+            wrapper?.toBeReusedForSameModifier = true
+        }
+    }
+
     // Delegation from Measurable to measurableAndPlaceable
     override fun measure(constraints: Constraints) =
         outerMeasurablePlaceable.measure(constraints)
@@ -1237,17 +1258,14 @@
     }
 
     private fun shouldInvalidateParentLayer(): Boolean {
-        if (innerLayerWrapper == null) {
-            return true
-        }
         forEachDelegateIncludingInner {
-            if (it is ModifiedDrawNode) {
-                return true
-            } else if (it.layer != null) {
+            if (it.layer != null) {
                 return false
+            } else if (it is ModifiedDrawNode) {
+                return true
             }
         }
-        error("innerLayerWrapper should have been reached.")
+        return true
     }
 
     /**
@@ -1262,6 +1280,9 @@
         }
     }
 
+    override val parentInfo: LayoutInfo?
+        get() = parent
+
     internal companion object {
         private val ErrorMeasureBlocks: NoIntrinsicsMeasureBlocks =
             object : NoIntrinsicsMeasureBlocks(
@@ -1329,22 +1350,6 @@
 }
 
 /**
- * Returns true if this [LayoutNode] currently has an [LayoutNode.owner].  Semantically,
- * this means that the LayoutNode is currently a part of a component tree.
- */
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun LayoutNode.isAttached() = owner != null
-
-/**
- * Used by tooling to examine the modifiers on a [LayoutNode].
- */
-class ModifierInfo(
-    val modifier: Modifier,
-    val coordinates: LayoutCoordinates,
-    val extra: Any? = null
-)
-
-/**
  * Returns [LayoutNode.owner] or throws if it is null.
  */
 internal fun LayoutNode.requireOwner(): Owner {
@@ -1356,8 +1361,9 @@
 }
 
 /**
- * Inserts a child [LayoutNode] at a last index. If this LayoutNode [isAttached]
- * then [child] will become [isAttached]ed also. [child] must have a `null` [LayoutNode.parent].
+ * Inserts a child [LayoutNode] at a last index. If this LayoutNode [LayoutNode.isAttached]
+ * then [child] will become [LayoutNode.isAttached] also. [child] must have a `null`
+ * [LayoutNode.parent].
  */
 internal fun LayoutNode.add(child: LayoutNode) {
     insertAt(children.size, child)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
index 6ad3889..5ea7a3a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
@@ -18,12 +18,12 @@
 
 package androidx.compose.ui.node
 
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.geometry.MutableRect
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.toRect
+import androidx.compose.ui.gesture.nestedscroll.NestedScrollDelegatingWrapper
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.GraphicsLayerScope
 import androidx.compose.ui.graphics.Matrix
@@ -65,13 +65,14 @@
 
     private var isClipping: Boolean = false
 
-    private var layerBlock: (GraphicsLayerScope.() -> Unit)? = null
+    protected var layerBlock: (GraphicsLayerScope.() -> Unit)? = null
+        private set
 
     private var _isAttached = false
     override val isAttached: Boolean
         get() {
             if (_isAttached) {
-                require(layoutNode.isAttached())
+                require(layoutNode.isAttached)
             }
             return _isAttached
         }
@@ -87,6 +88,7 @@
                 } else {
                     wrappedBy?.invalidateLayer()
                 }
+                layoutNode.owner?.onLayoutChange(layoutNode)
             }
             _measureResult = value
             measuredSize = IntSize(measureResult.width, measureResult.height)
@@ -109,9 +111,10 @@
     var isShallowPlacing = false
 
     private var _rectCache: MutableRect? = null
-    private val rectCache: MutableRect get() = _rectCache ?: MutableRect(0f, 0f, 0f, 0f).also {
-        _rectCache = it
-    }
+    private val rectCache: MutableRect
+        get() = _rectCache ?: MutableRect(0f, 0f, 0f, 0f).also {
+            _rectCache = it
+        }
 
     private val snapshotObserver get() = layoutNode.requireOwner().snapshotObserver
 
@@ -158,9 +161,7 @@
         zIndex: Float,
         layerBlock: (GraphicsLayerScope.() -> Unit)?
     ) {
-        if (wrappedBy?.isShallowPlacing != true) {
-            onLayerBlockUpdated(layerBlock)
-        }
+        onLayerBlockUpdated(layerBlock)
         if (this.position != position) {
             this.position = position
             val layer = layer
@@ -169,6 +170,7 @@
             } else {
                 wrappedBy?.invalidateLayer()
             }
+            layoutNode.owner?.onLayoutChange(layoutNode)
         }
         this.zIndex = zIndex
     }
@@ -222,6 +224,7 @@
                     move(position)
                 }
                 updateLayerParameters()
+                layoutNode.innerLayerWrapperIsDirty = true
                 invalidateParentLayer()
             } else if (blockHasBeenChanged) {
                 updateLayerParameters()
@@ -229,7 +232,7 @@
         } else {
             layer?.let {
                 it.destroy()
-
+                layoutNode.innerLayerWrapperIsDirty = true
                 invalidateParentLayer()
             }
             layer = null
@@ -499,14 +502,40 @@
     }
 
     /**
+     * Returns the first [NestedScrollDelegatingWrapper] in the wrapper list that wraps this
+     * [LayoutNodeWrapper].
+     *
+     * Note: This method tried to find [NestedScrollDelegatingWrapper] in the
+     * modifiers before the one wrapped with this [LayoutNodeWrapper] and goes up the hierarchy of
+     * [LayoutNode]s if needed.
+     */
+    abstract fun findPreviousNestedScrollWrapper(): NestedScrollDelegatingWrapper?
+
+    /**
+     * Returns the first [NestedScrollDelegatingWrapper] in the wrapper list that is wrapped by this
+     * [LayoutNodeWrapper].
+     *
+     * Note: This method only goes to the modifiers that follow the one wrapped by
+     * this [LayoutNodeWrapper], it doesn't to the children [LayoutNode]s.
+     */
+    abstract fun findNextNestedScrollWrapper(): NestedScrollDelegatingWrapper?
+
+    /**
      * Returns the first [focus node][ModifiedFocusNode] in the wrapper list that wraps this
      * [LayoutNodeWrapper].
+     *
+     * Note: This method tried to find [NestedScrollDelegatingWrapper] in the
+     * modifiers before the one wrapped with this [LayoutNodeWrapper] and goes up the hierarchy of
+     * [LayoutNode]s if needed.
      */
     abstract fun findPreviousFocusWrapper(): ModifiedFocusNode?
 
     /**
      * Returns the next [focus node][ModifiedFocusNode] in the wrapper list that is wrapped by
      * this [LayoutNodeWrapper].
+     *
+     * Note: This method only goes to the modifiers that follow the one wrapped by
+     * this [LayoutNodeWrapper], it doesn't to the children [LayoutNode]s.
      */
     abstract fun findNextFocusWrapper(): ModifiedFocusNode?
 
@@ -521,8 +550,9 @@
      * that wraps it. The focus state change must be propagated to the parents until we reach
      * another [focus node][ModifiedFocusNode].
      */
-    @OptIn(ExperimentalFocus::class)
-    abstract fun propagateFocusStateChange(focusState: FocusState)
+    open fun propagateFocusEvent(focusState: FocusState) {
+        wrappedBy?.propagateFocusEvent(focusState)
+    }
 
     /**
      * Find the first ancestor that is a [ModifiedFocusNode].
@@ -573,12 +603,19 @@
     /**
      * Returns the first [ModifiedKeyInputNode] in the wrapper list that wraps this
      * [LayoutNodeWrapper].
+     *
+     * Note: This method tried to find [NestedScrollDelegatingWrapper] in the
+     * modifiers before the one wrapped with this [LayoutNodeWrapper] and goes up the hierarchy of
+     * [LayoutNode]s if needed.
      */
     abstract fun findPreviousKeyInputWrapper(): ModifiedKeyInputNode?
 
     /**
      * Returns the next [ModifiedKeyInputNode] in the wrapper list that is wrapped by this
      * [LayoutNodeWrapper].
+     *
+     * Note: This method only goes to the modifiers that follow the one wrapped by
+     * this [LayoutNodeWrapper], it doesn't to the children [LayoutNode]s.
      */
     abstract fun findNextKeyInputWrapper(): ModifiedKeyInputNode?
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
index 126c75e..8024fb6 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
@@ -189,7 +189,7 @@
      * Iterates through all LayoutNodes that have requested layout and measures and lays them out
      */
     fun measureAndLayout(): Boolean {
-        require(root.isAttached())
+        require(root.isAttached)
         require(root.isPlaced)
         require(!duringMeasureLayout)
         // we don't need to measure any children unless we have the correct root constraints
@@ -225,7 +225,7 @@
                     // execute postponed `onRequestMeasure`
                     if (postponedMeasureRequests.isNotEmpty()) {
                         postponedMeasureRequests.fastForEach {
-                            if (it.isAttached()) {
+                            if (it.isAttached) {
                                 requestRemeasure(it)
                             }
                         }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusObserverNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusEventNode.kt
similarity index 72%
rename from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusObserverNode.kt
rename to compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusEventNode.kt
index 2aa55e8..131b6ff 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusObserverNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusEventNode.kt
@@ -16,21 +16,19 @@
 
 package androidx.compose.ui.node
 
-import androidx.compose.ui.FocusObserverModifier
-import androidx.compose.ui.focus.ExperimentalFocus
+import androidx.compose.ui.focus.FocusEventModifier
 import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.focus.FocusState.Inactive
 import androidx.compose.ui.focus.searchChildrenForFocusNode
 
-@OptIn(ExperimentalFocus::class)
-internal class ModifiedFocusObserverNode(
+internal class ModifiedFocusEventNode(
     wrapped: LayoutNodeWrapper,
-    modifier: FocusObserverModifier
-) : DelegatingLayoutNodeWrapper<FocusObserverModifier>(wrapped, modifier) {
+    modifier: FocusEventModifier
+) : DelegatingLayoutNodeWrapper<FocusEventModifier>(wrapped, modifier) {
 
-    override fun propagateFocusStateChange(focusState: FocusState) {
-        modifier.onFocusChange(focusState)
-        super.propagateFocusStateChange(focusState)
+    override fun propagateFocusEvent(focusState: FocusState) {
+        modifier.onFocusEvent(focusState)
+        super.propagateFocusEvent(focusState)
     }
 
     override fun onModifierChanged() {
@@ -40,6 +38,6 @@
         // modifier following this observer, it's focus state will be invalid. To solve this, we
         // always reset the focus state when a focus observer is re-used.
         val focusNode = wrapped.findNextFocusWrapper() ?: layoutNode.searchChildrenForFocusNode()
-        modifier.onFocusChange(focusNode?.modifier?.focusState ?: Inactive)
+        modifier.onFocusEvent(focusNode?.modifier?.focusState ?: Inactive)
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusNode.kt
index 7da03ef..886237c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusNode.kt
@@ -16,8 +16,7 @@
 
 package androidx.compose.ui.node
 
-import androidx.compose.ui.FocusModifier
-import androidx.compose.ui.focus.ExperimentalFocus
+import androidx.compose.ui.focus.FocusModifier
 import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.ActiveParent
@@ -28,9 +27,6 @@
 import androidx.compose.ui.focus.searchChildrenForFocusNode
 import androidx.compose.ui.util.fastForEach
 
-@OptIn(
-    ExperimentalFocus::class,
-)
 internal class ModifiedFocusNode(
     wrapped: LayoutNodeWrapper,
     modifier: FocusModifier
@@ -51,7 +47,7 @@
      */
     fun requestFocus(propagateFocus: Boolean = true) {
         when (modifier.focusState) {
-            Active, Captured, Disabled -> wrappedBy?.propagateFocusStateChange(modifier.focusState)
+            Active, Captured, Disabled -> wrappedBy?.propagateFocusEvent(modifier.focusState)
             ActiveParent -> {
                 val focusedChild = modifier.focusedChild
                 requireNotNull(focusedChild)
@@ -265,12 +261,12 @@
 
     override fun onModifierChanged() {
         super.onModifierChanged()
-        wrappedBy?.propagateFocusStateChange(modifier.focusState)
+        wrappedBy?.propagateFocusEvent(modifier.focusState)
     }
 
     override fun attach() {
         super.attach()
-        wrappedBy?.propagateFocusStateChange(modifier.focusState)
+        wrappedBy?.propagateFocusEvent(modifier.focusState)
     }
 
     override fun detach() {
@@ -286,9 +282,9 @@
                     ?: layoutNode.searchChildrenForFocusNode()
                 if (nextFocusNode != null) {
                     findParentFocusNode()?.modifier?.focusedChild = nextFocusNode
-                    wrappedBy?.propagateFocusStateChange(nextFocusNode.modifier.focusState)
+                    wrappedBy?.propagateFocusEvent(nextFocusNode.modifier.focusState)
                 } else {
-                    wrappedBy?.propagateFocusStateChange(Inactive)
+                    wrappedBy?.propagateFocusEvent(Inactive)
                 }
             }
             // TODO(b/155212782): Implement this after adding support for disabling focus modifiers.
@@ -304,7 +300,7 @@
 
     override fun findNextFocusWrapper() = this
 
-    override fun propagateFocusStateChange(focusState: FocusState) {
+    override fun propagateFocusEvent(focusState: FocusState) {
         // Do nothing. Stop propagating the focus change (since we hit another focus node).
     }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusRequesterNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusReferenceNode.kt
similarity index 66%
rename from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusRequesterNode.kt
rename to compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusReferenceNode.kt
index 03f8140..8a29f84 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusRequesterNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusReferenceNode.kt
@@ -16,25 +16,21 @@
 
 package androidx.compose.ui.node
 
-import androidx.compose.ui.FocusRequesterModifier
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.FocusReferenceModifier
 import androidx.compose.ui.focus.searchChildrenForFocusNode
 
-@OptIn(
-    ExperimentalFocus::class
-)
-internal class ModifiedFocusRequesterNode(
+internal class ModifiedFocusReferenceNode(
     wrapped: LayoutNodeWrapper,
-    modifier: FocusRequesterModifier
-) : DelegatingLayoutNodeWrapper<FocusRequesterModifier>(wrapped, modifier) {
+    modifier: FocusReferenceModifier
+) : DelegatingLayoutNodeWrapper<FocusReferenceModifier>(wrapped, modifier) {
 
-    private var focusRequester: FocusRequester? = null
+    private var focusReference: FocusReference? = null
         set(value) {
-            // Check if this focus requester node is associated with another focusRequester.
-            field?.focusRequesterNodes?.remove(this)
+            // Check if this focus requester node is associated with another focusReference.
+            field?.focusReferenceNodes?.remove(this)
             field = value
-            field?.focusRequesterNodes?.add(this)
+            field?.focusReferenceNodes?.add(this)
         }
 
     // Searches for the focus node associated with this focus requester node.
@@ -44,16 +40,16 @@
 
     override fun onModifierChanged() {
         super.onModifierChanged()
-        focusRequester = modifier.focusRequester
+        focusReference = modifier.focusReference
     }
 
     override fun attach() {
         super.attach()
-        focusRequester = modifier.focusRequester
+        focusReference = modifier.focusReference
     }
 
     override fun detach() {
-        focusRequester = null
+        focusReference = null
         super.detach()
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedKeyInputNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedKeyInputNode.kt
index 1c4856a..17b728f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedKeyInputNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedKeyInputNode.kt
@@ -16,11 +16,9 @@
 
 package androidx.compose.ui.node
 
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.input.key.KeyInputModifier
 
-@OptIn(ExperimentalKeyInput::class)
 internal class ModifiedKeyInputNode(wrapped: LayoutNodeWrapper, modifier: KeyInputModifier) :
     DelegatingLayoutNodeWrapper<KeyInputModifier>(wrapped, modifier) {
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt
index d01fe4c..8e952e0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt
@@ -66,7 +66,7 @@
         }
         // Place our wrapped to obtain their position inside ourselves.
         isShallowPlacing = true
-        placeAt(this.position, zIndex = 0f, null)
+        placeAt(this.position, this.zIndex, this.layerBlock)
         isShallowPlacing = false
         return if (line is HorizontalAlignmentLine) {
             positionInWrapped + wrapped.position.y
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OnPositionedDispatcher.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OnPositionedDispatcher.kt
index 867cd1c..d26b1f8 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OnPositionedDispatcher.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OnPositionedDispatcher.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.node
 
 import androidx.compose.runtime.collection.mutableVectorOf
-import androidx.compose.ui.util.identityHashCode
 
 /**
  * Tracks the nodes being positioned and dispatches OnPositioned callbacks when we finished
@@ -64,7 +63,7 @@
                 if (depthDiff != 0) {
                     return depthDiff
                 }
-                return a.identityHashCode().compareTo(b.identityHashCode())
+                return a.hashCode().compareTo(b.hashCode())
             }
         }
     }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
index b049c55..e13f229 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
@@ -15,13 +15,12 @@
  */
 package androidx.compose.ui.node
 
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.autofill.Autofill
 import androidx.compose.ui.autofill.AutofillTree
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.hapticfeedback.HapticFeedback
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.platform.ClipboardManager
 import androidx.compose.ui.platform.TextToolbar
@@ -67,11 +66,13 @@
      *  TODO(ralu): Replace with SemanticsTree. This is a temporary hack until we have a semantics
      *  tree implemented.
      */
+    @ExperimentalComposeUiApi
     val autofillTree: AutofillTree
 
     /**
      * The [Autofill] class can be used to perform autofill operations. It is used as an ambient.
      */
+    @ExperimentalComposeUiApi
     val autofill: Autofill?
 
     val density: Density
@@ -83,7 +84,6 @@
     /**
      * Provide a focus manager that controls focus within Compose.
      */
-    @ExperimentalFocus
     val focusManager: FocusManager
 
     /**
@@ -114,11 +114,6 @@
     fun onRequestRelayout(layoutNode: LayoutNode)
 
     /**
-     * Whether the Owner has pending layout work.
-     */
-    val hasPendingMeasureOrLayout: Boolean
-
-    /**
      * Called by [LayoutNode] when it is attached to the view system and now has an owner.
      * This is used by [Owner] to track which nodes are associated with it. It will only be
      * called when [node] is not already attached to an owner.
@@ -150,7 +145,6 @@
      *
      * @return true if the event was consumed. False otherwise.
      */
-    @ExperimentalKeyInput
     fun sendKeyEvent(keyEvent: KeyEvent): Boolean
 
     /**
@@ -170,6 +164,11 @@
      */
     fun onSemanticsChange()
 
+    /**
+     * The position and/or size of the [layoutNode] changed.
+     */
+    fun onLayoutChange(layoutNode: LayoutNode)
+
     val measureIteration: Long
 
     /**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/TreeSet.kt
similarity index 70%
copy from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
copy to compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/TreeSet.kt
index f9cb2fe..c717323 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/TreeSet.kt
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.focus
+package androidx.compose.ui.node
 
-@RequiresOptIn("The Focus API is experimental and is likely to change in the future.")
-annotation class ExperimentalFocus
\ No newline at end of file
+internal expect class TreeSet<E>(comparator: Comparator<in E>) {
+    fun add(element: E): Boolean
+    fun remove(element: E): Boolean
+    fun first(): E
+    fun contains(element: E): Boolean
+    fun isEmpty(): Boolean
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Ambients.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Ambients.kt
index bfbb003..c4f6b6d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Ambients.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Ambients.kt
@@ -20,9 +20,9 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.staticAmbientOf
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.autofill.Autofill
 import androidx.compose.ui.autofill.AutofillTree
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.hapticfeedback.HapticFeedback
 import androidx.compose.ui.node.Owner
@@ -52,19 +52,7 @@
 /**
  * The ambient that can be used to trigger autofill actions. Eg. [Autofill.requestAutofillForNode].
  */
-@Suppress("AmbientNaming")
-@Deprecated(
-    "Renamed to AmbientAutofill",
-    replaceWith = ReplaceWith(
-        "AmbientAutofill",
-        "androidx.compose.ui.platform.AmbientAutofill"
-    )
-)
-val AutofillAmbient get() = AmbientAutofill
-
-/**
- * The ambient that can be used to trigger autofill actions. Eg. [Autofill.requestAutofillForNode].
- */
+@ExperimentalComposeUiApi
 val AmbientAutofill = staticAmbientOf<Autofill?>()
 
 /**
@@ -73,22 +61,7 @@
  * [AutofillTree] is a temporary data structure that will be replaced by Autofill Semantics
  * (b/138604305).
  */
-@Suppress("AmbientNaming")
-@Deprecated(
-    "Renamed to AmbientAutofillTree",
-    replaceWith = ReplaceWith(
-        "AmbientAutofillTree",
-        "androidx.compose.ui.platform.AmbientAutofillTree"
-    )
-)
-val AutofillTreeAmbient get() = AmbientAutofillTree
-
-/**
- * The ambient that can be used to add
- * [AutofillNode][import androidx.compose.ui.autofill.AutofillNode]s to the autofill tree. The
- * [AutofillTree] is a temporary data structure that will be replaced by Autofill Semantics
- * (b/138604305).
- */
+@ExperimentalComposeUiApi
 val AmbientAutofillTree = staticAmbientOf<AutofillTree>()
 
 /**
@@ -146,13 +119,11 @@
         "androidx.compose.ui.platform.AmbientFocusManager"
     )
 )
-@ExperimentalFocus
 val FocusManagerAmbient get() = AmbientFocusManager
 
 /**
  * The ambient that can be used to control focus within Compose.
  */
-@ExperimentalFocus
 val AmbientFocusManager = staticAmbientOf<FocusManager>()
 
 /**
@@ -292,7 +263,7 @@
  */
 val AmbientWindowManager = staticAmbientOf<WindowManager>()
 
-@OptIn(ExperimentalFocus::class)
+@ExperimentalComposeUiApi
 @Composable
 internal fun ProvideCommonAmbients(
     owner: Owner,
diff --git a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/ClassHelpers.kt
similarity index 82%
copy from compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt
copy to compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/ClassHelpers.kt
index 3de9f0d..92b94de 100644
--- a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/ClassHelpers.kt
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.util
+package androidx.compose.ui.platform
 
-actual fun Float.toStringAsFixed(digits: Int): String = String.format("%.${digits}f", this)
+// For performance optimizations of type.
+internal expect fun Any.nativeClass(): Any
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Subcomposition.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Subcomposition.kt
index c741795..7e4506e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Subcomposition.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Subcomposition.kt
@@ -18,20 +18,12 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composition
 import androidx.compose.runtime.CompositionReference
-import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.util.annotation.MainThread
 
-internal expect fun actualSubcomposeInto(
-    container: LayoutNode,
-    parent: CompositionReference,
-    composable: @Composable () -> Unit
-): Composition
-
-@OptIn(ExperimentalComposeApi::class)
 @MainThread
-fun subcomposeInto(
+internal expect fun subcomposeInto(
     container: LayoutNode,
     parent: CompositionReference,
     composable: @Composable () -> Unit
-): Composition = actualSubcomposeInto(container, parent, composable)
\ No newline at end of file
+): Composition
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selectable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selectable.kt
index 26d168e..b98c168 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selectable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selectable.kt
@@ -20,11 +20,13 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
 
 /**
  * Provides [Selection] information for a composable to SelectionContainer. Composables who can
  * be selected should subscribe to [SelectionRegistrar] using this interface.
  */
+@ExperimentalTextApi
 interface Selectable {
     /**
      * Returns [Selection] information for a selectable composable. If no selection can be provided
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selection.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selection.kt
index 714ac17..bb80ac9 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selection.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/Selection.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.selection
 
 import androidx.compose.runtime.Immutable
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.style.ResolvedTextDirection
 
@@ -24,6 +25,7 @@
  * Information about the current Selection.
  */
 @Immutable
+@OptIn(ExperimentalTextApi::class)
 data class Selection(
     /**
      * Information about the start of the selection.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionContainer.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionContainer.kt
index 3e4b606..be22667 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionContainer.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionContainer.kt
@@ -135,17 +135,12 @@
                         handle = null
                     )
                 }
-                SelectionFloatingToolBar(manager = manager)
             }
         }
     }
 
     onDispose {
         manager.selection = null
+        manager.hideSelectionToolbar()
     }
 }
-
-@Composable
-private fun SelectionFloatingToolBar(manager: SelectionManager) {
-    manager.showSelectionToolbar()
-}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionManager.kt
index b6cbf8e..e4655c8 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionManager.kt
@@ -27,10 +27,12 @@
 import androidx.compose.ui.hapticfeedback.HapticFeedback
 import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.globalBounds
 import androidx.compose.ui.platform.ClipboardManager
 import androidx.compose.ui.platform.TextToolbar
 import androidx.compose.ui.platform.TextToolbarStatus
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.length
 import androidx.compose.ui.text.subSequence
@@ -40,7 +42,10 @@
 /**
  * A bridge class between user interaction to the text composables for text selection.
  */
-@OptIn(InternalTextApi::class)
+@OptIn(
+    InternalTextApi::class,
+    ExperimentalTextApi::class
+)
 internal class SelectionManager(private val selectionRegistrar: SelectionRegistrarImpl) {
     /**
      * The current selection.
@@ -48,8 +53,9 @@
     var selection: Selection? = null
         set(value) {
             field = value
-            updateHandleOffsets()
-            hideSelectionToolbar()
+            if (value != null) {
+                updateHandleOffsets()
+            }
         }
 
     /**
@@ -123,9 +129,20 @@
     init {
         selectionRegistrar.onPositionChangeCallback = {
             updateHandleOffsets()
+            updateSelectionToolbarPosition()
+        }
+
+        selectionRegistrar.onSelectionUpdateStartCallback = { layoutCoordinates, startPosition ->
+            updateSelection(
+                startPosition = convertToContainerCoordinates(layoutCoordinates, startPosition),
+                endPosition = convertToContainerCoordinates(layoutCoordinates, startPosition),
+                isStartHandle = true,
+                longPress = true
+            )
             hideSelectionToolbar()
         }
-        selectionRegistrar.onUpdateSelectionCallback =
+
+        selectionRegistrar.onSelectionUpdateCallback =
             { layoutCoordinates, startPosition, endPosition ->
                 updateSelection(
                     startPosition = convertToContainerCoordinates(layoutCoordinates, startPosition),
@@ -134,35 +151,58 @@
                     longPress = true
                 )
             }
+
+        selectionRegistrar.onSelectionUpdateEndCallback = {
+            showSelectionToolbar()
+        }
+
+        selectionRegistrar.onSelectableChangeCallback = { selectable ->
+            if (selectable in selectionRegistrar.selectables) {
+                // clear the selection range of each Selectable.
+                onRelease()
+                selection = null
+            }
+        }
     }
 
     private fun updateHandleOffsets() {
         val selection = selection
         val containerCoordinates = containerLayoutCoordinates
-        if (selection != null && containerCoordinates != null && containerCoordinates.isAttached) {
-            val startLayoutCoordinates = selection.start.selectable.getLayoutCoordinates()
-            val endLayoutCoordinates = selection.end.selectable.getLayoutCoordinates()
+        val startLayoutCoordinates = selection?.start?.selectable?.getLayoutCoordinates()
+        val endLayoutCoordinates = selection?.end?.selectable?.getLayoutCoordinates()
 
-            if (startLayoutCoordinates != null && endLayoutCoordinates != null) {
-                startHandlePosition = containerCoordinates.childToLocal(
-                    startLayoutCoordinates,
-                    selection.start.selectable.getHandlePosition(
-                        selection = selection,
-                        isStartHandle = true
-                    )
-                )
-                endHandlePosition = containerCoordinates.childToLocal(
-                    endLayoutCoordinates,
-                    selection.end.selectable.getHandlePosition(
-                        selection = selection,
-                        isStartHandle = false
-                    )
-                )
-                return
-            }
+        if (
+            selection == null ||
+            containerCoordinates == null ||
+            !containerCoordinates.isAttached ||
+            startLayoutCoordinates == null ||
+            endLayoutCoordinates == null
+        ) {
+            this.startHandlePosition = null
+            this.endHandlePosition = null
+            return
         }
-        startHandlePosition = null
-        endHandlePosition = null
+
+        val startHandlePosition = containerCoordinates.childToLocal(
+            startLayoutCoordinates,
+            selection.start.selectable.getHandlePosition(
+                selection = selection,
+                isStartHandle = true
+            )
+        )
+        val endHandlePosition = containerCoordinates.childToLocal(
+            endLayoutCoordinates,
+            selection.end.selectable.getHandlePosition(
+                selection = selection,
+                isStartHandle = false
+            )
+        )
+
+        val visibleBounds = containerCoordinates.visibleBounds()
+        this.startHandlePosition =
+            if (visibleBounds.containsInclusive(startHandlePosition)) startHandlePosition else null
+        this.endHandlePosition =
+            if (visibleBounds.containsInclusive(endHandlePosition)) endHandlePosition else null
     }
 
     /**
@@ -265,12 +305,9 @@
         }
     }
 
-    private fun hideSelectionToolbar() {
+    internal fun hideSelectionToolbar() {
         if (textToolbar?.status == TextToolbarStatus.Shown) {
-            val selection = selection
-            if (selection == null) {
-                textToolbar?.hide()
-            }
+            textToolbar?.hide()
         }
     }
 
@@ -356,12 +393,14 @@
             endPosition = Offset(-1f, -1f),
             previousSelection = selection
         )
+        hideSelectionToolbar()
         if (selection != null) onSelectionChange(null)
     }
 
     fun handleDragObserver(isStartHandle: Boolean): DragObserver {
         return object : DragObserver {
             override fun onStart(downPosition: Offset) {
+                hideSelectionToolbar()
                 val selection = selection!!
                 // The LayoutCoordinates of the composable where the drag gesture should begin. This
                 // is used to convert the position of the beginning of the drag gesture from the
@@ -375,13 +414,15 @@
                 // The position of the character where the drag gesture should begin. This is in
                 // the composable coordinates.
                 val beginCoordinates = getAdjustedCoordinates(
-                    if (isStartHandle)
+                    if (isStartHandle) {
                         selection.start.selectable.getHandlePosition(
                             selection = selection, isStartHandle = true
-                        ) else
+                        )
+                    } else {
                         selection.end.selectable.getHandlePosition(
                             selection = selection, isStartHandle = false
                         )
+                    }
                 )
 
                 // Convert the position where drag gesture begins from composable coordinates to
@@ -434,6 +475,14 @@
                 )
                 return dragDistance
             }
+
+            override fun onStop(velocity: Offset) {
+                showSelectionToolbar()
+            }
+
+            override fun onCancel() {
+                showSelectionToolbar()
+            }
         }
     }
 
@@ -468,6 +517,7 @@
     return lhs?.merge(rhs) ?: rhs
 }
 
+@OptIn(ExperimentalTextApi::class)
 internal fun getCurrentSelectedText(
     selectable: Selectable,
     selection: Selection
@@ -510,3 +560,17 @@
         }
     }
 }
+
+/** Returns the boundary of the visible area in this [LayoutCoordinates]. */
+private fun LayoutCoordinates.visibleBounds(): Rect {
+    // globalBounds is the global boundaries of this LayoutCoordinates after it's clipped by
+    // parents. We can think it as the global visible bounds of this Layout. Here globalBounds
+    // is convert to local, which is the boundary of the visible area within the LayoutCoordinates.
+    return Rect(
+        globalToLocal(globalBounds.topLeft),
+        globalToLocal(globalBounds.bottomRight)
+    )
+}
+
+private fun Rect.containsInclusive(offset: Offset): Boolean =
+    offset.x in left..right && offset.y in top..bottom
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrar.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrar.kt
index 3dbff76..963f684 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrar.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrar.kt
@@ -19,10 +19,12 @@
 import androidx.compose.runtime.ambientOf
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.ExperimentalTextApi
 
 /**
  *  An interface allowing a composable to subscribe and unsubscribe to selection changes.
  */
+@ExperimentalTextApi
 interface SelectionRegistrar {
     /**
      * Subscribe to SelectionContainer selection changes.
@@ -38,20 +40,66 @@
      * When the Global Position of a subscribed [Selectable] changes, this method
      * is called.
      */
-    fun onPositionChange()
+    fun notifyPositionChange()
 
     /**
-     * When selection changes, this method is called.
+     * Call this method to notify the [SelectionContainer] that the selection has been initiated.
+     * Depends on the input, [notifySelectionUpdate] may be called repeatedly after
+     * [notifySelectionUpdateStart] is called. And [notifySelectionUpdateEnd] should always be
+     * called after selection finished.
+     * For example:
+     *  1. User long pressed the text and then release. [notifySelectionUpdateStart] should be
+     *  called followed by [notifySelectionUpdateEnd] being called once.
+     *  2. User long pressed the text and then drag a distance and then release.
+     *  [notifySelectionUpdateStart] should be called first after the user long press, and then
+     *  [notifySelectionUpdate] is called several times reporting the updates, in the end
+     *  [notifySelectionUpdateEnd] is called to finish the selection.
+     *
+     * @param layoutCoordinates [LayoutCoordinates] of the [Selectable].
+     * @param startPosition coordinates of where the selection is initiated.
+     *
+     * @see notifySelectionUpdate
+     * @see notifySelectionUpdateEnd
+     */
+    fun notifySelectionUpdateStart(
+        layoutCoordinates: LayoutCoordinates,
+        startPosition: Offset
+    )
+
+    /**
+     * Call this method to notify the [SelectionContainer] that  the selection has been updated.
+     * The caller of this method should make sure that [notifySelectionUpdateStart] is always
+     * called once before calling this function. And [notifySelectionUpdateEnd] is always called
+     * once after the all updates finished.
      *
      * @param layoutCoordinates [LayoutCoordinates] of the [Selectable].
      * @param startPosition coordinates of where the selection starts.
      * @param endPosition coordinates of where the selection ends.
+     *
+     * @see notifySelectionUpdateStart
+     * @see notifySelectionUpdateEnd
      */
-    fun onUpdateSelection(
+    fun notifySelectionUpdate(
         layoutCoordinates: LayoutCoordinates,
         startPosition: Offset,
-        endPosition: Offset
+        endPosition: Offset,
     )
+
+    /**
+     * Call this method to notify the [SelectionContainer] that the selection update has stopped.
+     *
+     * @see notifySelectionUpdateStart
+     * @see notifySelectionUpdate
+     */
+    fun notifySelectionUpdateEnd()
+
+    /**
+     * Call this method to notify the [SelectionContainer] that the content of the passed
+     * selectable has been changed.
+     *
+     * @param selectable the selectable whose the content has been updated.
+     */
+    fun notifySelectableChange(selectable: Selectable)
 }
 
 /**
@@ -72,4 +120,5 @@
  * Ambient of SelectionRegistrar. Composables that implement selection logic can use this ambient
  * to get a [SelectionRegistrar] in order to subscribe and unsubscribe to [SelectionRegistrar].
  */
+@OptIn(ExperimentalTextApi::class)
 val AmbientSelectionRegistrar = ambientOf<SelectionRegistrar?>()
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrarImpl.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrarImpl.kt
index c42a8d1..809b977 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrarImpl.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionRegistrarImpl.kt
@@ -18,7 +18,9 @@
 
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.ExperimentalTextApi
 
+@OptIn(ExperimentalTextApi::class)
 internal class SelectionRegistrarImpl : SelectionRegistrar {
     /**
      * A flag to check if the [Selectable]s have already been sorted.
@@ -42,6 +44,26 @@
      */
     internal var onPositionChangeCallback: (() -> Unit)? = null
 
+    /**
+     * The callback to be invoked when the selection is initiated.
+     */
+    internal var onSelectionUpdateStartCallback: ((LayoutCoordinates, Offset) -> Unit)? = null
+
+    /**
+     * The callback to be invoked when the selection is updated.
+     */
+    internal var onSelectionUpdateCallback: ((LayoutCoordinates, Offset, Offset) -> Unit)? = null
+
+    /**
+     * The callback to be invoked when selection update finished.
+     */
+    internal var onSelectionUpdateEndCallback: (() -> Unit)? = null
+
+    /**
+     * The callback to be invoked when one of the selectable has changed.
+     */
+    internal var onSelectableChangeCallback: ((Selectable) -> Unit)? = null
+
     override fun subscribe(selectable: Selectable): Selectable {
         _selectables.add(selectable)
         sorted = false
@@ -87,27 +109,33 @@
         return selectables
     }
 
-    override fun onPositionChange() {
+    override fun notifyPositionChange() {
         // Set the variable sorted to be false, when the global position of a registered
         // selectable changes.
         sorted = false
         onPositionChangeCallback?.invoke()
     }
 
-    /**
-     * The callback to be invoked when the selection change was triggered.
-     */
-    internal var onUpdateSelectionCallback: ((LayoutCoordinates, Offset, Offset) -> Unit)? = null
+    override fun notifySelectionUpdateStart(
+        layoutCoordinates: LayoutCoordinates,
+        startPosition: Offset
+    ) {
+        onSelectionUpdateStartCallback?.invoke(layoutCoordinates, startPosition)
+    }
 
-    override fun onUpdateSelection(
+    override fun notifySelectionUpdate(
         layoutCoordinates: LayoutCoordinates,
         startPosition: Offset,
         endPosition: Offset
     ) {
-        onUpdateSelectionCallback?.invoke(
-            layoutCoordinates,
-            startPosition,
-            endPosition
-        )
+        onSelectionUpdateCallback?.invoke(layoutCoordinates, startPosition, endPosition)
+    }
+
+    override fun notifySelectionUpdateEnd() {
+        onSelectionUpdateEndCallback?.invoke()
+    }
+
+    override fun notifySelectableChange(selectable: Selectable) {
+        onSelectableChangeCallback?.invoke(selectable)
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/TextSelectionColors.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/TextSelectionColors.kt
index 6602ce5..1c763e7 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/TextSelectionColors.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/TextSelectionColors.kt
@@ -20,7 +20,6 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.ambientOf
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.util.nativeClass
 
 /**
  * Represents the colors used for text selection by text and text field components.
@@ -42,9 +41,7 @@
 ) {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (nativeClass() != other?.nativeClass()) return false
-
-        other as TextSelectionColors
+        if (other !is TextSelectionColors) return false
 
         if (handleColor != other.handleColor) return false
         if (backgroundColor != other.backgroundColor) return false
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
index 8b28026..bca6c84 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
@@ -23,8 +23,10 @@
 import androidx.compose.ui.layout.globalBounds
 import androidx.compose.ui.layout.globalPosition
 import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.layout.LayoutInfo
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.node.LayoutNodeWrapper
+import androidx.compose.ui.node.Owner
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastForEach
 
@@ -66,9 +68,20 @@
     val id: Int = layoutNodeWrapper.modifier.id
 
     /**
+     * The [LayoutInfo] that this is associated with.
+     */
+    val layoutInfo: LayoutInfo = layoutNodeWrapper.layoutNode
+
+    /**
+     * The [Owner] this node is attached to.
+     */
+    // TODO(b/174747742) Stop using Owner in tests and use RootForTest instead
+    val owner: Owner? get() = layoutNode.owner
+
+    /**
      * The [LayoutNode] that this is associated with.
      */
-    val layoutNode: LayoutNode = layoutNodeWrapper.layoutNode
+    internal val layoutNode: LayoutNode = layoutNodeWrapper.layoutNode
 
     // GEOMETRY
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
index e6d3278..bc823ec 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
@@ -33,10 +33,10 @@
      * Developer-set content description of the semantics node. If this is not set, accessibility
      * services will present the [Text] of this node as content part.
      *
-     * @see SemanticsPropertyReceiver.accessibilityLabel
+     * @see SemanticsPropertyReceiver.contentDescription
      */
-    val AccessibilityLabel = SemanticsPropertyKey<String>(
-        name = "AccessibilityLabel",
+    val ContentDescription = SemanticsPropertyKey<String>(
+        name = "ContentDescription",
         mergePolicy = { parentValue, childValue ->
             if (parentValue == null) {
                 childValue
@@ -52,20 +52,21 @@
      * [AccessibilityRangeInfo], but it is not guaranteed and the format will be decided by
      * accessibility services.
      *
-     * @see SemanticsPropertyReceiver.accessibilityValue
+     * @see SemanticsPropertyReceiver.stateDescription
      */
-    val AccessibilityValue = SemanticsPropertyKey<String>("AccessibilityValue")
+    val StateDescription = SemanticsPropertyKey<String>("StateDescription")
 
     /**
      * The node is a range with current value.
      *
-     * @see SemanticsPropertyReceiver.accessibilityValueRange
+     * @see SemanticsPropertyReceiver.stateDescriptionRange
      */
     val AccessibilityRangeInfo =
         SemanticsPropertyKey<AccessibilityRangeInfo>("AccessibilityRangeInfo")
 
     /**
-     * Whether this semantics node is disabled.
+     * Whether this semantics node is disabled. Note that proper [SemanticsActions] should still
+     * be added when this property is set.
      *
      * @see SemanticsPropertyReceiver.disabled
      */
@@ -410,9 +411,15 @@
  * Developer-set content description of the semantics node. If this is not set, accessibility
  * services will present the text of this node as content part.
  *
- * @see SemanticsProperties.AccessibilityLabel
+ * @see SemanticsProperties.ContentDescription
  */
-var SemanticsPropertyReceiver.accessibilityLabel by SemanticsProperties.AccessibilityLabel
+var SemanticsPropertyReceiver.contentDescription by SemanticsProperties.ContentDescription
+
+@Deprecated(
+    "accessibilityLabel was renamed to contentDescription",
+    ReplaceWith("contentDescription", "androidx.compose.ui.semantics")
+)
+var SemanticsPropertyReceiver.accessibilityLabel by SemanticsProperties.ContentDescription
 
 /**
  * Developer-set state description of the semantics node. For example: on/off. If this not
@@ -420,16 +427,22 @@
  * [AccessibilityRangeInfo], but it is not guaranteed and the format will be decided by
  * accessibility services.
  *
- * @see SemanticsProperties.AccessibilityValue
+ * @see SemanticsProperties.StateDescription
  */
-var SemanticsPropertyReceiver.accessibilityValue by SemanticsProperties.AccessibilityValue
+var SemanticsPropertyReceiver.stateDescription by SemanticsProperties.StateDescription
+
+@Deprecated(
+    "accessibilityValue was renamed to stateDescription",
+    ReplaceWith("stateDescription", "androidx.compose.ui.semantics")
+)
+var SemanticsPropertyReceiver.accessibilityValue by SemanticsProperties.StateDescription
 
 /**
  * The node is a range with current value.
  *
  * @see SemanticsProperties.AccessibilityRangeInfo
  */
-var SemanticsPropertyReceiver.accessibilityValueRange by SemanticsProperties.AccessibilityRangeInfo
+var SemanticsPropertyReceiver.stateDescriptionRange by SemanticsProperties.AccessibilityRangeInfo
 
 /**
  * Whether this semantics node is disabled.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsWrapper.kt
index f40d31b..9c951f0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsWrapper.kt
@@ -40,6 +40,7 @@
     }
 
     override fun onModifierChanged() {
+        super.onModifierChanged()
         layoutNode.owner?.onSemanticsChange()
     }
 
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.kt
index 9a82047..cd34bb7 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.kt
@@ -15,7 +15,6 @@
  */
 package androidx.compose.desktop
 
-import androidx.compose.runtime.Composable
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.window.MenuBar
@@ -142,12 +141,19 @@
      */
     abstract fun setSize(width: Int, height: Int)
 
-    /**
-     * Shows a window with the given Compose content.
-     *
-     * @param content Composable content of the window.
-     */
-    abstract fun show(content: @Composable () -> Unit)
+    // TODO(demin): uncomment this after b/175234629 will be fixed
+//    /**
+//     * Shows a window with the given Compose content.
+//     *
+//     * @param parentComposition The parent composition reference to coordinate
+//     *        scheduling of composition updates.
+//     *        If null then default root composition will be used.
+//     * @param content Composable content of the window.
+//     */
+//    abstract fun show(
+//        parentComposition: CompositionReference? = null,
+//        content: @Composable () -> Unit
+//    )
 
     /**
      * Closes the window.
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt
index f4131a1..a4a323b 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt
@@ -16,10 +16,10 @@
 package androidx.compose.desktop
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionReference
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.ambientOf
 import androidx.compose.runtime.emptyContent
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.platform.Keyboard
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
@@ -321,8 +321,11 @@
         window.setLocation(x, y)
     }
 
-    private fun onCreate(content: @Composable () -> Unit) {
-        window.setContent {
+    private fun onCreate(
+        parentComposition: CompositionReference? = null,
+        content: @Composable () -> Unit
+    ) {
+        window.setContent(parentComposition) {
             Providers(
                 AppWindowAmbient provides this,
                 content = content
@@ -333,16 +336,21 @@
     /**
      * Shows a window with the given Compose content.
      *
+     * @param parentComposition The parent composition reference to coordinate
+     *        scheduling of composition updates.
+     *        If null then default root composition will be used.
      * @param content Composable content of the window.
      */
-    @OptIn(ExperimentalKeyInput::class)
-    override fun show(content: @Composable () -> Unit) {
+    fun show(
+        parentComposition: CompositionReference? = null,
+        content: @Composable () -> Unit
+    ) {
         if (invoker != null) {
             invoker!!.lockWindow()
             window.setAlwaysOnTop(true)
         }
 
-        onCreate {
+        onCreate(parentComposition) {
             window.layer.owners?.keyboard = keyboard
             content()
         }
@@ -387,6 +395,5 @@
     /**
      * Gets the Keyboard object of the window.
      */
-    @ExperimentalKeyInput
     val keyboard: Keyboard = Keyboard()
 }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt
index c189e14..66110a8 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composition
+import androidx.compose.runtime.CompositionReference
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.input.mouse.MouseScrollEvent
 import androidx.compose.ui.input.mouse.MouseScrollUnit
@@ -28,10 +29,10 @@
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.unit.Density
 import org.jetbrains.skija.Canvas
-import org.jetbrains.skiko.HardwareLayer
 import org.jetbrains.skija.Picture
 import org.jetbrains.skija.PictureRecorder
 import org.jetbrains.skija.Rect
+import org.jetbrains.skiko.HardwareLayer
 import org.jetbrains.skiko.SkiaLayer
 import org.jetbrains.skiko.SkiaRenderer
 import java.awt.DisplayMode
@@ -295,6 +296,7 @@
     internal fun setContent(
         parent: Any? = null,
         invalidate: () -> Unit = this::needRedrawLayer,
+        parentComposition: CompositionReference? = null,
         content: @Composable () -> Unit
     ): Composition {
         check(owners == null) {
@@ -304,7 +306,7 @@
         val desktopOwner = DesktopOwner(desktopOwners, density)
 
         owners = desktopOwners
-        val composition = desktopOwner.setContent(content)
+        val composition = desktopOwner.setContent(parent = parentComposition, content = content)
 
         onDensityChanged(desktopOwner::density::set)
 
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
index b980f90..4b5c6b7 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
@@ -17,6 +17,7 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composition
+import androidx.compose.runtime.CompositionReference
 import java.awt.event.ComponentAdapter
 import java.awt.event.ComponentEvent
 import javax.swing.JFrame
@@ -42,14 +43,21 @@
     /**
      * Sets Compose content of the ComposeWindow.
      *
+     * @param parentComposition The parent composition reference to coordinate
+     *        scheduling of composition updates.
+     *        If null then default root composition will be used.
      * @param content Composable content of the ComposeWindow.
      *
      * @return Composition of the content.
      */
-    fun setContent(content: @Composable () -> Unit): Composition {
+    fun setContent(
+        parentComposition: CompositionReference? = null,
+        content: @Composable () -> Unit
+    ): Composition {
         return layer.setContent(
             parent = parent,
             invalidate = this::needRedrawLayer,
+            parentComposition = parentComposition,
             content = content
         )
     }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/key/KeyEventDesktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/key/KeyEventDesktop.kt
index c8aeb01..af0c990 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/key/KeyEventDesktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/key/KeyEventDesktop.kt
@@ -20,7 +20,6 @@
 import java.awt.event.KeyEvent.KEY_RELEASED
 import java.awt.event.KeyEvent as KeyEventAwt
 
-@OptIn(ExperimentalKeyInput::class)
 internal inline class KeyEventDesktop(val keyEvent: KeyEventAwt) : KeyEvent {
 
     override val key: Key
@@ -47,19 +46,4 @@
 
     override val isShiftPressed: Boolean
         get() = keyEvent.isShiftDown
-
-    @Suppress("DEPRECATION", "OverridingDeprecatedMember")
-    override val alt: Alt
-        get() = AltDesktop(keyEvent)
-}
-
-@Suppress("DEPRECATION")
-@OptIn(ExperimentalKeyInput::class)
-internal inline class AltDesktop(val keyEvent: KeyEventAwt) : Alt {
-
-    override val isLeftAltPressed
-        get() = keyEvent.isAltDown
-
-    override val isRightAltPressed
-        get() = keyEvent.isAltGraphDown
 }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/key/ShortcutsModifier.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/key/ShortcutsModifier.kt
index 0dad286..5f69dc6 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/key/ShortcutsModifier.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/key/ShortcutsModifier.kt
@@ -80,7 +80,6 @@
 
 private fun makeHandlers() = TreeMap<KeysSet, () -> Unit>()
 
-@ExperimentalKeyInput
 internal class ShortcutsInstance(
     internal var handlers: TreeMap<KeysSet, () -> Unit> = makeHandlers()
 ) {
@@ -130,15 +129,14 @@
 /**
  * [KeyEvent] handler which tracks pressed keys and triggers matched callbacks
  *
- * @see [keyInputFilter]
+ * @see [onKeyEvent]
  * @see [androidx.compose.ui.platform.Keyboard] to define window-scoped shortcuts
  */
-@ExperimentalKeyInput
 @Composable
 fun Modifier.shortcuts(builder: (ShortcutsBuilderScope).() -> Unit) = composed {
     val instance = remember { ShortcutsInstance() }
     instance.handlers = ShortcutsBuilderScope().also(builder).handlers
-    keyInputFilter(instance::process)
+    onKeyEvent(instance::process)
 }
 
 class ShortcutsBuilderScope {
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
index 3510b9c..7c38b83 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
@@ -24,13 +24,12 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.autofill.Autofill
 import androidx.compose.ui.autofill.AutofillTree
-import androidx.compose.ui.focus.ExperimentalFocus
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.focus.FocusManagerImpl
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.DesktopCanvas
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.input.key.KeyInputModifier
 import androidx.compose.ui.input.mouse.MouseScrollEvent
@@ -57,8 +56,7 @@
 import androidx.compose.ui.unit.LayoutDirection
 
 @OptIn(
-    ExperimentalFocus::class,
-    ExperimentalKeyInput::class,
+    ExperimentalComposeUiApi::class,
     ExperimentalComposeApi::class,
     InternalCoreApi::class
 )
@@ -177,9 +175,6 @@
         }
     }
 
-    override val hasPendingMeasureOrLayout
-        get() = measureAndLayoutDelegate.hasPendingMeasureOrLayout
-
     override fun createLayer(
         drawBlock: (Canvas) -> Unit,
         invalidateParentLayer: () -> Unit
@@ -194,6 +189,8 @@
 
     override fun onSemanticsChange() = Unit
 
+    override fun onLayoutChange(layoutNode: LayoutNode) = Unit
+
     override fun calculatePosition() = IntOffset.Zero
 
     fun setSize(width: Int, height: Int) {
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt
index d615e5e..5abcf8a 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt
@@ -18,7 +18,6 @@
 import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.staticAmbientOf
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.KeyEventDesktop
 import androidx.compose.ui.input.mouse.MouseScrollEvent
 import androidx.compose.ui.input.pointer.PointerId
@@ -49,7 +48,6 @@
     }
 
     val list = LinkedHashSet<DesktopOwner>()
-    @ExperimentalKeyInput
     var keyboard: Keyboard? = null
 
     private var pointerId = 0L
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelection.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelection.kt
index 1c150ec..e9fa62c 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelection.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelection.kt
@@ -22,7 +22,6 @@
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.gesture.DragObserver
 import androidx.compose.ui.gesture.rawDragGestureFilter
@@ -91,7 +90,6 @@
     }
 }
 
-@OptIn(ExperimentalFocus::class)
 @Composable
 fun DesktopSelectionContainer(
     selection: Selection?,
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionManager.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionManager.kt
index 8cf64c5..ea143f0 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionManager.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionManager.kt
@@ -25,7 +25,9 @@
 import androidx.compose.ui.selection.getCurrentSelectedText
 import androidx.compose.ui.selection.merge
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
 
+@OptIn(ExperimentalTextApi::class)
 internal class DesktopSelectionManager(private val selectionRegistrar: SelectionRegistrarImpl) {
     private var dragBeginPosition = Offset.Zero
 
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionRegistrar.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionRegistrar.kt
index ce9ba4b4..fdfe3c3 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionRegistrar.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopSelectionRegistrar.kt
@@ -20,8 +20,10 @@
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.selection.Selectable
 import androidx.compose.ui.selection.SelectionRegistrar
+import androidx.compose.ui.text.ExperimentalTextApi
 
 // based on androidx.compose.ui.selection.SelectionRegistrarImpl
+@OptIn(ExperimentalTextApi::class)
 internal class DesktopSelectionRegistrar : SelectionRegistrar {
     internal var sorted: Boolean = false
 
@@ -71,12 +73,23 @@
         return selectables
     }
 
-    override fun onPositionChange() {
+    override fun notifyPositionChange() {
         sorted = false
         onPositionChangeCallback?.invoke()
     }
 
-    override fun onUpdateSelection(
+    override fun notifySelectionUpdateStart(
+        layoutCoordinates: LayoutCoordinates,
+        startPosition: Offset
+    ) {
+        onUpdateSelectionCallback?.invoke(
+            layoutCoordinates,
+            startPosition,
+            startPosition
+        )
+    }
+
+    override fun notifySelectionUpdate(
         layoutCoordinates: LayoutCoordinates,
         startPosition: Offset,
         endPosition: Offset
@@ -87,4 +100,8 @@
             endPosition
         )
     }
+
+    override fun notifySelectionUpdateEnd() { /* do nothing */ }
+
+    override fun notifySelectableChange(selectable: Selectable) { /* do nothing */ }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Keyboard.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Keyboard.kt
index 750baf3..95b1628 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Keyboard.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Keyboard.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.platform
 
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.input.key.KeysSet
@@ -28,7 +27,6 @@
  *
  * @see [shortcuts] to setup event handlers based on the element that is in focus
  */
-@ExperimentalKeyInput
 class Keyboard {
     private val shortcutsInstance = lazy {
         ShortcutsInstance()
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
index 9c7e461..43ca949c 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
@@ -22,14 +22,24 @@
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.compositionFor
-import androidx.compose.ui.input.key.ExperimentalKeyInput
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.node.LayoutNode
 
-@OptIn(ExperimentalComposeApi::class, ExperimentalKeyInput::class)
-fun DesktopOwner.setContent(content: @Composable () -> Unit): Composition {
+/**
+ * Composes the given composable into [DesktopOwner]
+ *
+ * @param parent The parent composition reference to coordinate scheduling of composition updates
+ *        If null then default root composition will be used.
+ * @param content A `@Composable` function declaring the UI contents
+ */
+@OptIn(ExperimentalComposeApi::class)
+fun DesktopOwner.setContent(
+    parent: CompositionReference? = null,
+    content: @Composable () -> Unit
+): Composition {
     GlobalSnapshotManager.ensureStarted()
 
-    val composition = compositionFor(root, DesktopUiApplier(root), Recomposer.current())
+    val composition = compositionFor(root, DesktopUiApplier(root), parent ?: Recomposer.current())
     composition.setContent {
         ProvideDesktopAmbients(this) {
             DesktopSelectionContainer(content)
@@ -46,8 +56,7 @@
 
     return composition
 }
-
-@OptIn(ExperimentalKeyInput::class)
+@OptIn(ExperimentalComposeUiApi::class)
 @Composable
 private fun ProvideDesktopAmbients(owner: DesktopOwner, content: @Composable () -> Unit) {
     Providers(
@@ -64,7 +73,7 @@
 }
 
 @OptIn(ExperimentalComposeApi::class)
-internal actual fun actualSubcomposeInto(
+internal actual fun subcomposeInto(
     container: LayoutNode,
     parent: CompositionReference,
     composable: @Composable () -> Unit
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/vector/DesktopXmlVectorParser.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/vector/DesktopXmlVectorParser.kt
index 08b3ac3..70253a7 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/vector/DesktopXmlVectorParser.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/vector/DesktopXmlVectorParser.kt
@@ -19,14 +19,11 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorStop
-import androidx.compose.ui.graphics.LinearGradient
+import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.PathFillType
-import androidx.compose.ui.graphics.RadialGradient
-import androidx.compose.ui.graphics.ShaderBrush
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.StrokeCap
 import androidx.compose.ui.graphics.StrokeJoin
-import androidx.compose.ui.graphics.SweepGradient
 import androidx.compose.ui.graphics.TileMode
 import androidx.compose.ui.graphics.vector.DefaultPivotX
 import androidx.compose.ui.graphics.vector.DefaultPivotY
@@ -157,13 +154,13 @@
 
 private fun parseStringBrush(str: String) = SolidColor(Color(parseColorValue(str)))
 
-private fun Element.parseElementBrush(): ShaderBrush? =
+private fun Element.parseElementBrush(): Brush? =
     childrenSequence
         .filterIsInstance<Element>()
         .find { it.nodeName == "gradient" }
         ?.parseGradient()
 
-private fun Element.parseGradient(): ShaderBrush? {
+private fun Element.parseGradient(): Brush? {
     return when (attributeOrNull(ANDROID_NS, "type")) {
         "linear" -> parseLinearGradient()
         "radial" -> parseRadialGradient()
@@ -173,26 +170,32 @@
 }
 
 @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-private fun Element.parseLinearGradient() = LinearGradient(
+private fun Element.parseLinearGradient() = Brush.linearGradient(
     colorStops = parseColorStops(),
-    startX = attributeOrNull(ANDROID_NS, "startX")?.toFloat() ?: 0f,
-    startY = attributeOrNull(ANDROID_NS, "startY")?.toFloat() ?: 0f,
-    endX = attributeOrNull(ANDROID_NS, "endX")?.toFloat() ?: 0f,
-    endY = attributeOrNull(ANDROID_NS, "endY")?.toFloat() ?: 0f,
+    start = Offset(
+        attributeOrNull(ANDROID_NS, "startX")?.toFloat() ?: 0f,
+        attributeOrNull(ANDROID_NS, "startY")?.toFloat() ?: 0f
+    ),
+    end = Offset(
+        attributeOrNull(ANDROID_NS, "endX")?.toFloat() ?: 0f,
+        attributeOrNull(ANDROID_NS, "endY")?.toFloat() ?: 0f
+    ),
     tileMode = attributeOrNull(ANDROID_NS, "tileMode")?.let(::parseTileMode) ?: TileMode.Clamp
 )
 
 @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-private fun Element.parseRadialGradient() = RadialGradient(
+private fun Element.parseRadialGradient() = Brush.radialGradient(
     colorStops = parseColorStops(),
-    centerX = attributeOrNull(ANDROID_NS, "centerX")?.toFloat() ?: 0f,
-    centerY = attributeOrNull(ANDROID_NS, "centerY")?.toFloat() ?: 0f,
+    center = Offset(
+        attributeOrNull(ANDROID_NS, "centerX")?.toFloat() ?: 0f,
+        attributeOrNull(ANDROID_NS, "centerY")?.toFloat() ?: 0f
+    ),
     radius = attributeOrNull(ANDROID_NS, "gradientRadius")?.toFloat() ?: 0f,
     tileMode = attributeOrNull(ANDROID_NS, "tileMode")?.let(::parseTileMode) ?: TileMode.Clamp
 )
 
 @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-private fun Element.parseSweepGradient() = SweepGradient(
+private fun Element.parseSweepGradient() = Brush.sweepGradient(
     colorStops = parseColorStops(),
     center = Offset(
         attributeOrNull(ANDROID_NS, "centerX")?.toFloat() ?: 0f,
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopDialog.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopDialog.kt
index 8ce70de..9b5cc81 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopDialog.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopDialog.kt
@@ -17,10 +17,11 @@
 package androidx.compose.ui.window
 
 import androidx.compose.desktop.AppWindow
-import androidx.compose.desktop.WindowEvents
 import androidx.compose.desktop.AppWindowAmbient
+import androidx.compose.desktop.WindowEvents
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.compositionReference
 import androidx.compose.runtime.onActive
 import androidx.compose.runtime.onDispose
 import androidx.compose.runtime.remember
@@ -74,6 +75,7 @@
         return
     }
 
+    val parentComposition = compositionReference()
     val dialog = remember {
         AppWindow(
             attached = attached,
@@ -90,7 +92,7 @@
     }
 
     onActive {
-        dialog.show {
+        dialog.show(parentComposition) {
             content()
         }
     }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopPopup.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopPopup.kt
index 545b562..5b83e1a 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopPopup.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopPopup.kt
@@ -16,18 +16,19 @@
 package androidx.compose.ui.window
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.onDispose
+import androidx.compose.runtime.compositionReference
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.onDispose
 import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.gesture.tapGestureFilter
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.DesktopOwner
 import androidx.compose.ui.platform.DesktopOwnersAmbient
 import androidx.compose.ui.platform.setContent
-import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.unit.IntBounds
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.round
@@ -53,7 +54,7 @@
     val owners = DesktopOwnersAmbient.current
     val density = AmbientDensity.current
 
-    var parentBounds = remember { mutableStateOf(IntBounds(0, 0, 0, 0)) }
+    val parentBounds = remember { mutableStateOf(IntBounds(0, 0, 0, 0)) }
 
     // getting parent bounds
     Layout(
@@ -70,9 +71,10 @@
         }
     )
 
-    val owner = remember {
+    val parentComposition = compositionReference()
+    val (owner, composition) = remember {
         val owner = DesktopOwner(owners, density)
-        owner.setContent {
+        val composition = owner.setContent(parent = parentComposition) {
             Layout(
                 content = content,
                 modifier = Modifier.tapGestureFilter {
@@ -106,10 +108,11 @@
                 }
             )
         }
-        owner
+        owner to composition
     }
     owner.density = density
     onDispose {
+        composition.dispose()
         owner.dispose()
     }
 }
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/key/KeyInputUtil.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/key/KeyInputUtil.kt
index 0429c05..280e4817 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/key/KeyInputUtil.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/key/KeyInputUtil.kt
@@ -28,7 +28,6 @@
  * The [KeyEvent] is usually created by the system. This function creates an instance of
  * [KeyEvent] that can be used in tests.
  */
-@OptIn(ExperimentalKeyInput::class)
 fun keyEvent(key: Key, keyEventType: KeyEventType): KeyEvent {
     val action = when (keyEventType) {
         KeyEventType.KeyDown -> KEY_PRESSED
@@ -46,7 +45,6 @@
 /**
  * Creates [KeyEvent] of Unknown type. It wraps KEY_TYPED AWTs KeyEvent
  */
-@OptIn(ExperimentalKeyInput::class)
 fun keyTypedEvent(key: Key): KeyEvent {
     return KeyEventDesktop(
         KeyEventAwt(
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/key/ShortcutsTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/key/ShortcutsTest.kt
index 78df8af..5ccfc39 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/key/ShortcutsTest.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/key/ShortcutsTest.kt
@@ -22,10 +22,9 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focusRequester
+import androidx.compose.ui.focus.focusModifier
+import androidx.compose.ui.focus.FocusReference
+import androidx.compose.ui.focus.focusReference
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performKeyPress
@@ -37,24 +36,20 @@
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-@OptIn(
-    ExperimentalFocus::class,
-    ExperimentalKeyInput::class
-)
 class ShortcutsTest {
     @get:Rule
     val rule = createComposeRule()
 
     @Test
     fun shortcuts_triggered() {
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         var triggered = 0
         rule.setContent {
             Box(
                 modifier = Modifier
                     .size(10.dp, 10.dp)
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .focusReference(focusReference)
+                    .focusModifier()
                     .shortcuts {
                         on(Key.MetaLeft + Key.Enter) {
                             triggered += 1
@@ -63,7 +58,7 @@
             )
         }
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         val firstKeyConsumed = rule.onRoot().performKeyPress(
@@ -97,15 +92,15 @@
 
     @Test
     fun shortcuts_states() {
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         var triggered = 0
         var setShortcuts by mutableStateOf(true)
         rule.setContent {
             Box(
                 modifier = Modifier
                     .size(10.dp, 10.dp)
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .focusReference(focusReference)
+                    .focusModifier()
                     .shortcuts {
                         if (setShortcuts) {
                             on(Key.Enter) {
@@ -117,7 +112,7 @@
         }
 
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         rule.onRoot().performKeyPress(
@@ -154,15 +149,15 @@
 
     @Test
     fun shortcuts_priority() {
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         var enterTriggered = 0
         var shortcutTriggered = 0
         rule.setContent {
             Box(
                 modifier = Modifier
                     .size(10.dp, 10.dp)
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .focusReference(focusReference)
+                    .focusModifier()
                     .shortcuts {
                         on(Key.Enter) {
                             enterTriggered += 1
@@ -176,7 +171,7 @@
         }
 
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         rule.onRoot().performKeyPress(
@@ -222,15 +217,15 @@
 
     @Test
     fun shortcuts_multiple() {
-        val focusRequester = FocusRequester()
+        val focusReference = FocusReference()
         var aTriggered = 0
         var cTriggered = 0
         rule.setContent {
             Box(
                 modifier = Modifier
                     .size(10.dp, 10.dp)
-                    .focusRequester(focusRequester)
-                    .focus()
+                    .focusReference(focusReference)
+                    .focusModifier()
                     .shortcuts {
                         on(Key.MetaLeft + Key.A) {
                             aTriggered += 1
@@ -244,7 +239,7 @@
         }
 
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            focusReference.requestFocus()
         }
 
         rule.onRoot().performKeyPress(
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilterTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilterTest.kt
index 1fed94d..d743d95 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilterTest.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilterTest.kt
@@ -146,7 +146,7 @@
             )
             Box(
                 Modifier
-                    .mouseScrollFilter { event, bounds ->
+                    .mouseScrollFilter { _, _ ->
                         false
                     }
                     .size(5.dp, 10.dp)
@@ -254,7 +254,7 @@
             ) {
                 Box(
                     Modifier
-                        .mouseScrollFilter { event, bounds ->
+                        .mouseScrollFilter { _, _ ->
                             false
                         }
                         .size(5.dp, 10.dp)
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
index 87ad037..7f0aca2 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
@@ -29,7 +29,7 @@
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.lazy.LazyColumnFor
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -53,7 +53,6 @@
 import org.junit.Test
 
 @OptIn(
-    ExperimentalLayoutNodeApi::class,
     ExperimentalComposeApi::class,
     ExperimentalCoroutinesApi::class
 )
@@ -289,10 +288,12 @@
         var height by mutableStateOf(10.dp)
         setContent {
             Box(Modifier.padding(10.dp)) {
-                LazyColumnFor(
-                    listOf(Color.Red, Color.Green, Color.Blue, Color.Black, Color.Gray)
-                ) { color ->
-                    Box(Modifier.size(width = 30.dp, height = height).background(color))
+                LazyColumn {
+                    items(
+                        listOf(Color.Red, Color.Green, Color.Blue, Color.Black, Color.Gray)
+                    ) { color ->
+                        Box(Modifier.size(width = 30.dp, height = height).background(color))
+                    }
                 }
             }
         }
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/DesktopPopupTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/DesktopPopupTest.kt
new file mode 100644
index 0000000..307fc91
--- /dev/null
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/DesktopPopupTest.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.window
+
+import androidx.compose.runtime.Providers
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.onDispose
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.staticAmbientOf
+import androidx.compose.ui.platform.AmbientDensity
+import androidx.compose.ui.test.ExperimentalTesting
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Density
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+
+@OptIn(ExperimentalTesting::class)
+class DesktopPopupTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun `pass ambients to popup`() {
+        val ambient = staticAmbientOf<Int>()
+
+        var actualAmbientValue = 0
+
+        rule.setContent {
+            Providers(ambient provides 3) {
+                Popup {
+                    actualAmbientValue = ambient.current
+                }
+            }
+        }
+
+        Truth.assertThat(actualAmbientValue).isEqualTo(3)
+    }
+
+    @Test
+    fun `onDispose inside popup`() {
+        var isPopupShowing by mutableStateOf(true)
+        var isDisposed = false
+
+        rule.setContent {
+            if (isPopupShowing) {
+                Popup {
+                    onDispose {
+                        isDisposed = true
+                    }
+                }
+            }
+        }
+
+        isPopupShowing = false
+        rule.waitForIdle()
+
+        Truth.assertThat(isDisposed).isEqualTo(true)
+    }
+
+    @Test
+    fun `use density inside popup`() {
+        var density by mutableStateOf(Density(2f, 1f))
+        var densityInsidePopup = 0f
+
+        rule.setContent {
+            Providers(AmbientDensity provides density) {
+                Popup {
+                    densityInsidePopup = AmbientDensity.current.density
+                }
+            }
+        }
+
+        Truth.assertThat(densityInsidePopup).isEqualTo(2f)
+
+        density = Density(3f, 1f)
+        rule.waitForIdle()
+        Truth.assertThat(densityInsidePopup).isEqualTo(3f)
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt b/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/node/JvmTreeSet.kt
similarity index 76%
copy from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
copy to compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/node/JvmTreeSet.kt
index f9cb2fe..c0cd263 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
+++ b/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/node/JvmTreeSet.kt
@@ -1,3 +1,4 @@
+// ktlint-disable filename
 /*
  * Copyright 2020 The Android Open Source Project
  *
@@ -14,7 +15,7 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.focus
+package androidx.compose.ui.node
 
-@RequiresOptIn("The Focus API is experimental and is likely to change in the future.")
-annotation class ExperimentalFocus
\ No newline at end of file
+internal actual class TreeSet<E> actual constructor(comparator: Comparator<in E>) :
+    java.util.TreeSet<E>(comparator)
diff --git a/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/platform/JvmActuals.kt b/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/platform/JvmActuals.kt
index 3a97ea9..0395969 100644
--- a/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/platform/JvmActuals.kt
+++ b/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/platform/JvmActuals.kt
@@ -26,4 +26,6 @@
     }
 
     return className + "@" + String.format("%07x", System.identityHashCode(obj))
-}
\ No newline at end of file
+}
+
+internal actual fun Any.nativeClass(): Any = this.javaClass
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/ComposedModifierTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/ComposedModifierTest.kt
index 7485666..8b0058f 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/ComposedModifierTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/ComposedModifierTest.kt
@@ -22,7 +22,6 @@
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.InternalComposeApi
 import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.SlotTable
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.dispatch.MonotonicFrameClock
 import androidx.compose.runtime.invalidate
@@ -211,7 +210,6 @@
     block: @Composable () -> Unit
 ): Composer<Unit> {
     return Composer(
-        SlotTable(),
         EmptyApplier(),
         recomposer
     ).apply {
@@ -221,7 +219,7 @@
             fn(this, 0)
         }
         applyChanges()
-        slotTable.verifyWellFormed()
+        verifyConsistent()
     }
 }
 
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidAutofillTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidAutofillTest.kt
index 3cbb212..6ef2547 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidAutofillTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidAutofillTest.kt
@@ -19,6 +19,7 @@
 import android.app.Activity
 import android.view.View
 import android.view.autofill.AutofillManager
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.toComposeRect
 import androidx.compose.ui.test.ComposeUiRobolectricTestRunner
@@ -35,6 +36,7 @@
 import org.robolectric.shadow.api.Shadow
 import android.graphics.Rect as AndroidRect
 
+@OptIn(ExperimentalComposeUiApi::class)
 @RunWith(ComposeUiRobolectricTestRunner::class)
 @Config(
     shadows = [ShadowAutofillManager::class],
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidAutofillTypeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidAutofillTypeTest.kt
index 0475d44..4361ec1 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidAutofillTypeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidAutofillTypeTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.autofill
 
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.autofill.AutofillType.AddressAuxiliaryDetails
 import androidx.compose.ui.autofill.AutofillType.AddressCountry
 import androidx.compose.ui.autofill.AutofillType.AddressLocality
@@ -57,6 +58,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
+@OptIn(ExperimentalComposeUiApi::class)
 @RunWith(JUnit4::class)
 class AndroidAutofillTypeTest {
 
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidPerformAutofillTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidPerformAutofillTest.kt
index 4cb174c..f223b15 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidPerformAutofillTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidPerformAutofillTest.kt
@@ -20,6 +20,7 @@
 import android.util.SparseArray
 import android.view.View
 import android.view.autofill.AutofillValue
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.test.ComposeUiRobolectricTestRunner
 import com.google.common.truth.Truth
@@ -29,6 +30,7 @@
 import org.robolectric.Robolectric
 import org.robolectric.annotation.Config
 
+@OptIn(ExperimentalComposeUiApi::class)
 @RunWith(ComposeUiRobolectricTestRunner::class)
 @Config(minSdk = 26)
 class AndroidPerformAutofillTest {
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidPopulateViewStructureTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidPopulateViewStructureTest.kt
index 1653cf7..499687b 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidPopulateViewStructureTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AndroidPopulateViewStructureTest.kt
@@ -21,6 +21,7 @@
 import android.view.ViewStructure
 import androidx.autofill.HintConstants.AUTOFILL_HINT_PERSON_NAME
 import androidx.compose.testutils.fake.FakeViewStructure
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.test.ComposeUiRobolectricTestRunner
 import com.google.common.truth.Truth.assertThat
@@ -30,6 +31,7 @@
 import org.robolectric.Robolectric
 import org.robolectric.annotation.Config
 
+@OptIn(ExperimentalComposeUiApi::class)
 @RunWith(ComposeUiRobolectricTestRunner::class)
 @Config(
     manifest = Config.NONE,
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AutofillNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AutofillNodeTest.kt
index c56ffcc..42294c67 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AutofillNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/autofill/AutofillNodeTest.kt
@@ -16,11 +16,13 @@
 
 package androidx.compose.ui.autofill
 
+import androidx.compose.ui.ExperimentalComposeUiApi
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
+@OptIn(ExperimentalComposeUiApi::class)
 @RunWith(JUnit4::class)
 class AutofillNodeTest {
     @Test
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusObserverModifierTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusChangedModifierTest.kt
similarity index 76%
rename from compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusObserverModifierTest.kt
rename to compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusChangedModifierTest.kt
index 682eed0..85ae7c6 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusObserverModifierTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusChangedModifierTest.kt
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui
+package androidx.compose.ui.focus
 
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.ValueElement
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
@@ -26,7 +25,7 @@
 import org.junit.Before
 import org.junit.Test
 
-class FocusObserverModifierTest {
+class FocusChangedModifierTest {
     @Before
     fun before() {
         isDebugInspectorInfoEnabled = true
@@ -37,15 +36,14 @@
         isDebugInspectorInfoEnabled = false
     }
 
-    @OptIn(ExperimentalFocus::class)
     @Test
     fun testInspectorValue() {
         val onFocusChange: (FocusState) -> Unit = {}
-        val modifier = Modifier.focusObserver(onFocusChange) as InspectableValue
-        assertThat(modifier.nameFallback).isEqualTo("focusObserver")
+        val modifier = Modifier.onFocusChanged(onFocusChange) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("onFocusChanged")
         assertThat(modifier.valueOverride).isNull()
         assertThat(modifier.inspectableElements.asIterable()).containsExactly(
-            ValueElement("onFocusChange", onFocusChange)
+            ValueElement("onFocusChanged", onFocusChange)
         )
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusObserverModifierTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusEventModifierTest.kt
similarity index 73%
copy from compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusObserverModifierTest.kt
copy to compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusEventModifierTest.kt
index 682eed0..788320b 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusObserverModifierTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusEventModifierTest.kt
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui
+package androidx.compose.ui.focus
 
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.ValueElement
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
@@ -26,7 +25,7 @@
 import org.junit.Before
 import org.junit.Test
 
-class FocusObserverModifierTest {
+class FocusEventModifierTest {
     @Before
     fun before() {
         isDebugInspectorInfoEnabled = true
@@ -37,15 +36,14 @@
         isDebugInspectorInfoEnabled = false
     }
 
-    @OptIn(ExperimentalFocus::class)
     @Test
     fun testInspectorValue() {
-        val onFocusChange: (FocusState) -> Unit = {}
-        val modifier = Modifier.focusObserver(onFocusChange) as InspectableValue
-        assertThat(modifier.nameFallback).isEqualTo("focusObserver")
+        val onFocusEvent: (FocusState) -> Unit = {}
+        val modifier = Modifier.onFocusEvent(onFocusEvent) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("onFocusEvent")
         assertThat(modifier.valueOverride).isNull()
         assertThat(modifier.inspectableElements.asIterable()).containsExactly(
-            ValueElement("onFocusChange", onFocusChange)
+            ValueElement("onFocusEvent", onFocusEvent)
         )
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt
index 4caf023..8bf5916 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.focus
 
-import androidx.compose.ui.FocusModifier
 import androidx.compose.ui.focus.FocusState.Active
 import androidx.compose.ui.focus.FocusState.ActiveParent
 import androidx.compose.ui.focus.FocusState.Captured
@@ -32,9 +31,6 @@
 import org.junit.runners.Parameterized
 import kotlin.jvm.JvmStatic
 
-@OptIn(
-    ExperimentalFocus::class,
-)
 @RunWith(Parameterized::class)
 class FocusManagerTest(private val initialFocusState: FocusState) {
     companion object {
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusRequesterModifierTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusReferenceModifierTest.kt
similarity index 74%
rename from compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusRequesterModifierTest.kt
rename to compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusReferenceModifierTest.kt
index d54d1d1..dd154ee 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusRequesterModifierTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusReferenceModifierTest.kt
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui
+package androidx.compose.ui.focus
 
-import androidx.compose.ui.focus.ExperimentalFocus
-import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.ValueElement
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
@@ -26,7 +25,7 @@
 import org.junit.Before
 import org.junit.Test
 
-class FocusRequesterModifierTest {
+class FocusReferenceModifierTest {
     @Before
     fun before() {
         isDebugInspectorInfoEnabled = true
@@ -37,15 +36,14 @@
         isDebugInspectorInfoEnabled = false
     }
 
-    @OptIn(ExperimentalFocus::class)
     @Test
     fun testInspectorValue() {
-        val focusRequester = FocusRequester()
-        val modifier = Modifier.focusRequester(focusRequester) as InspectableValue
-        assertThat(modifier.nameFallback).isEqualTo("focusRequester")
+        val focusReference = FocusReference()
+        val modifier = Modifier.focusReference(focusReference) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("focusReference")
         assertThat(modifier.valueOverride).isNull()
         assertThat(modifier.inspectableElements.asIterable()).containsExactly(
-            ValueElement("focusRequester", focusRequester)
+            ValueElement("focusReference", focusReference)
         )
     }
 }
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 20cffbc..3d5fc83 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
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.ui.gesture
 
 import androidx.compose.ui.Modifier
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/DragSlopExceededGestureFilterTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/DragSlopExceededGestureFilterTest.kt
index 778a789..f45f5c0 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/DragSlopExceededGestureFilterTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/DragSlopExceededGestureFilterTest.kt
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
 @file:Suppress("PrivatePropertyName")
 
 package androidx.compose.ui.gesture
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/TapGestureFilterTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/TapGestureFilterTest.kt
index 9d686f0..658136a 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/TapGestureFilterTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/TapGestureFilterTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.ui.gesture
 
 import androidx.compose.ui.Modifier
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLockerSetupTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLockerSetupTest.kt
index b70b2b6..70abfca 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLockerSetupTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLockerSetupTest.kt
@@ -14,11 +14,8 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.ui.gesture.scrollorientationlocking
 
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.input.pointer.CustomEvent
 import androidx.compose.ui.input.pointer.CustomEventDispatcher
 import androidx.compose.ui.input.pointer.PointerEventPass
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLockerTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLockerTest.kt
index 8d26027..37139367 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLockerTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/gesture/scrollorientationlocking/ScrollOrientationLockerTest.kt
@@ -14,11 +14,8 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalPointerInput::class)
-
 package androidx.compose.ui.gesture.scrollorientationlocking
 
-import androidx.compose.ui.gesture.ExperimentalPointerInput
 import androidx.compose.ui.input.pointer.CustomEventDispatcher
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerInputChange
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/key/KeyInputModifierTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/key/KeyInputModifierTest.kt
index d072a85..b29c924 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/key/KeyInputModifierTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/key/KeyInputModifierTest.kt
@@ -36,24 +36,22 @@
         isDebugInspectorInfoEnabled = false
     }
 
-    @OptIn(ExperimentalKeyInput::class)
     @Test
-    fun testInspectorValueForKeyInputFilter() {
+    fun testInspectorValueForKeyEvent() {
         val onKeyEvent: (KeyEvent) -> Boolean = { true }
-        val modifier = Modifier.keyInputFilter(onKeyEvent) as InspectableValue
-        assertThat(modifier.nameFallback).isEqualTo("keyInputFilter")
+        val modifier = Modifier.onKeyEvent(onKeyEvent) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("onKeyEvent")
         assertThat(modifier.valueOverride).isNull()
         assertThat(modifier.inspectableElements.asIterable()).containsExactly(
             ValueElement("onKeyEvent", onKeyEvent)
         )
     }
 
-    @OptIn(ExperimentalKeyInput::class)
     @Test
-    fun testInspectorValueForPreviewKeyInputFilter() {
+    fun testInspectorValueForPreviewKeyEvent() {
         val onPreviewKeyEvent: (KeyEvent) -> Boolean = { true }
-        val modifier = Modifier.previewKeyInputFilter(onPreviewKeyEvent) as InspectableValue
-        assertThat(modifier.nameFallback).isEqualTo("previewKeyInputFilter")
+        val modifier = Modifier.onPreviewKeyEvent(onPreviewKeyEvent) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("onPreviewKeyEvent")
         assertThat(modifier.valueOverride).isNull()
         assertThat(modifier.inspectableElements.asIterable()).containsExactly(
             ValueElement("onPreviewKeyEvent", onPreviewKeyEvent)
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ConstraintsTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ConstraintsTest.kt
index e3b334b..23156b2 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ConstraintsTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ConstraintsTest.kt
@@ -24,7 +24,7 @@
 import androidx.compose.ui.unit.hasFixedWidth
 import androidx.compose.ui.unit.isZero
 import androidx.compose.ui.unit.offset
-import androidx.compose.ui.unit.satisfiedBy
+import androidx.compose.ui.unit.isSatisfiedBy
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -157,13 +157,13 @@
     @Test
     fun satisfiedBy() {
         val constraints = Constraints(2, 5, 7, 9)
-        assertTrue(constraints.satisfiedBy(IntSize(4, 8)))
-        assertTrue(constraints.satisfiedBy(IntSize(2, 7)))
-        assertTrue(constraints.satisfiedBy(IntSize(5, 9)))
-        assertFalse(constraints.satisfiedBy(IntSize(1, 8)))
-        assertFalse(constraints.satisfiedBy(IntSize(7, 8)))
-        assertFalse(constraints.satisfiedBy(IntSize(4, 5)))
-        assertFalse(constraints.satisfiedBy(IntSize(4, 11)))
+        assertTrue(constraints.isSatisfiedBy(IntSize(4, 8)))
+        assertTrue(constraints.isSatisfiedBy(IntSize(2, 7)))
+        assertTrue(constraints.isSatisfiedBy(IntSize(5, 9)))
+        assertFalse(constraints.isSatisfiedBy(IntSize(1, 8)))
+        assertFalse(constraints.isSatisfiedBy(IntSize(7, 8)))
+        assertFalse(constraints.isSatisfiedBy(IntSize(4, 5)))
+        assertFalse(constraints.isSatisfiedBy(IntSize(4, 11)))
     }
 
     @Test
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
index f2b8c87..fe6e120d 100644
--- 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
@@ -18,6 +18,8 @@
 
 import androidx.compose.ui.geometry.Size
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -109,4 +111,26 @@
     fun testSizeDivision() {
         assertEquals(Size(1f, 2f), Size(100f, 300f) / ScaleFactor(100f, 150f))
     }
+
+    @Test
+    fun testIsSpecified() {
+        assertFalse(ScaleFactor.Unspecified.isSpecified)
+        assertTrue(ScaleFactor(1f, 1f).isSpecified)
+    }
+
+    @Test
+    fun testIsUnspecified() {
+        assertTrue(ScaleFactor.Unspecified.isUnspecified)
+        assertFalse(ScaleFactor(1f, 1f).isUnspecified)
+    }
+
+    @Test
+    fun testTakeOrElseTrue() {
+        assertTrue(ScaleFactor(1f, 1f).takeOrElse { ScaleFactor.Unspecified }.isSpecified)
+    }
+
+    @Test
+    fun testTakeOrElseFalse() {
+        assertTrue(ScaleFactor.Unspecified.takeOrElse { ScaleFactor(1f, 1f) }.isSpecified)
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index 625a6d3..217044c 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -15,6 +15,7 @@
  */
 package androidx.compose.ui.node
 
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.draw.DrawModifier
 import androidx.compose.ui.Modifier
@@ -22,7 +23,6 @@
 import androidx.compose.ui.autofill.Autofill
 import androidx.compose.ui.autofill.AutofillTree
 import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Canvas
@@ -30,14 +30,15 @@
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.hapticfeedback.HapticFeedback
-import androidx.compose.ui.input.key.ExperimentalKeyInput
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.input.pointer.PointerInputFilter
 import androidx.compose.ui.input.pointer.PointerInputModifier
 import androidx.compose.ui.layout.LayoutModifier
+import androidx.compose.ui.layout.AlignmentLine
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.layout
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.platform.ClipboardManager
 import androidx.compose.ui.platform.TextToolbar
@@ -79,13 +80,13 @@
         val owner = MockOwner()
         node.attach(owner)
         assertEquals(owner, node.owner)
-        assertTrue(node.isAttached())
+        assertTrue(node.isAttached)
 
         assertEquals(1, owner.onAttachParams.count { it === node })
 
         node.detach()
         assertNull(node.owner)
-        assertFalse(node.isAttached())
+        assertFalse(node.isAttached)
         assertEquals(1, owner.onDetachParams.count { it === node })
     }
 
@@ -1633,12 +1634,66 @@
         // Dispose
         root.removeAt(0, 1)
 
-        assertFalse(node1.isAttached())
-        assertFalse(node2.isAttached())
+        assertFalse(node1.isAttached)
+        assertFalse(node2.isAttached)
         assertEquals(0, owner.onRequestMeasureParams.count { it === node1 })
         assertEquals(0, owner.onRequestMeasureParams.count { it === node2 })
     }
 
+    @Test
+    fun modifierMatchesWrapperWithIdentity() {
+        val modifier1 = Modifier.layout { measurable, constraints ->
+            val placeable = measurable.measure(constraints)
+            layout(placeable.width, placeable.height) {
+                placeable.place(0, 0)
+            }
+        }
+        val modifier2 = Modifier.layout { measurable, constraints ->
+            val placeable = measurable.measure(constraints)
+            layout(placeable.width, placeable.height) {
+                placeable.place(1, 1)
+            }
+        }
+
+        val root = LayoutNode()
+        root.modifier = modifier1.then(modifier2)
+
+        val wrapper1 = root.outerLayoutNodeWrapper
+        val wrapper2 = root.outerLayoutNodeWrapper.wrapped
+
+        assertEquals(modifier1, (wrapper1 as DelegatingLayoutNodeWrapper<*>).modifier)
+        assertEquals(modifier2, (wrapper2 as DelegatingLayoutNodeWrapper<*>).modifier)
+
+        root.modifier = modifier2.then(modifier1)
+
+        assertEquals(wrapper2, root.outerLayoutNodeWrapper)
+        assertEquals(wrapper1, root.outerLayoutNodeWrapper.wrapped)
+        assertEquals(
+            modifier1,
+            (root.outerLayoutNodeWrapper.wrapped as DelegatingLayoutNodeWrapper<*>).modifier
+        )
+        assertEquals(
+            modifier2,
+            (root.outerLayoutNodeWrapper as DelegatingLayoutNodeWrapper<*>).modifier
+        )
+    }
+
+    @Test
+    fun measureResultAndPositionChangesCallOnLayoutChange() {
+        val node = LayoutNode(20, 20, 100, 100)
+        val owner = MockOwner()
+        node.attach(owner)
+        node.innerLayoutNodeWrapper.measureResult = object : MeasureResult {
+            override val width = 50
+            override val height = 50
+            override val alignmentLines: Map<AlignmentLine, Int> get() = mapOf()
+            override fun placeChildren() {}
+        }
+        assertEquals(1, owner.layoutChangeCount)
+        node.place(0, 0)
+        assertEquals(2, owner.layoutChangeCount)
+    }
+
     private fun createSimpleLayout(): Triple<LayoutNode, LayoutNode, LayoutNode> {
         val layoutNode = ZeroSizedLayoutNode()
         val child1 = ZeroSizedLayoutNode()
@@ -1654,10 +1709,7 @@
         PointerInputModifier
 }
 
-@OptIn(
-    ExperimentalFocus::class,
-    InternalCoreApi::class
-)
+@OptIn(InternalCoreApi::class)
 private class MockOwner(
     val position: IntOffset = IntOffset.Zero,
     override val root: LayoutNode = LayoutNode()
@@ -1665,6 +1717,7 @@
     val onRequestMeasureParams = mutableListOf<LayoutNode>()
     val onAttachParams = mutableListOf<LayoutNode>()
     val onDetachParams = mutableListOf<LayoutNode>()
+    var layoutChangeCount = 0
 
     override val hapticFeedBack: HapticFeedback
         get() = TODO("Not yet implemented")
@@ -1672,8 +1725,10 @@
         get() = TODO("Not yet implemented")
     override val textToolbar: TextToolbar
         get() = TODO("Not yet implemented")
+    @OptIn(ExperimentalComposeUiApi::class)
     override val autofillTree: AutofillTree
         get() = TODO("Not yet implemented")
+    @OptIn(ExperimentalComposeUiApi::class)
     override val autofill: Autofill?
         get() = TODO("Not yet implemented")
     override val density: Density
@@ -1702,8 +1757,6 @@
         layoutNode.layoutState = LayoutNode.LayoutState.NeedsRelayout
     }
 
-    override val hasPendingMeasureOrLayout = false
-
     override fun onAttach(node: LayoutNode) {
         onAttachParams += node
     }
@@ -1716,7 +1769,6 @@
 
     override fun requestFocus(): Boolean = false
 
-    @ExperimentalKeyInput
     override fun sendKeyEvent(keyEvent: KeyEvent): Boolean = false
 
     override fun measureAndLayout() {
@@ -1774,6 +1826,10 @@
     override fun onSemanticsChange() {
     }
 
+    override fun onLayoutChange(layoutNode: LayoutNode) {
+        layoutChangeCount++
+    }
+
     override var measureIteration: Long = 0
     override val viewConfiguration: ViewConfiguration
         get() = TODO("Not yet implemented")
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/MockSelectable.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/MockSelectable.kt
index ede87ef..baa520a 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/MockSelectable.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/MockSelectable.kt
@@ -20,7 +20,9 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
 
+@OptIn(ExperimentalTextApi::class)
 class MockSelectable(
     var getSelectionValue: Selection? = null,
     var getHandlePositionValue: Offset = Offset.Zero,
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerDragTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerDragTest.kt
index 323adf5..ff19014 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerDragTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerDragTest.kt
@@ -18,10 +18,11 @@
 
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.style.ResolvedTextDirection
+import androidx.compose.ui.unit.IntSize
 import com.google.common.truth.Truth.assertThat
 import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.doReturn
 import com.nhaarman.mockitokotlin2.mock
 import com.nhaarman.mockitokotlin2.spy
 import com.nhaarman.mockitokotlin2.times
@@ -32,19 +33,25 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
+@OptIn(ExperimentalTextApi::class)
 @RunWith(JUnit4::class)
 class SelectionManagerDragTest {
     private val selectionRegistrar = SelectionRegistrarImpl()
     private val selectable = mock<Selectable>()
     private val selectionManager = SelectionManager(selectionRegistrar)
 
-    private val childToLocal_result = Offset(300f, 400f)
+    private val size = IntSize(500, 600)
+    private val globalOffset = Offset(100f, 200f)
+    private val childToLocalOffset = Offset(300f, 400f)
 
-    private val containerLayoutCoordinates = mock<LayoutCoordinates> {
-        on { isAttached } doReturn true
-        on { childToLocal(child = any(), childLocal = Offset(any())) } doAnswer
-            childToLocal_result
-    }
+    private val containerLayoutCoordinates = spy(
+        MockCoordinates(
+            size = size,
+            globalOffset = globalOffset,
+            childToLocalOffset = childToLocalOffset,
+            isAttached = true
+        )
+    )
 
     private val startSelectable = mock<Selectable> {
         on { getHandlePosition(any(), any()) } doAnswer Offset.Zero
@@ -133,8 +140,9 @@
     @Test
     fun handleDragObserver_onDrag_startHandle_reuse_endHandle_calls_getSelection_change_selection
     () {
+        val startOffset = Offset(30f, 50f)
         val dragDistance = Offset(100f, 100f)
-        selectionManager.handleDragObserver(isStartHandle = true).onStart(Offset.Zero)
+        selectionManager.handleDragObserver(isStartHandle = true).onStart(startOffset)
 
         val result = selectionManager.handleDragObserver(isStartHandle = true).onDrag(dragDistance)
 
@@ -145,8 +153,8 @@
             )
         verify(selectable, times(1))
             .getSelection(
-                startPosition = childToLocal_result + dragDistance,
-                endPosition = childToLocal_result,
+                startPosition = childToLocalOffset + dragDistance,
+                endPosition = childToLocalOffset,
                 containerLayoutCoordinates = selectionManager.requireContainerCoordinates(),
                 longPress = false,
                 isStartHandle = true,
@@ -158,10 +166,11 @@
     }
 
     @Test
-    fun handleDragObserver_onDrag_endHandle_resue_startHandle_calls_getSelection_change_selection
+    fun handleDragObserver_onDrag_endHandle_reuse_startHandle_calls_getSelection_change_selection
     () {
+        val startOffset = Offset(30f, 50f)
         val dragDistance = Offset(100f, 100f)
-        selectionManager.handleDragObserver(isStartHandle = false).onStart(Offset.Zero)
+        selectionManager.handleDragObserver(isStartHandle = false).onStart(startOffset)
 
         val result = selectionManager.handleDragObserver(isStartHandle = false).onDrag(dragDistance)
 
@@ -172,8 +181,8 @@
             )
         verify(selectable, times(1))
             .getSelection(
-                startPosition = childToLocal_result,
-                endPosition = childToLocal_result + dragDistance,
+                startPosition = childToLocalOffset,
+                endPosition = childToLocalOffset + dragDistance,
                 containerLayoutCoordinates = selectionManager.requireContainerCoordinates(),
                 longPress = false,
                 isStartHandle = false,
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerTest.kt
index ff70baa..ba89a5f 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionManagerTest.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.platform.ClipboardManager
 import androidx.compose.ui.platform.TextToolbar
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.length
 import androidx.compose.ui.text.style.ResolvedTextDirection
 import androidx.compose.ui.text.subSequence
@@ -42,6 +43,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
+@OptIn(ExperimentalTextApi::class)
 @RunWith(JUnit4::class)
 class SelectionManagerTest {
     private val selectionRegistrar = spy(SelectionRegistrarImpl())
@@ -423,4 +425,43 @@
             times(1)
         ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
     }
+
+    @Test
+    fun notifySelectableChange_clears_selection() {
+        val fakeSelection =
+            Selection(
+                start = Selection.AnchorInfo(
+                    direction = ResolvedTextDirection.Ltr,
+                    offset = 0,
+                    selectable = startSelectable
+                ),
+                end = Selection.AnchorInfo(
+                    direction = ResolvedTextDirection.Ltr,
+                    offset = 5,
+                    selectable = endSelectable
+                )
+            )
+        var selection: Selection? = fakeSelection
+        val lambda: (Selection?) -> Unit = { selection = it }
+        val spyLambda = spy(lambda)
+        selectionManager.onSelectionChange = spyLambda
+        selectionManager.selection = fakeSelection
+
+        selectionRegistrar.notifySelectableChange(selectable)
+
+        verify(selectable, times(1))
+            .getSelection(
+                startPosition = Offset(-1f, -1f),
+                endPosition = Offset(-1f, -1f),
+                containerLayoutCoordinates = selectionManager.requireContainerCoordinates(),
+                longPress = false,
+                previousSelection = fakeSelection
+            )
+        assertThat(selection).isNull()
+        verify(spyLambda, times(1)).invoke(null)
+        verify(
+            hapticFeedback,
+            times(1)
+        ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
+    }
 }
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionRegistrarImplTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionRegistrarImplTest.kt
index 130db56..4bf9891a 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionRegistrarImplTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionRegistrarImplTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.ExperimentalTextApi
 import com.google.common.truth.Truth.assertThat
 import com.nhaarman.mockitokotlin2.mock
 import com.nhaarman.mockitokotlin2.whenever
@@ -25,6 +26,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
+@OptIn(ExperimentalTextApi::class)
 @RunWith(JUnit4::class)
 class SelectionRegistrarImplTest {
     @Test
@@ -188,7 +190,7 @@
         assertThat(selectionRegistrar.sorted).isTrue()
 
         // Act.
-        selectionRegistrar.onPositionChange()
+        selectionRegistrar.notifyPositionChange()
 
         // Assert.
         assertThat(selectionRegistrar.sorted).isFalse()
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionTest.kt
index 5febaab..6afcab9 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/selection/SelectionTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.selection
 
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.style.ResolvedTextDirection
 import com.google.common.truth.Truth.assertThat
@@ -24,6 +25,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
+@OptIn(ExperimentalTextApi::class)
 @RunWith(JUnit4::class)
 class SelectionTest {
     @Test
diff --git a/concurrent/futures/src/main/java/androidx/concurrent/futures/CallbackToFutureAdapter.java b/concurrent/futures/src/main/java/androidx/concurrent/futures/CallbackToFutureAdapter.java
index 1590b89..67ed8ae 100644
--- a/concurrent/futures/src/main/java/androidx/concurrent/futures/CallbackToFutureAdapter.java
+++ b/concurrent/futures/src/main/java/androidx/concurrent/futures/CallbackToFutureAdapter.java
@@ -327,6 +327,7 @@
         // toString intentionally left omitted, so that if the tag object (which holds this object
         // as a field) includes it in its toString, we won't infinitely recurse.
 
+        @SuppressWarnings("deprecation")
         @Override
         protected void finalize() {
             SafeFuture<T> localFuture = future;
diff --git a/concurrent/futures/src/test/java/androidx/concurrent/futures/AbstractResolvableFutureTest.java b/concurrent/futures/src/test/java/androidx/concurrent/futures/AbstractResolvableFutureTest.java
index d24822bf..0f36ad0 100644
--- a/concurrent/futures/src/test/java/androidx/concurrent/futures/AbstractResolvableFutureTest.java
+++ b/concurrent/futures/src/test/java/androidx/concurrent/futures/AbstractResolvableFutureTest.java
@@ -25,8 +25,14 @@
 import com.google.common.collect.Range;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.Atomics;
-import com.google.common.util.concurrent.Uninterruptibles;
 import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.junit.internal.runners.JUnit38ClassRunner;
+import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -48,12 +54,6 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.LockSupport;
 
-import junit.framework.AssertionFailedError;
-import junit.framework.TestCase;
-
-import org.junit.internal.runners.JUnit38ClassRunner;
-import org.junit.runner.RunWith;
-
 /**
  * Tests for {@link AbstractResolvableFuture}.
  *
@@ -232,7 +232,7 @@
      * ranges
      * derived from observing how much time actually passed for various operations.
      */
-    @SuppressWarnings({"DeprecatedThreadMethods", "ThreadPriorityCheck"})
+    @SuppressWarnings({"DeprecatedThreadMethods", "ThreadPriorityCheck", "deprecation"})
     public void testToString_delayedTimeout() throws Exception {
         TimedWaiterThread thread =
                 new TimedWaiterThread(new AbstractResolvableFuture<Object>() {
diff --git a/concurrent/futures/src/test/java/androidx/concurrent/futures/CallbackToFutureAdapterTest.java b/concurrent/futures/src/test/java/androidx/concurrent/futures/CallbackToFutureAdapterTest.java
index 5e15221..971d3ed 100644
--- a/concurrent/futures/src/test/java/androidx/concurrent/futures/CallbackToFutureAdapterTest.java
+++ b/concurrent/futures/src/test/java/androidx/concurrent/futures/CallbackToFutureAdapterTest.java
@@ -111,6 +111,7 @@
      */
     private static void createUnreachableLatchFinalizer(final CountDownLatch latch) {
         new Object() {
+            @SuppressWarnings("deprecation")
             @Override
             protected void finalize() {
                 latch.countDown();
diff --git a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/ContentPagerTest.java b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/ContentPagerTest.java
index f8a66e7..7b5f3c7 100644
--- a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/ContentPagerTest.java
+++ b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/ContentPagerTest.java
@@ -26,7 +26,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import android.app.Activity;
 import android.content.ContentResolver;
 import android.database.Cursor;
 import android.net.Uri;
@@ -38,7 +37,6 @@
 import androidx.contentpager.content.ContentPager.ContentCallback;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -60,8 +58,10 @@
     private TestContentCallback mCallback;
     private ContentPager mPager;
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule(TestActivity.class);
+    public androidx.test.rule.ActivityTestRule<TestActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(TestActivity.class);
 
     @Before
     public void setUp() {
diff --git a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/LoaderQueryRunnerTest.java b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/LoaderQueryRunnerTest.java
index 51643a0..a6eaaa8 100644
--- a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/LoaderQueryRunnerTest.java
+++ b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/LoaderQueryRunnerTest.java
@@ -22,11 +22,11 @@
 import android.app.Activity;
 import android.database.Cursor;
 
+import androidx.annotation.NonNull;
 import androidx.contentpager.content.ContentPager.ContentCallback;
 import androidx.contentpager.content.ContentPager.QueryRunner;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -37,8 +37,10 @@
 @RunWith(AndroidJUnit4.class)
 public class LoaderQueryRunnerTest {
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule(TestActivity.class);
+    public androidx.test.rule.ActivityTestRule<TestActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(TestActivity.class);
 
     private Activity mActivity;
     private QueryRunner mRunner;
@@ -58,7 +60,7 @@
 
         ContentCallback dummyContentCallback = new ContentCallback() {
             @Override
-            public void onCursorReady(Query query, Cursor cursor) {
+            public void onCursorReady(@NonNull Query query, Cursor cursor) {
                 // Nothing to see here. Move along.
             }
         };
diff --git a/contentpager/contentpager/src/main/java/androidx/contentpager/content/LoaderQueryRunner.java b/contentpager/contentpager/src/main/java/androidx/contentpager/content/LoaderQueryRunner.java
index 5e054d9..7054cfe 100644
--- a/contentpager/contentpager/src/main/java/androidx/contentpager/content/LoaderQueryRunner.java
+++ b/contentpager/contentpager/src/main/java/androidx/contentpager/content/LoaderQueryRunner.java
@@ -19,7 +19,6 @@
 import static androidx.core.util.Preconditions.checkArgument;
 
 import android.app.LoaderManager;
-import android.app.LoaderManager.LoaderCallbacks;
 import android.content.Context;
 import android.content.Loader;
 import android.database.Cursor;
@@ -48,11 +47,12 @@
     }
 
     @Override
-    @SuppressWarnings("unchecked")  // feels spurious. But can't commit line :80 w/o this.
+    @SuppressWarnings({"unchecked", "deprecation"})
     public void query(final @NonNull Query query, @NonNull final Callback callback) {
         if (DEBUG) Log.d(TAG, "Handling query: " + query);
 
-        LoaderCallbacks callbacks = new LoaderCallbacks<Cursor>() {
+        android.app.LoaderManager.LoaderCallbacks<Cursor> callbacks =
+                new android.app.LoaderManager.LoaderCallbacks<Cursor>() {
             @Override
             public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
                 if (DEBUG) Log.i(TAG, "Loading results for query: " + query);
diff --git a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutNestedScrollingParentTest.java b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutNestedScrollingParentTest.java
index 4f55c81..c7fce80 100644
--- a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutNestedScrollingParentTest.java
+++ b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutNestedScrollingParentTest.java
@@ -248,6 +248,7 @@
         assertThat(consumed, is(new int[]{24, 225}));
     }
 
+    @SuppressWarnings("unchecked")
     private static CoordinatorLayout.Behavior<View> setupMockBehavior() {
         CoordinatorLayout.Behavior<View> mockBehavior = mock(CoordinatorLayout.Behavior.class);
         when(mockBehavior.onStartNestedScroll(
diff --git a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutSortTest.java b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutSortTest.java
index 7cda115c..666de71 100644
--- a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutSortTest.java
+++ b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutSortTest.java
@@ -36,6 +36,7 @@
 import java.util.Collection;
 import java.util.List;
 
+@SuppressWarnings("unchecked")
 @RunWith(Parameterized.class)
 @SmallTest
 public class CoordinatorLayoutSortTest {
diff --git a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTest.java b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTest.java
index 1e7336e..548f4ba 100644
--- a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTest.java
+++ b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTest.java
@@ -69,6 +69,7 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
+@SuppressWarnings("unchecked")
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class CoordinatorLayoutTest {
diff --git a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/DirectedAcyclicGraphTest.java b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/DirectedAcyclicGraphTest.java
index c7726a7..9040367 100644
--- a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/DirectedAcyclicGraphTest.java
+++ b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/DirectedAcyclicGraphTest.java
@@ -82,6 +82,7 @@
         mGraph.addEdge(node, edge);
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void test_getIncomingEdges() {
         final TestNode node = new TestNode("node");
diff --git a/core/README.md b/core/README.md
index d92c2f4..8ffb0b8 100644
--- a/core/README.md
+++ b/core/README.md
@@ -4,7 +4,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/core)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/core/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/core/)
 
 [Reference documentation](https://developer.android.com/reference/androidx/classes.html)
 
diff --git a/core/core-ktx/api/1.5.0-beta01.txt b/core/core-ktx/api/1.5.0-beta01.txt
new file mode 100644
index 0000000..ea0fa5f
--- /dev/null
+++ b/core/core-ktx/api/1.5.0-beta01.txt
@@ -0,0 +1,606 @@
+// Signature format: 4.0
+package androidx.core.animation {
+
+  public final class AnimatorKt {
+    method public static inline android.animation.Animator.AnimatorListener addListener(android.animation.Animator, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onEnd, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onStart, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onCancel, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onRepeat);
+    method @RequiresApi(19) public static inline android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onResume, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onPause);
+    method public static inline android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentValuesKt {
+    method public static android.content.ContentValues contentValuesOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class ContextKt {
+    method public static inline <reified T> T! getSystemService(android.content.Context);
+    method public static inline void withStyledAttributes(android.content.Context, optional android.util.AttributeSet? set, int[] attrs, optional @AttrRes int defStyleAttr, optional @StyleRes int defStyleRes, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+    method public static inline void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+  }
+
+  public final class SharedPreferencesKt {
+    method public static inline void edit(android.content.SharedPreferences, optional boolean commit, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content.res {
+
+  public final class TypedArrayKt {
+    method public static boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @ColorInt public static int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @RequiresApi(26) public static android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @AnyRes public static int getResourceIdOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence![] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static inline <R> R! use(android.content.res.TypedArray, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,? extends R> block);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorKt {
+    method public static inline byte[]? getBlobOrNull(android.database.Cursor, int index);
+    method public static inline Double? getDoubleOrNull(android.database.Cursor, int index);
+    method public static inline Float? getFloatOrNull(android.database.Cursor, int index);
+    method public static inline Integer? getIntOrNull(android.database.Cursor, int index);
+    method public static inline Long? getLongOrNull(android.database.Cursor, int index);
+    method public static inline Short? getShortOrNull(android.database.Cursor, int index);
+    method public static inline String? getStringOrNull(android.database.Cursor, int index);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteDatabaseKt {
+    method public static inline <T> T! transaction(android.database.sqlite.SQLiteDatabase, optional boolean exclusive, kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapKt {
+    method public static inline android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline operator boolean contains(android.graphics.Bitmap, android.graphics.Point p);
+    method public static inline operator boolean contains(android.graphics.Bitmap, android.graphics.PointF p);
+    method public static inline android.graphics.Bitmap createBitmap(int width, int height, optional android.graphics.Bitmap.Config config);
+    method @RequiresApi(26) public static inline android.graphics.Bitmap createBitmap(int width, int height, optional android.graphics.Bitmap.Config config, optional boolean hasAlpha, optional android.graphics.ColorSpace colorSpace);
+    method public static inline operator int get(android.graphics.Bitmap, int x, int y);
+    method public static inline android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, optional boolean filter);
+    method public static inline operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+  }
+
+  public final class CanvasKt {
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.Rect clipRect, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.RectF clipRect, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, int left, int top, int right, int bottom, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, float left, float top, float right, float bottom, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.Path clipPath, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withMatrix(android.graphics.Canvas, optional android.graphics.Matrix matrix, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withRotation(android.graphics.Canvas, optional float degrees, optional float pivotX, optional float pivotY, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withScale(android.graphics.Canvas, optional float x, optional float y, optional float pivotX, optional float pivotY, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withSkew(android.graphics.Canvas, optional float x, optional float y, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withTranslation(android.graphics.Canvas, optional float x, optional float y, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class ColorKt {
+    method @RequiresApi(26) public static inline operator float component1(android.graphics.Color);
+    method public static inline operator int component1(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component1(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component2(android.graphics.Color);
+    method public static inline operator int component2(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component2(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component3(android.graphics.Color);
+    method public static inline operator int component3(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component3(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component4(android.graphics.Color);
+    method public static inline operator int component4(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component4(@ColorLong long);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorInt int, android.graphics.ColorSpace.Named colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorInt int, android.graphics.ColorSpace colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorLong long, android.graphics.ColorSpace.Named colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorLong long, android.graphics.ColorSpace colorSpace);
+    method @RequiresApi(26) public static inline infix android.graphics.Color convertTo(android.graphics.Color, android.graphics.ColorSpace.Named colorSpace);
+    method @RequiresApi(26) public static inline infix android.graphics.Color convertTo(android.graphics.Color, android.graphics.ColorSpace colorSpace);
+    method public static inline int getAlpha(int);
+    method @RequiresApi(26) public static inline float getAlpha(long);
+    method public static inline int getBlue(int);
+    method @RequiresApi(26) public static inline float getBlue(long);
+    method @RequiresApi(26) public static inline android.graphics.ColorSpace getColorSpace(long);
+    method public static inline int getGreen(int);
+    method @RequiresApi(26) public static inline float getGreen(long);
+    method @RequiresApi(26) public static inline float getLuminance(int);
+    method @RequiresApi(26) public static inline float getLuminance(long);
+    method public static inline int getRed(int);
+    method @RequiresApi(26) public static inline float getRed(long);
+    method @RequiresApi(26) public static inline boolean isSrgb(long);
+    method @RequiresApi(26) public static inline boolean isWideGamut(long);
+    method @RequiresApi(26) public static operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+    method @RequiresApi(26) public static inline android.graphics.Color toColor(@ColorInt int);
+    method @RequiresApi(26) public static inline android.graphics.Color toColor(@ColorLong long);
+    method @ColorInt @RequiresApi(26) public static inline int toColorInt(@ColorLong long);
+    method @ColorInt public static inline int toColorInt(String);
+    method @ColorLong @RequiresApi(26) public static inline long toColorLong(@ColorInt int);
+  }
+
+  public final class ImageDecoderKt {
+    method @RequiresApi(28) public static inline android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source, kotlin.jvm.functions.Function3<? super android.graphics.ImageDecoder,? super android.graphics.ImageDecoder.ImageInfo,? super android.graphics.ImageDecoder.Source,kotlin.Unit> action);
+    method @RequiresApi(28) public static inline android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, kotlin.jvm.functions.Function3<? super android.graphics.ImageDecoder,? super android.graphics.ImageDecoder.ImageInfo,? super android.graphics.ImageDecoder.Source,kotlin.Unit> action);
+  }
+
+  public final class MatrixKt {
+    method public static android.graphics.Matrix rotationMatrix(float degrees, optional float px, optional float py);
+    method public static android.graphics.Matrix scaleMatrix(optional float sx, optional float sy);
+    method public static inline operator android.graphics.Matrix times(android.graphics.Matrix, android.graphics.Matrix m);
+    method public static android.graphics.Matrix translationMatrix(optional float tx, optional float ty);
+    method public static inline float[] values(android.graphics.Matrix);
+  }
+
+  public final class PaintKt {
+    method public static inline boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat? blendModeCompat);
+  }
+
+  public final class PathKt {
+    method @RequiresApi(19) public static inline infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(26) public static Iterable<androidx.core.graphics.PathSegment> flatten(android.graphics.Path, optional float error);
+    method @RequiresApi(19) public static inline operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+  }
+
+  public final class PictureKt {
+    method public static inline android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class PointKt {
+    method public static inline operator int component1(android.graphics.Point);
+    method public static inline operator float component1(android.graphics.PointF);
+    method public static inline operator int component2(android.graphics.Point);
+    method public static inline operator float component2(android.graphics.PointF);
+    method public static inline operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+    method public static inline operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Point minus(android.graphics.Point, int xy);
+    method public static inline operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+    method public static inline operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+    method public static inline operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Point plus(android.graphics.Point, int xy);
+    method public static inline operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+    method public static inline android.graphics.Point toPoint(android.graphics.PointF);
+    method public static inline android.graphics.PointF toPointF(android.graphics.Point);
+    method public static inline operator android.graphics.Point unaryMinus(android.graphics.Point);
+    method public static inline operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+  }
+
+  public final class PorterDuffKt {
+    method public static inline android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+    method public static inline android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final class RectKt {
+    method public static inline infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator int component1(android.graphics.Rect);
+    method public static inline operator float component1(android.graphics.RectF);
+    method public static inline operator int component2(android.graphics.Rect);
+    method public static inline operator float component2(android.graphics.RectF);
+    method public static inline operator int component3(android.graphics.Rect);
+    method public static inline operator float component3(android.graphics.RectF);
+    method public static inline operator int component4(android.graphics.Rect);
+    method public static inline operator float component4(android.graphics.RectF);
+    method public static inline operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+    method public static inline operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+    method public static inline operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+    method public static inline operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+    method public static inline operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static inline infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static inline operator android.graphics.Rect times(android.graphics.Rect, int factor);
+    method public static inline operator android.graphics.RectF times(android.graphics.RectF, int factor);
+    method public static inline operator android.graphics.RectF times(android.graphics.RectF, float factor);
+    method public static inline android.graphics.Rect toRect(android.graphics.RectF);
+    method public static inline android.graphics.RectF toRectF(android.graphics.Rect);
+    method public static inline android.graphics.Region toRegion(android.graphics.Rect);
+    method public static inline android.graphics.Region toRegion(android.graphics.RectF);
+    method public static inline android.graphics.RectF transform(android.graphics.RectF, android.graphics.Matrix m);
+    method public static inline infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+  }
+
+  public final class RegionKt {
+    method public static inline infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator boolean contains(android.graphics.Region, android.graphics.Point p);
+    method public static inline void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+    method public static operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+    method public static inline operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region not(android.graphics.Region);
+    method public static inline infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region unaryMinus(android.graphics.Region);
+    method public static inline infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+  }
+
+  public final class ShaderKt {
+    method public static inline void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class BitmapDrawableKt {
+    method public static inline android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+  }
+
+  public final class ColorDrawableKt {
+    method public static inline android.graphics.drawable.ColorDrawable toDrawable(@ColorInt int);
+    method @RequiresApi(26) public static inline android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+  }
+
+  public final class DrawableKt {
+    method public static android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, optional @Px int width, optional @Px int height, optional android.graphics.Bitmap.Config? config);
+    method public static void updateBounds(android.graphics.drawable.Drawable, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+  }
+
+  public final class IconKt {
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(android.net.Uri);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(byte[]);
+  }
+
+}
+
+package androidx.core.location {
+
+  public final class LocationKt {
+    method public static inline operator double component1(android.location.Location);
+    method public static inline operator double component2(android.location.Location);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class UriKt {
+    method public static java.io.File toFile(android.net.Uri);
+    method public static inline android.net.Uri toUri(String);
+    method public static inline android.net.Uri toUri(java.io.File);
+  }
+
+}
+
+package androidx.core.os {
+
+  public final class BundleKt {
+    method public static android.os.Bundle bundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class HandlerKt {
+    method public static inline Runnable postAtTime(android.os.Handler, long uptimeMillis, optional Object? token, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static inline Runnable postDelayed(android.os.Handler, long delayInMillis, optional Object? token, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+  }
+
+  public final class PersistableBundleKt {
+    method @RequiresApi(21) public static android.os.PersistableBundle persistableBundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class TraceKt {
+    method @Deprecated public static inline <T> T! trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class CharSequenceKt {
+    method public static inline boolean isDigitsOnly(CharSequence);
+    method public static inline int trimmedLength(CharSequence);
+  }
+
+  public final class HtmlKt {
+    method public static inline android.text.Spanned parseAsHtml(String, optional int flags, optional android.text.Html.ImageGetter? imageGetter, optional android.text.Html.TagHandler? tagHandler);
+    method public static inline String toHtml(android.text.Spanned, optional int option);
+  }
+
+  public final class LocaleKt {
+    method @RequiresApi(17) public static inline int getLayoutDirection(java.util.Locale);
+  }
+
+  public final class SpannableStringBuilderKt {
+    method public static inline android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object![] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object span, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder scale(android.text.SpannableStringBuilder, float proportion, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder strikeThrough(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder subscript(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder superscript(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+  }
+
+  public final class SpannableStringKt {
+    method public static inline void clearSpans(android.text.Spannable);
+    method public static inline operator void set(android.text.Spannable, int start, int end, Object span);
+    method public static inline operator void set(android.text.Spannable, kotlin.ranges.IntRange range, Object span);
+    method public static inline android.text.Spannable toSpannable(CharSequence);
+  }
+
+  public final class SpannedStringKt {
+    method public static inline <reified T> T![]! getSpans(android.text.Spanned, optional int start, optional int end);
+    method public static inline android.text.Spanned toSpanned(CharSequence);
+  }
+
+  public final class StringKt {
+    method public static inline String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.transition {
+
+  public final class TransitionKt {
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener addListener(android.transition.Transition, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onEnd, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onStart, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onCancel, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onResume, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onPause);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.util {
+
+  public final class AtomicFileKt {
+    method @RequiresApi(17) public static inline byte[] readBytes(android.util.AtomicFile);
+    method @RequiresApi(17) public static String readText(android.util.AtomicFile, optional java.nio.charset.Charset charset);
+    method @RequiresApi(17) public static inline void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+    method @RequiresApi(17) public static void writeBytes(android.util.AtomicFile, byte[] array);
+    method @RequiresApi(17) public static void writeText(android.util.AtomicFile, String text, optional java.nio.charset.Charset charset);
+  }
+
+  public final class HalfKt {
+    method @RequiresApi(26) public static inline android.util.Half toHalf(@HalfFloat short);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(float);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(double);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(String);
+  }
+
+  public final class LongSparseArrayKt {
+    method @RequiresApi(16) public static inline operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static inline <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static inline <T> boolean containsValue(android.util.LongSparseArray<T>, T? value);
+    method @RequiresApi(16) public static inline <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Long,? super T,kotlin.Unit> action);
+    method @RequiresApi(16) public static inline <T> T! getOrDefault(android.util.LongSparseArray<T>, long key, T? defaultValue);
+    method @RequiresApi(16) public static inline <T> T! getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method @RequiresApi(16) public static inline <T> int getSize(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static inline <T> boolean isEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static inline <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> boolean remove(android.util.LongSparseArray<T>, long key, T? value);
+    method @RequiresApi(16) public static inline operator <T> void set(android.util.LongSparseArray<T>, long key, T? value);
+    method @RequiresApi(16) public static <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+  }
+
+  public final class LruCacheKt {
+    method public static inline <K, V> android.util.LruCache<K,V> lruCache(int maxSize, optional kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf, optional kotlin.jvm.functions.Function1<? super K,? extends V> create, optional kotlin.jvm.functions.Function4<? super java.lang.Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved);
+  }
+
+  public final class PairKt {
+    method public static inline operator <F, S> F! component1(androidx.core.util.Pair<F,S>);
+    method public static inline operator <F, S> F! component1(android.util.Pair<F,S>);
+    method public static inline operator <F, S> S! component2(androidx.core.util.Pair<F,S>);
+    method public static inline operator <F, S> S! component2(android.util.Pair<F,S>);
+    method public static inline <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+    method public static inline <F, S> androidx.core.util.Pair<F,S> toAndroidXPair(kotlin.Pair<? extends F,? extends S>);
+    method public static inline <F, S> kotlin.Pair<F,S> toKotlinPair(androidx.core.util.Pair<F,S>);
+    method public static inline <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+  }
+
+  public final class RangeKt {
+    method @RequiresApi(21) public static inline infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static inline operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+    method @RequiresApi(21) public static inline operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static inline infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+  }
+
+  public final class SizeKt {
+    method @RequiresApi(21) public static inline operator int component1(android.util.Size);
+    method @RequiresApi(21) public static inline operator float component1(android.util.SizeF);
+    method @RequiresApi(21) public static inline operator int component2(android.util.Size);
+    method @RequiresApi(21) public static inline operator float component2(android.util.SizeF);
+  }
+
+  public final class SparseArrayKt {
+    method public static inline operator <T> boolean contains(android.util.SparseArray<T>, int key);
+    method public static inline <T> boolean containsKey(android.util.SparseArray<T>, int key);
+    method public static inline <T> boolean containsValue(android.util.SparseArray<T>, T? value);
+    method public static inline <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
+    method public static inline <T> T! getOrDefault(android.util.SparseArray<T>, int key, T? defaultValue);
+    method public static inline <T> T! getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public static inline <T> int getSize(android.util.SparseArray<T>);
+    method public static inline <T> boolean isEmpty(android.util.SparseArray<T>);
+    method public static inline <T> boolean isNotEmpty(android.util.SparseArray<T>);
+    method public static <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+    method public static operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> boolean remove(android.util.SparseArray<T>, int key, T? value);
+    method public static inline operator <T> void set(android.util.SparseArray<T>, int key, T? value);
+    method public static <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+  }
+
+  public final class SparseBooleanArrayKt {
+    method public static inline operator boolean contains(android.util.SparseBooleanArray, int key);
+    method public static inline boolean containsKey(android.util.SparseBooleanArray, int key);
+    method public static inline boolean containsValue(android.util.SparseBooleanArray, boolean value);
+    method public static inline void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Boolean,kotlin.Unit> action);
+    method public static inline boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+    method public static inline boolean getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<java.lang.Boolean> defaultValue);
+    method public static inline int getSize(android.util.SparseBooleanArray);
+    method public static inline boolean isEmpty(android.util.SparseBooleanArray);
+    method public static inline boolean isNotEmpty(android.util.SparseBooleanArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+    method public static operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+    method public static inline operator void set(android.util.SparseBooleanArray, int key, boolean value);
+    method public static kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+  }
+
+  public final class SparseIntArrayKt {
+    method public static inline operator boolean contains(android.util.SparseIntArray, int key);
+    method public static inline boolean containsKey(android.util.SparseIntArray, int key);
+    method public static inline boolean containsValue(android.util.SparseIntArray, int value);
+    method public static inline void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static inline int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+    method public static inline int getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
+    method public static inline int getSize(android.util.SparseIntArray);
+    method public static inline boolean isEmpty(android.util.SparseIntArray);
+    method public static inline boolean isNotEmpty(android.util.SparseIntArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+    method public static operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static boolean remove(android.util.SparseIntArray, int key, int value);
+    method public static inline operator void set(android.util.SparseIntArray, int key, int value);
+    method public static kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+  }
+
+  public final class SparseLongArrayKt {
+    method @RequiresApi(18) public static inline operator boolean contains(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static inline boolean containsKey(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static inline boolean containsValue(android.util.SparseLongArray, long value);
+    method @RequiresApi(18) public static inline void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Long,kotlin.Unit> action);
+    method @RequiresApi(18) public static inline long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+    method @RequiresApi(18) public static inline long getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
+    method @RequiresApi(18) public static inline int getSize(android.util.SparseLongArray);
+    method @RequiresApi(18) public static inline boolean isEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static inline boolean isNotEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+    method @RequiresApi(18) public static operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static boolean remove(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static inline operator void set(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+  }
+
+}
+
+package androidx.core.view {
+
+  public final class MenuKt {
+    method public static operator boolean contains(android.view.Menu, android.view.MenuItem item);
+    method public static inline void forEach(android.view.Menu, kotlin.jvm.functions.Function1<? super android.view.MenuItem,kotlin.Unit> action);
+    method public static inline void forEachIndexed(android.view.Menu, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.MenuItem,kotlin.Unit> action);
+    method public static inline operator android.view.MenuItem get(android.view.Menu, int index);
+    method public static kotlin.sequences.Sequence<android.view.MenuItem> getChildren(android.view.Menu);
+    method public static inline int getSize(android.view.Menu);
+    method public static inline boolean isEmpty(android.view.Menu);
+    method public static inline boolean isNotEmpty(android.view.Menu);
+    method public static operator java.util.Iterator<android.view.MenuItem> iterator(android.view.Menu);
+    method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+  }
+
+  public final class ViewGroupKt {
+    method public static inline operator boolean contains(android.view.ViewGroup, android.view.View view);
+    method public static inline void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.View,kotlin.Unit> action);
+    method public static operator android.view.View get(android.view.ViewGroup, int index);
+    method public static kotlin.sequences.Sequence<android.view.View> getChildren(android.view.ViewGroup);
+    method public static kotlin.sequences.Sequence<android.view.View> getDescendants(android.view.ViewGroup);
+    method public static inline int getSize(android.view.ViewGroup);
+    method public static inline boolean isEmpty(android.view.ViewGroup);
+    method public static inline boolean isNotEmpty(android.view.ViewGroup);
+    method public static operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+    method public static inline operator void minusAssign(android.view.ViewGroup, android.view.View view);
+    method public static inline operator void plusAssign(android.view.ViewGroup, android.view.View view);
+    method public static inline void setMargins(android.view.ViewGroup.MarginLayoutParams, @Px int size);
+    method public static inline void updateMargins(android.view.ViewGroup.MarginLayoutParams, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+    method @RequiresApi(17) public static inline void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, optional @Px int start, optional @Px int top, optional @Px int end, optional @Px int bottom);
+  }
+
+  public final class ViewKt {
+    method public static inline void doOnAttach(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnDetach(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline androidx.core.view.OneShotPreDrawListener doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static android.graphics.Bitmap drawToBitmap(android.view.View, optional android.graphics.Bitmap.Config config);
+    method public static kotlin.sequences.Sequence<android.view.View> getAllViews(android.view.View);
+    method public static kotlin.sequences.Sequence<android.view.ViewParent> getAncestors(android.view.View);
+    method public static inline int getMarginBottom(android.view.View);
+    method public static inline int getMarginEnd(android.view.View);
+    method public static inline int getMarginLeft(android.view.View);
+    method public static inline int getMarginRight(android.view.View);
+    method public static inline int getMarginStart(android.view.View);
+    method public static inline int getMarginTop(android.view.View);
+    method public static inline boolean isGone(android.view.View);
+    method public static inline boolean isInvisible(android.view.View);
+    method public static inline boolean isVisible(android.view.View);
+    method public static inline Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method @RequiresApi(16) public static inline Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static inline void setGone(android.view.View, boolean value);
+    method public static inline void setInvisible(android.view.View, boolean value);
+    method public static inline void setPadding(android.view.View, @Px int size);
+    method public static inline void setVisible(android.view.View, boolean value);
+    method public static inline void updateLayoutParams(android.view.View, kotlin.jvm.functions.Function1<? super android.view.ViewGroup.LayoutParams,kotlin.Unit> block);
+    method public static inline <reified T extends android.view.ViewGroup.LayoutParams> void updateLayoutParams(android.view.View, kotlin.jvm.functions.Function1<? super T,? extends kotlin.Unit> block);
+    method public static inline void updatePadding(android.view.View, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+    method @RequiresApi(17) public static inline void updatePaddingRelative(android.view.View, optional @Px int start, optional @Px int top, optional @Px int end, optional @Px int bottom);
+  }
+
+}
+
+package androidx.core.widget {
+
+  public final class TextViewKt {
+    method public static inline android.text.TextWatcher addTextChangedListener(android.widget.TextView, optional kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> beforeTextChanged, optional kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> onTextChanged, optional kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit> afterTextChanged);
+    method public static inline android.text.TextWatcher doAfterTextChanged(android.widget.TextView, kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit> action);
+    method public static inline android.text.TextWatcher doBeforeTextChanged(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static inline android.text.TextWatcher doOnTextChanged(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+  }
+
+}
+
diff --git a/core/core-ktx/api/public_plus_experimental_1.5.0-beta01.txt b/core/core-ktx/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..ea0fa5f
--- /dev/null
+++ b/core/core-ktx/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,606 @@
+// Signature format: 4.0
+package androidx.core.animation {
+
+  public final class AnimatorKt {
+    method public static inline android.animation.Animator.AnimatorListener addListener(android.animation.Animator, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onEnd, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onStart, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onCancel, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onRepeat);
+    method @RequiresApi(19) public static inline android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onResume, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onPause);
+    method public static inline android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentValuesKt {
+    method public static android.content.ContentValues contentValuesOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class ContextKt {
+    method public static inline <reified T> T! getSystemService(android.content.Context);
+    method public static inline void withStyledAttributes(android.content.Context, optional android.util.AttributeSet? set, int[] attrs, optional @AttrRes int defStyleAttr, optional @StyleRes int defStyleRes, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+    method public static inline void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+  }
+
+  public final class SharedPreferencesKt {
+    method public static inline void edit(android.content.SharedPreferences, optional boolean commit, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content.res {
+
+  public final class TypedArrayKt {
+    method public static boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @ColorInt public static int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @RequiresApi(26) public static android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @AnyRes public static int getResourceIdOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence![] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static inline <R> R! use(android.content.res.TypedArray, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,? extends R> block);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorKt {
+    method public static inline byte[]? getBlobOrNull(android.database.Cursor, int index);
+    method public static inline Double? getDoubleOrNull(android.database.Cursor, int index);
+    method public static inline Float? getFloatOrNull(android.database.Cursor, int index);
+    method public static inline Integer? getIntOrNull(android.database.Cursor, int index);
+    method public static inline Long? getLongOrNull(android.database.Cursor, int index);
+    method public static inline Short? getShortOrNull(android.database.Cursor, int index);
+    method public static inline String? getStringOrNull(android.database.Cursor, int index);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteDatabaseKt {
+    method public static inline <T> T! transaction(android.database.sqlite.SQLiteDatabase, optional boolean exclusive, kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapKt {
+    method public static inline android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline operator boolean contains(android.graphics.Bitmap, android.graphics.Point p);
+    method public static inline operator boolean contains(android.graphics.Bitmap, android.graphics.PointF p);
+    method public static inline android.graphics.Bitmap createBitmap(int width, int height, optional android.graphics.Bitmap.Config config);
+    method @RequiresApi(26) public static inline android.graphics.Bitmap createBitmap(int width, int height, optional android.graphics.Bitmap.Config config, optional boolean hasAlpha, optional android.graphics.ColorSpace colorSpace);
+    method public static inline operator int get(android.graphics.Bitmap, int x, int y);
+    method public static inline android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, optional boolean filter);
+    method public static inline operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+  }
+
+  public final class CanvasKt {
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.Rect clipRect, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.RectF clipRect, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, int left, int top, int right, int bottom, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, float left, float top, float right, float bottom, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.Path clipPath, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withMatrix(android.graphics.Canvas, optional android.graphics.Matrix matrix, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withRotation(android.graphics.Canvas, optional float degrees, optional float pivotX, optional float pivotY, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withScale(android.graphics.Canvas, optional float x, optional float y, optional float pivotX, optional float pivotY, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withSkew(android.graphics.Canvas, optional float x, optional float y, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withTranslation(android.graphics.Canvas, optional float x, optional float y, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class ColorKt {
+    method @RequiresApi(26) public static inline operator float component1(android.graphics.Color);
+    method public static inline operator int component1(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component1(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component2(android.graphics.Color);
+    method public static inline operator int component2(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component2(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component3(android.graphics.Color);
+    method public static inline operator int component3(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component3(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component4(android.graphics.Color);
+    method public static inline operator int component4(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component4(@ColorLong long);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorInt int, android.graphics.ColorSpace.Named colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorInt int, android.graphics.ColorSpace colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorLong long, android.graphics.ColorSpace.Named colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorLong long, android.graphics.ColorSpace colorSpace);
+    method @RequiresApi(26) public static inline infix android.graphics.Color convertTo(android.graphics.Color, android.graphics.ColorSpace.Named colorSpace);
+    method @RequiresApi(26) public static inline infix android.graphics.Color convertTo(android.graphics.Color, android.graphics.ColorSpace colorSpace);
+    method public static inline int getAlpha(int);
+    method @RequiresApi(26) public static inline float getAlpha(long);
+    method public static inline int getBlue(int);
+    method @RequiresApi(26) public static inline float getBlue(long);
+    method @RequiresApi(26) public static inline android.graphics.ColorSpace getColorSpace(long);
+    method public static inline int getGreen(int);
+    method @RequiresApi(26) public static inline float getGreen(long);
+    method @RequiresApi(26) public static inline float getLuminance(int);
+    method @RequiresApi(26) public static inline float getLuminance(long);
+    method public static inline int getRed(int);
+    method @RequiresApi(26) public static inline float getRed(long);
+    method @RequiresApi(26) public static inline boolean isSrgb(long);
+    method @RequiresApi(26) public static inline boolean isWideGamut(long);
+    method @RequiresApi(26) public static operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+    method @RequiresApi(26) public static inline android.graphics.Color toColor(@ColorInt int);
+    method @RequiresApi(26) public static inline android.graphics.Color toColor(@ColorLong long);
+    method @ColorInt @RequiresApi(26) public static inline int toColorInt(@ColorLong long);
+    method @ColorInt public static inline int toColorInt(String);
+    method @ColorLong @RequiresApi(26) public static inline long toColorLong(@ColorInt int);
+  }
+
+  public final class ImageDecoderKt {
+    method @RequiresApi(28) public static inline android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source, kotlin.jvm.functions.Function3<? super android.graphics.ImageDecoder,? super android.graphics.ImageDecoder.ImageInfo,? super android.graphics.ImageDecoder.Source,kotlin.Unit> action);
+    method @RequiresApi(28) public static inline android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, kotlin.jvm.functions.Function3<? super android.graphics.ImageDecoder,? super android.graphics.ImageDecoder.ImageInfo,? super android.graphics.ImageDecoder.Source,kotlin.Unit> action);
+  }
+
+  public final class MatrixKt {
+    method public static android.graphics.Matrix rotationMatrix(float degrees, optional float px, optional float py);
+    method public static android.graphics.Matrix scaleMatrix(optional float sx, optional float sy);
+    method public static inline operator android.graphics.Matrix times(android.graphics.Matrix, android.graphics.Matrix m);
+    method public static android.graphics.Matrix translationMatrix(optional float tx, optional float ty);
+    method public static inline float[] values(android.graphics.Matrix);
+  }
+
+  public final class PaintKt {
+    method public static inline boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat? blendModeCompat);
+  }
+
+  public final class PathKt {
+    method @RequiresApi(19) public static inline infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(26) public static Iterable<androidx.core.graphics.PathSegment> flatten(android.graphics.Path, optional float error);
+    method @RequiresApi(19) public static inline operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+  }
+
+  public final class PictureKt {
+    method public static inline android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class PointKt {
+    method public static inline operator int component1(android.graphics.Point);
+    method public static inline operator float component1(android.graphics.PointF);
+    method public static inline operator int component2(android.graphics.Point);
+    method public static inline operator float component2(android.graphics.PointF);
+    method public static inline operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+    method public static inline operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Point minus(android.graphics.Point, int xy);
+    method public static inline operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+    method public static inline operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+    method public static inline operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Point plus(android.graphics.Point, int xy);
+    method public static inline operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+    method public static inline android.graphics.Point toPoint(android.graphics.PointF);
+    method public static inline android.graphics.PointF toPointF(android.graphics.Point);
+    method public static inline operator android.graphics.Point unaryMinus(android.graphics.Point);
+    method public static inline operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+  }
+
+  public final class PorterDuffKt {
+    method public static inline android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+    method public static inline android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final class RectKt {
+    method public static inline infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator int component1(android.graphics.Rect);
+    method public static inline operator float component1(android.graphics.RectF);
+    method public static inline operator int component2(android.graphics.Rect);
+    method public static inline operator float component2(android.graphics.RectF);
+    method public static inline operator int component3(android.graphics.Rect);
+    method public static inline operator float component3(android.graphics.RectF);
+    method public static inline operator int component4(android.graphics.Rect);
+    method public static inline operator float component4(android.graphics.RectF);
+    method public static inline operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+    method public static inline operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+    method public static inline operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+    method public static inline operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+    method public static inline operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static inline infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static inline operator android.graphics.Rect times(android.graphics.Rect, int factor);
+    method public static inline operator android.graphics.RectF times(android.graphics.RectF, int factor);
+    method public static inline operator android.graphics.RectF times(android.graphics.RectF, float factor);
+    method public static inline android.graphics.Rect toRect(android.graphics.RectF);
+    method public static inline android.graphics.RectF toRectF(android.graphics.Rect);
+    method public static inline android.graphics.Region toRegion(android.graphics.Rect);
+    method public static inline android.graphics.Region toRegion(android.graphics.RectF);
+    method public static inline android.graphics.RectF transform(android.graphics.RectF, android.graphics.Matrix m);
+    method public static inline infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+  }
+
+  public final class RegionKt {
+    method public static inline infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator boolean contains(android.graphics.Region, android.graphics.Point p);
+    method public static inline void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+    method public static operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+    method public static inline operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region not(android.graphics.Region);
+    method public static inline infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region unaryMinus(android.graphics.Region);
+    method public static inline infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+  }
+
+  public final class ShaderKt {
+    method public static inline void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class BitmapDrawableKt {
+    method public static inline android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+  }
+
+  public final class ColorDrawableKt {
+    method public static inline android.graphics.drawable.ColorDrawable toDrawable(@ColorInt int);
+    method @RequiresApi(26) public static inline android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+  }
+
+  public final class DrawableKt {
+    method public static android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, optional @Px int width, optional @Px int height, optional android.graphics.Bitmap.Config? config);
+    method public static void updateBounds(android.graphics.drawable.Drawable, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+  }
+
+  public final class IconKt {
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(android.net.Uri);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(byte[]);
+  }
+
+}
+
+package androidx.core.location {
+
+  public final class LocationKt {
+    method public static inline operator double component1(android.location.Location);
+    method public static inline operator double component2(android.location.Location);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class UriKt {
+    method public static java.io.File toFile(android.net.Uri);
+    method public static inline android.net.Uri toUri(String);
+    method public static inline android.net.Uri toUri(java.io.File);
+  }
+
+}
+
+package androidx.core.os {
+
+  public final class BundleKt {
+    method public static android.os.Bundle bundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class HandlerKt {
+    method public static inline Runnable postAtTime(android.os.Handler, long uptimeMillis, optional Object? token, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static inline Runnable postDelayed(android.os.Handler, long delayInMillis, optional Object? token, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+  }
+
+  public final class PersistableBundleKt {
+    method @RequiresApi(21) public static android.os.PersistableBundle persistableBundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class TraceKt {
+    method @Deprecated public static inline <T> T! trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class CharSequenceKt {
+    method public static inline boolean isDigitsOnly(CharSequence);
+    method public static inline int trimmedLength(CharSequence);
+  }
+
+  public final class HtmlKt {
+    method public static inline android.text.Spanned parseAsHtml(String, optional int flags, optional android.text.Html.ImageGetter? imageGetter, optional android.text.Html.TagHandler? tagHandler);
+    method public static inline String toHtml(android.text.Spanned, optional int option);
+  }
+
+  public final class LocaleKt {
+    method @RequiresApi(17) public static inline int getLayoutDirection(java.util.Locale);
+  }
+
+  public final class SpannableStringBuilderKt {
+    method public static inline android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object![] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object span, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder scale(android.text.SpannableStringBuilder, float proportion, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder strikeThrough(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder subscript(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder superscript(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+  }
+
+  public final class SpannableStringKt {
+    method public static inline void clearSpans(android.text.Spannable);
+    method public static inline operator void set(android.text.Spannable, int start, int end, Object span);
+    method public static inline operator void set(android.text.Spannable, kotlin.ranges.IntRange range, Object span);
+    method public static inline android.text.Spannable toSpannable(CharSequence);
+  }
+
+  public final class SpannedStringKt {
+    method public static inline <reified T> T![]! getSpans(android.text.Spanned, optional int start, optional int end);
+    method public static inline android.text.Spanned toSpanned(CharSequence);
+  }
+
+  public final class StringKt {
+    method public static inline String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.transition {
+
+  public final class TransitionKt {
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener addListener(android.transition.Transition, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onEnd, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onStart, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onCancel, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onResume, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onPause);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.util {
+
+  public final class AtomicFileKt {
+    method @RequiresApi(17) public static inline byte[] readBytes(android.util.AtomicFile);
+    method @RequiresApi(17) public static String readText(android.util.AtomicFile, optional java.nio.charset.Charset charset);
+    method @RequiresApi(17) public static inline void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+    method @RequiresApi(17) public static void writeBytes(android.util.AtomicFile, byte[] array);
+    method @RequiresApi(17) public static void writeText(android.util.AtomicFile, String text, optional java.nio.charset.Charset charset);
+  }
+
+  public final class HalfKt {
+    method @RequiresApi(26) public static inline android.util.Half toHalf(@HalfFloat short);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(float);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(double);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(String);
+  }
+
+  public final class LongSparseArrayKt {
+    method @RequiresApi(16) public static inline operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static inline <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static inline <T> boolean containsValue(android.util.LongSparseArray<T>, T? value);
+    method @RequiresApi(16) public static inline <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Long,? super T,kotlin.Unit> action);
+    method @RequiresApi(16) public static inline <T> T! getOrDefault(android.util.LongSparseArray<T>, long key, T? defaultValue);
+    method @RequiresApi(16) public static inline <T> T! getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method @RequiresApi(16) public static inline <T> int getSize(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static inline <T> boolean isEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static inline <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> boolean remove(android.util.LongSparseArray<T>, long key, T? value);
+    method @RequiresApi(16) public static inline operator <T> void set(android.util.LongSparseArray<T>, long key, T? value);
+    method @RequiresApi(16) public static <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+  }
+
+  public final class LruCacheKt {
+    method public static inline <K, V> android.util.LruCache<K,V> lruCache(int maxSize, optional kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf, optional kotlin.jvm.functions.Function1<? super K,? extends V> create, optional kotlin.jvm.functions.Function4<? super java.lang.Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved);
+  }
+
+  public final class PairKt {
+    method public static inline operator <F, S> F! component1(androidx.core.util.Pair<F,S>);
+    method public static inline operator <F, S> F! component1(android.util.Pair<F,S>);
+    method public static inline operator <F, S> S! component2(androidx.core.util.Pair<F,S>);
+    method public static inline operator <F, S> S! component2(android.util.Pair<F,S>);
+    method public static inline <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+    method public static inline <F, S> androidx.core.util.Pair<F,S> toAndroidXPair(kotlin.Pair<? extends F,? extends S>);
+    method public static inline <F, S> kotlin.Pair<F,S> toKotlinPair(androidx.core.util.Pair<F,S>);
+    method public static inline <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+  }
+
+  public final class RangeKt {
+    method @RequiresApi(21) public static inline infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static inline operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+    method @RequiresApi(21) public static inline operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static inline infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+  }
+
+  public final class SizeKt {
+    method @RequiresApi(21) public static inline operator int component1(android.util.Size);
+    method @RequiresApi(21) public static inline operator float component1(android.util.SizeF);
+    method @RequiresApi(21) public static inline operator int component2(android.util.Size);
+    method @RequiresApi(21) public static inline operator float component2(android.util.SizeF);
+  }
+
+  public final class SparseArrayKt {
+    method public static inline operator <T> boolean contains(android.util.SparseArray<T>, int key);
+    method public static inline <T> boolean containsKey(android.util.SparseArray<T>, int key);
+    method public static inline <T> boolean containsValue(android.util.SparseArray<T>, T? value);
+    method public static inline <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
+    method public static inline <T> T! getOrDefault(android.util.SparseArray<T>, int key, T? defaultValue);
+    method public static inline <T> T! getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public static inline <T> int getSize(android.util.SparseArray<T>);
+    method public static inline <T> boolean isEmpty(android.util.SparseArray<T>);
+    method public static inline <T> boolean isNotEmpty(android.util.SparseArray<T>);
+    method public static <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+    method public static operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> boolean remove(android.util.SparseArray<T>, int key, T? value);
+    method public static inline operator <T> void set(android.util.SparseArray<T>, int key, T? value);
+    method public static <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+  }
+
+  public final class SparseBooleanArrayKt {
+    method public static inline operator boolean contains(android.util.SparseBooleanArray, int key);
+    method public static inline boolean containsKey(android.util.SparseBooleanArray, int key);
+    method public static inline boolean containsValue(android.util.SparseBooleanArray, boolean value);
+    method public static inline void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Boolean,kotlin.Unit> action);
+    method public static inline boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+    method public static inline boolean getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<java.lang.Boolean> defaultValue);
+    method public static inline int getSize(android.util.SparseBooleanArray);
+    method public static inline boolean isEmpty(android.util.SparseBooleanArray);
+    method public static inline boolean isNotEmpty(android.util.SparseBooleanArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+    method public static operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+    method public static inline operator void set(android.util.SparseBooleanArray, int key, boolean value);
+    method public static kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+  }
+
+  public final class SparseIntArrayKt {
+    method public static inline operator boolean contains(android.util.SparseIntArray, int key);
+    method public static inline boolean containsKey(android.util.SparseIntArray, int key);
+    method public static inline boolean containsValue(android.util.SparseIntArray, int value);
+    method public static inline void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static inline int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+    method public static inline int getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
+    method public static inline int getSize(android.util.SparseIntArray);
+    method public static inline boolean isEmpty(android.util.SparseIntArray);
+    method public static inline boolean isNotEmpty(android.util.SparseIntArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+    method public static operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static boolean remove(android.util.SparseIntArray, int key, int value);
+    method public static inline operator void set(android.util.SparseIntArray, int key, int value);
+    method public static kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+  }
+
+  public final class SparseLongArrayKt {
+    method @RequiresApi(18) public static inline operator boolean contains(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static inline boolean containsKey(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static inline boolean containsValue(android.util.SparseLongArray, long value);
+    method @RequiresApi(18) public static inline void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Long,kotlin.Unit> action);
+    method @RequiresApi(18) public static inline long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+    method @RequiresApi(18) public static inline long getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
+    method @RequiresApi(18) public static inline int getSize(android.util.SparseLongArray);
+    method @RequiresApi(18) public static inline boolean isEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static inline boolean isNotEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+    method @RequiresApi(18) public static operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static boolean remove(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static inline operator void set(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+  }
+
+}
+
+package androidx.core.view {
+
+  public final class MenuKt {
+    method public static operator boolean contains(android.view.Menu, android.view.MenuItem item);
+    method public static inline void forEach(android.view.Menu, kotlin.jvm.functions.Function1<? super android.view.MenuItem,kotlin.Unit> action);
+    method public static inline void forEachIndexed(android.view.Menu, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.MenuItem,kotlin.Unit> action);
+    method public static inline operator android.view.MenuItem get(android.view.Menu, int index);
+    method public static kotlin.sequences.Sequence<android.view.MenuItem> getChildren(android.view.Menu);
+    method public static inline int getSize(android.view.Menu);
+    method public static inline boolean isEmpty(android.view.Menu);
+    method public static inline boolean isNotEmpty(android.view.Menu);
+    method public static operator java.util.Iterator<android.view.MenuItem> iterator(android.view.Menu);
+    method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+  }
+
+  public final class ViewGroupKt {
+    method public static inline operator boolean contains(android.view.ViewGroup, android.view.View view);
+    method public static inline void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.View,kotlin.Unit> action);
+    method public static operator android.view.View get(android.view.ViewGroup, int index);
+    method public static kotlin.sequences.Sequence<android.view.View> getChildren(android.view.ViewGroup);
+    method public static kotlin.sequences.Sequence<android.view.View> getDescendants(android.view.ViewGroup);
+    method public static inline int getSize(android.view.ViewGroup);
+    method public static inline boolean isEmpty(android.view.ViewGroup);
+    method public static inline boolean isNotEmpty(android.view.ViewGroup);
+    method public static operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+    method public static inline operator void minusAssign(android.view.ViewGroup, android.view.View view);
+    method public static inline operator void plusAssign(android.view.ViewGroup, android.view.View view);
+    method public static inline void setMargins(android.view.ViewGroup.MarginLayoutParams, @Px int size);
+    method public static inline void updateMargins(android.view.ViewGroup.MarginLayoutParams, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+    method @RequiresApi(17) public static inline void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, optional @Px int start, optional @Px int top, optional @Px int end, optional @Px int bottom);
+  }
+
+  public final class ViewKt {
+    method public static inline void doOnAttach(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnDetach(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline androidx.core.view.OneShotPreDrawListener doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static android.graphics.Bitmap drawToBitmap(android.view.View, optional android.graphics.Bitmap.Config config);
+    method public static kotlin.sequences.Sequence<android.view.View> getAllViews(android.view.View);
+    method public static kotlin.sequences.Sequence<android.view.ViewParent> getAncestors(android.view.View);
+    method public static inline int getMarginBottom(android.view.View);
+    method public static inline int getMarginEnd(android.view.View);
+    method public static inline int getMarginLeft(android.view.View);
+    method public static inline int getMarginRight(android.view.View);
+    method public static inline int getMarginStart(android.view.View);
+    method public static inline int getMarginTop(android.view.View);
+    method public static inline boolean isGone(android.view.View);
+    method public static inline boolean isInvisible(android.view.View);
+    method public static inline boolean isVisible(android.view.View);
+    method public static inline Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method @RequiresApi(16) public static inline Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static inline void setGone(android.view.View, boolean value);
+    method public static inline void setInvisible(android.view.View, boolean value);
+    method public static inline void setPadding(android.view.View, @Px int size);
+    method public static inline void setVisible(android.view.View, boolean value);
+    method public static inline void updateLayoutParams(android.view.View, kotlin.jvm.functions.Function1<? super android.view.ViewGroup.LayoutParams,kotlin.Unit> block);
+    method public static inline <reified T extends android.view.ViewGroup.LayoutParams> void updateLayoutParams(android.view.View, kotlin.jvm.functions.Function1<? super T,? extends kotlin.Unit> block);
+    method public static inline void updatePadding(android.view.View, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+    method @RequiresApi(17) public static inline void updatePaddingRelative(android.view.View, optional @Px int start, optional @Px int top, optional @Px int end, optional @Px int bottom);
+  }
+
+}
+
+package androidx.core.widget {
+
+  public final class TextViewKt {
+    method public static inline android.text.TextWatcher addTextChangedListener(android.widget.TextView, optional kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> beforeTextChanged, optional kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> onTextChanged, optional kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit> afterTextChanged);
+    method public static inline android.text.TextWatcher doAfterTextChanged(android.widget.TextView, kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit> action);
+    method public static inline android.text.TextWatcher doBeforeTextChanged(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static inline android.text.TextWatcher doOnTextChanged(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+  }
+
+}
+
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutDirections.kt b/core/core-ktx/api/res-1.5.0-beta01.txt
similarity index 100%
rename from compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutDirections.kt
rename to core/core-ktx/api/res-1.5.0-beta01.txt
diff --git a/core/core-ktx/api/restricted_1.5.0-beta01.txt b/core/core-ktx/api/restricted_1.5.0-beta01.txt
new file mode 100644
index 0000000..ea0fa5f
--- /dev/null
+++ b/core/core-ktx/api/restricted_1.5.0-beta01.txt
@@ -0,0 +1,606 @@
+// Signature format: 4.0
+package androidx.core.animation {
+
+  public final class AnimatorKt {
+    method public static inline android.animation.Animator.AnimatorListener addListener(android.animation.Animator, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onEnd, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onStart, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onCancel, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onRepeat);
+    method @RequiresApi(19) public static inline android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onResume, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onPause);
+    method public static inline android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentValuesKt {
+    method public static android.content.ContentValues contentValuesOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class ContextKt {
+    method public static inline <reified T> T! getSystemService(android.content.Context);
+    method public static inline void withStyledAttributes(android.content.Context, optional android.util.AttributeSet? set, int[] attrs, optional @AttrRes int defStyleAttr, optional @StyleRes int defStyleRes, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+    method public static inline void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+  }
+
+  public final class SharedPreferencesKt {
+    method public static inline void edit(android.content.SharedPreferences, optional boolean commit, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content.res {
+
+  public final class TypedArrayKt {
+    method public static boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @ColorInt public static int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @RequiresApi(26) public static android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @AnyRes public static int getResourceIdOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence![] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static inline <R> R! use(android.content.res.TypedArray, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,? extends R> block);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorKt {
+    method public static inline byte[]? getBlobOrNull(android.database.Cursor, int index);
+    method public static inline Double? getDoubleOrNull(android.database.Cursor, int index);
+    method public static inline Float? getFloatOrNull(android.database.Cursor, int index);
+    method public static inline Integer? getIntOrNull(android.database.Cursor, int index);
+    method public static inline Long? getLongOrNull(android.database.Cursor, int index);
+    method public static inline Short? getShortOrNull(android.database.Cursor, int index);
+    method public static inline String? getStringOrNull(android.database.Cursor, int index);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteDatabaseKt {
+    method public static inline <T> T! transaction(android.database.sqlite.SQLiteDatabase, optional boolean exclusive, kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapKt {
+    method public static inline android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline operator boolean contains(android.graphics.Bitmap, android.graphics.Point p);
+    method public static inline operator boolean contains(android.graphics.Bitmap, android.graphics.PointF p);
+    method public static inline android.graphics.Bitmap createBitmap(int width, int height, optional android.graphics.Bitmap.Config config);
+    method @RequiresApi(26) public static inline android.graphics.Bitmap createBitmap(int width, int height, optional android.graphics.Bitmap.Config config, optional boolean hasAlpha, optional android.graphics.ColorSpace colorSpace);
+    method public static inline operator int get(android.graphics.Bitmap, int x, int y);
+    method public static inline android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, optional boolean filter);
+    method public static inline operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+  }
+
+  public final class CanvasKt {
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.Rect clipRect, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.RectF clipRect, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, int left, int top, int right, int bottom, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, float left, float top, float right, float bottom, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.Path clipPath, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withMatrix(android.graphics.Canvas, optional android.graphics.Matrix matrix, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withRotation(android.graphics.Canvas, optional float degrees, optional float pivotX, optional float pivotY, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withScale(android.graphics.Canvas, optional float x, optional float y, optional float pivotX, optional float pivotY, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withSkew(android.graphics.Canvas, optional float x, optional float y, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withTranslation(android.graphics.Canvas, optional float x, optional float y, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class ColorKt {
+    method @RequiresApi(26) public static inline operator float component1(android.graphics.Color);
+    method public static inline operator int component1(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component1(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component2(android.graphics.Color);
+    method public static inline operator int component2(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component2(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component3(android.graphics.Color);
+    method public static inline operator int component3(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component3(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component4(android.graphics.Color);
+    method public static inline operator int component4(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component4(@ColorLong long);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorInt int, android.graphics.ColorSpace.Named colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorInt int, android.graphics.ColorSpace colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorLong long, android.graphics.ColorSpace.Named colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorLong long, android.graphics.ColorSpace colorSpace);
+    method @RequiresApi(26) public static inline infix android.graphics.Color convertTo(android.graphics.Color, android.graphics.ColorSpace.Named colorSpace);
+    method @RequiresApi(26) public static inline infix android.graphics.Color convertTo(android.graphics.Color, android.graphics.ColorSpace colorSpace);
+    method public static inline int getAlpha(int);
+    method @RequiresApi(26) public static inline float getAlpha(long);
+    method public static inline int getBlue(int);
+    method @RequiresApi(26) public static inline float getBlue(long);
+    method @RequiresApi(26) public static inline android.graphics.ColorSpace getColorSpace(long);
+    method public static inline int getGreen(int);
+    method @RequiresApi(26) public static inline float getGreen(long);
+    method @RequiresApi(26) public static inline float getLuminance(int);
+    method @RequiresApi(26) public static inline float getLuminance(long);
+    method public static inline int getRed(int);
+    method @RequiresApi(26) public static inline float getRed(long);
+    method @RequiresApi(26) public static inline boolean isSrgb(long);
+    method @RequiresApi(26) public static inline boolean isWideGamut(long);
+    method @RequiresApi(26) public static operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+    method @RequiresApi(26) public static inline android.graphics.Color toColor(@ColorInt int);
+    method @RequiresApi(26) public static inline android.graphics.Color toColor(@ColorLong long);
+    method @ColorInt @RequiresApi(26) public static inline int toColorInt(@ColorLong long);
+    method @ColorInt public static inline int toColorInt(String);
+    method @ColorLong @RequiresApi(26) public static inline long toColorLong(@ColorInt int);
+  }
+
+  public final class ImageDecoderKt {
+    method @RequiresApi(28) public static inline android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source, kotlin.jvm.functions.Function3<? super android.graphics.ImageDecoder,? super android.graphics.ImageDecoder.ImageInfo,? super android.graphics.ImageDecoder.Source,kotlin.Unit> action);
+    method @RequiresApi(28) public static inline android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, kotlin.jvm.functions.Function3<? super android.graphics.ImageDecoder,? super android.graphics.ImageDecoder.ImageInfo,? super android.graphics.ImageDecoder.Source,kotlin.Unit> action);
+  }
+
+  public final class MatrixKt {
+    method public static android.graphics.Matrix rotationMatrix(float degrees, optional float px, optional float py);
+    method public static android.graphics.Matrix scaleMatrix(optional float sx, optional float sy);
+    method public static inline operator android.graphics.Matrix times(android.graphics.Matrix, android.graphics.Matrix m);
+    method public static android.graphics.Matrix translationMatrix(optional float tx, optional float ty);
+    method public static inline float[] values(android.graphics.Matrix);
+  }
+
+  public final class PaintKt {
+    method public static inline boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat? blendModeCompat);
+  }
+
+  public final class PathKt {
+    method @RequiresApi(19) public static inline infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(26) public static Iterable<androidx.core.graphics.PathSegment> flatten(android.graphics.Path, optional float error);
+    method @RequiresApi(19) public static inline operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+  }
+
+  public final class PictureKt {
+    method public static inline android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class PointKt {
+    method public static inline operator int component1(android.graphics.Point);
+    method public static inline operator float component1(android.graphics.PointF);
+    method public static inline operator int component2(android.graphics.Point);
+    method public static inline operator float component2(android.graphics.PointF);
+    method public static inline operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+    method public static inline operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Point minus(android.graphics.Point, int xy);
+    method public static inline operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+    method public static inline operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+    method public static inline operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Point plus(android.graphics.Point, int xy);
+    method public static inline operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+    method public static inline android.graphics.Point toPoint(android.graphics.PointF);
+    method public static inline android.graphics.PointF toPointF(android.graphics.Point);
+    method public static inline operator android.graphics.Point unaryMinus(android.graphics.Point);
+    method public static inline operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+  }
+
+  public final class PorterDuffKt {
+    method public static inline android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+    method public static inline android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final class RectKt {
+    method public static inline infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator int component1(android.graphics.Rect);
+    method public static inline operator float component1(android.graphics.RectF);
+    method public static inline operator int component2(android.graphics.Rect);
+    method public static inline operator float component2(android.graphics.RectF);
+    method public static inline operator int component3(android.graphics.Rect);
+    method public static inline operator float component3(android.graphics.RectF);
+    method public static inline operator int component4(android.graphics.Rect);
+    method public static inline operator float component4(android.graphics.RectF);
+    method public static inline operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+    method public static inline operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+    method public static inline operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+    method public static inline operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+    method public static inline operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static inline infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static inline operator android.graphics.Rect times(android.graphics.Rect, int factor);
+    method public static inline operator android.graphics.RectF times(android.graphics.RectF, int factor);
+    method public static inline operator android.graphics.RectF times(android.graphics.RectF, float factor);
+    method public static inline android.graphics.Rect toRect(android.graphics.RectF);
+    method public static inline android.graphics.RectF toRectF(android.graphics.Rect);
+    method public static inline android.graphics.Region toRegion(android.graphics.Rect);
+    method public static inline android.graphics.Region toRegion(android.graphics.RectF);
+    method public static inline android.graphics.RectF transform(android.graphics.RectF, android.graphics.Matrix m);
+    method public static inline infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+  }
+
+  public final class RegionKt {
+    method public static inline infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator boolean contains(android.graphics.Region, android.graphics.Point p);
+    method public static inline void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+    method public static operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+    method public static inline operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region not(android.graphics.Region);
+    method public static inline infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region unaryMinus(android.graphics.Region);
+    method public static inline infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+  }
+
+  public final class ShaderKt {
+    method public static inline void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class BitmapDrawableKt {
+    method public static inline android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+  }
+
+  public final class ColorDrawableKt {
+    method public static inline android.graphics.drawable.ColorDrawable toDrawable(@ColorInt int);
+    method @RequiresApi(26) public static inline android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+  }
+
+  public final class DrawableKt {
+    method public static android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, optional @Px int width, optional @Px int height, optional android.graphics.Bitmap.Config? config);
+    method public static void updateBounds(android.graphics.drawable.Drawable, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+  }
+
+  public final class IconKt {
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(android.net.Uri);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(byte[]);
+  }
+
+}
+
+package androidx.core.location {
+
+  public final class LocationKt {
+    method public static inline operator double component1(android.location.Location);
+    method public static inline operator double component2(android.location.Location);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class UriKt {
+    method public static java.io.File toFile(android.net.Uri);
+    method public static inline android.net.Uri toUri(String);
+    method public static inline android.net.Uri toUri(java.io.File);
+  }
+
+}
+
+package androidx.core.os {
+
+  public final class BundleKt {
+    method public static android.os.Bundle bundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class HandlerKt {
+    method public static inline Runnable postAtTime(android.os.Handler, long uptimeMillis, optional Object? token, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static inline Runnable postDelayed(android.os.Handler, long delayInMillis, optional Object? token, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+  }
+
+  public final class PersistableBundleKt {
+    method @RequiresApi(21) public static android.os.PersistableBundle persistableBundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class TraceKt {
+    method @Deprecated public static inline <T> T! trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class CharSequenceKt {
+    method public static inline boolean isDigitsOnly(CharSequence);
+    method public static inline int trimmedLength(CharSequence);
+  }
+
+  public final class HtmlKt {
+    method public static inline android.text.Spanned parseAsHtml(String, optional int flags, optional android.text.Html.ImageGetter? imageGetter, optional android.text.Html.TagHandler? tagHandler);
+    method public static inline String toHtml(android.text.Spanned, optional int option);
+  }
+
+  public final class LocaleKt {
+    method @RequiresApi(17) public static inline int getLayoutDirection(java.util.Locale);
+  }
+
+  public final class SpannableStringBuilderKt {
+    method public static inline android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object![] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object span, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder scale(android.text.SpannableStringBuilder, float proportion, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder strikeThrough(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder subscript(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder superscript(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+  }
+
+  public final class SpannableStringKt {
+    method public static inline void clearSpans(android.text.Spannable);
+    method public static inline operator void set(android.text.Spannable, int start, int end, Object span);
+    method public static inline operator void set(android.text.Spannable, kotlin.ranges.IntRange range, Object span);
+    method public static inline android.text.Spannable toSpannable(CharSequence);
+  }
+
+  public final class SpannedStringKt {
+    method public static inline <reified T> T![]! getSpans(android.text.Spanned, optional int start, optional int end);
+    method public static inline android.text.Spanned toSpanned(CharSequence);
+  }
+
+  public final class StringKt {
+    method public static inline String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.transition {
+
+  public final class TransitionKt {
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener addListener(android.transition.Transition, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onEnd, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onStart, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onCancel, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onResume, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onPause);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.util {
+
+  public final class AtomicFileKt {
+    method @RequiresApi(17) public static inline byte[] readBytes(android.util.AtomicFile);
+    method @RequiresApi(17) public static String readText(android.util.AtomicFile, optional java.nio.charset.Charset charset);
+    method @RequiresApi(17) public static inline void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+    method @RequiresApi(17) public static void writeBytes(android.util.AtomicFile, byte[] array);
+    method @RequiresApi(17) public static void writeText(android.util.AtomicFile, String text, optional java.nio.charset.Charset charset);
+  }
+
+  public final class HalfKt {
+    method @RequiresApi(26) public static inline android.util.Half toHalf(@HalfFloat short);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(float);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(double);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(String);
+  }
+
+  public final class LongSparseArrayKt {
+    method @RequiresApi(16) public static inline operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static inline <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static inline <T> boolean containsValue(android.util.LongSparseArray<T>, T? value);
+    method @RequiresApi(16) public static inline <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Long,? super T,kotlin.Unit> action);
+    method @RequiresApi(16) public static inline <T> T! getOrDefault(android.util.LongSparseArray<T>, long key, T? defaultValue);
+    method @RequiresApi(16) public static inline <T> T! getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method @RequiresApi(16) public static inline <T> int getSize(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static inline <T> boolean isEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static inline <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> boolean remove(android.util.LongSparseArray<T>, long key, T? value);
+    method @RequiresApi(16) public static inline operator <T> void set(android.util.LongSparseArray<T>, long key, T? value);
+    method @RequiresApi(16) public static <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+  }
+
+  public final class LruCacheKt {
+    method public static inline <K, V> android.util.LruCache<K,V> lruCache(int maxSize, optional kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf, optional kotlin.jvm.functions.Function1<? super K,? extends V> create, optional kotlin.jvm.functions.Function4<? super java.lang.Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved);
+  }
+
+  public final class PairKt {
+    method public static inline operator <F, S> F! component1(androidx.core.util.Pair<F,S>);
+    method public static inline operator <F, S> F! component1(android.util.Pair<F,S>);
+    method public static inline operator <F, S> S! component2(androidx.core.util.Pair<F,S>);
+    method public static inline operator <F, S> S! component2(android.util.Pair<F,S>);
+    method public static inline <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+    method public static inline <F, S> androidx.core.util.Pair<F,S> toAndroidXPair(kotlin.Pair<? extends F,? extends S>);
+    method public static inline <F, S> kotlin.Pair<F,S> toKotlinPair(androidx.core.util.Pair<F,S>);
+    method public static inline <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+  }
+
+  public final class RangeKt {
+    method @RequiresApi(21) public static inline infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static inline operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+    method @RequiresApi(21) public static inline operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static inline infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+  }
+
+  public final class SizeKt {
+    method @RequiresApi(21) public static inline operator int component1(android.util.Size);
+    method @RequiresApi(21) public static inline operator float component1(android.util.SizeF);
+    method @RequiresApi(21) public static inline operator int component2(android.util.Size);
+    method @RequiresApi(21) public static inline operator float component2(android.util.SizeF);
+  }
+
+  public final class SparseArrayKt {
+    method public static inline operator <T> boolean contains(android.util.SparseArray<T>, int key);
+    method public static inline <T> boolean containsKey(android.util.SparseArray<T>, int key);
+    method public static inline <T> boolean containsValue(android.util.SparseArray<T>, T? value);
+    method public static inline <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
+    method public static inline <T> T! getOrDefault(android.util.SparseArray<T>, int key, T? defaultValue);
+    method public static inline <T> T! getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public static inline <T> int getSize(android.util.SparseArray<T>);
+    method public static inline <T> boolean isEmpty(android.util.SparseArray<T>);
+    method public static inline <T> boolean isNotEmpty(android.util.SparseArray<T>);
+    method public static <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+    method public static operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> boolean remove(android.util.SparseArray<T>, int key, T? value);
+    method public static inline operator <T> void set(android.util.SparseArray<T>, int key, T? value);
+    method public static <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+  }
+
+  public final class SparseBooleanArrayKt {
+    method public static inline operator boolean contains(android.util.SparseBooleanArray, int key);
+    method public static inline boolean containsKey(android.util.SparseBooleanArray, int key);
+    method public static inline boolean containsValue(android.util.SparseBooleanArray, boolean value);
+    method public static inline void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Boolean,kotlin.Unit> action);
+    method public static inline boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+    method public static inline boolean getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<java.lang.Boolean> defaultValue);
+    method public static inline int getSize(android.util.SparseBooleanArray);
+    method public static inline boolean isEmpty(android.util.SparseBooleanArray);
+    method public static inline boolean isNotEmpty(android.util.SparseBooleanArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+    method public static operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+    method public static inline operator void set(android.util.SparseBooleanArray, int key, boolean value);
+    method public static kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+  }
+
+  public final class SparseIntArrayKt {
+    method public static inline operator boolean contains(android.util.SparseIntArray, int key);
+    method public static inline boolean containsKey(android.util.SparseIntArray, int key);
+    method public static inline boolean containsValue(android.util.SparseIntArray, int value);
+    method public static inline void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static inline int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+    method public static inline int getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
+    method public static inline int getSize(android.util.SparseIntArray);
+    method public static inline boolean isEmpty(android.util.SparseIntArray);
+    method public static inline boolean isNotEmpty(android.util.SparseIntArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+    method public static operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static boolean remove(android.util.SparseIntArray, int key, int value);
+    method public static inline operator void set(android.util.SparseIntArray, int key, int value);
+    method public static kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+  }
+
+  public final class SparseLongArrayKt {
+    method @RequiresApi(18) public static inline operator boolean contains(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static inline boolean containsKey(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static inline boolean containsValue(android.util.SparseLongArray, long value);
+    method @RequiresApi(18) public static inline void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Long,kotlin.Unit> action);
+    method @RequiresApi(18) public static inline long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+    method @RequiresApi(18) public static inline long getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
+    method @RequiresApi(18) public static inline int getSize(android.util.SparseLongArray);
+    method @RequiresApi(18) public static inline boolean isEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static inline boolean isNotEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+    method @RequiresApi(18) public static operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static boolean remove(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static inline operator void set(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+  }
+
+}
+
+package androidx.core.view {
+
+  public final class MenuKt {
+    method public static operator boolean contains(android.view.Menu, android.view.MenuItem item);
+    method public static inline void forEach(android.view.Menu, kotlin.jvm.functions.Function1<? super android.view.MenuItem,kotlin.Unit> action);
+    method public static inline void forEachIndexed(android.view.Menu, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.MenuItem,kotlin.Unit> action);
+    method public static inline operator android.view.MenuItem get(android.view.Menu, int index);
+    method public static kotlin.sequences.Sequence<android.view.MenuItem> getChildren(android.view.Menu);
+    method public static inline int getSize(android.view.Menu);
+    method public static inline boolean isEmpty(android.view.Menu);
+    method public static inline boolean isNotEmpty(android.view.Menu);
+    method public static operator java.util.Iterator<android.view.MenuItem> iterator(android.view.Menu);
+    method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+  }
+
+  public final class ViewGroupKt {
+    method public static inline operator boolean contains(android.view.ViewGroup, android.view.View view);
+    method public static inline void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.View,kotlin.Unit> action);
+    method public static operator android.view.View get(android.view.ViewGroup, int index);
+    method public static kotlin.sequences.Sequence<android.view.View> getChildren(android.view.ViewGroup);
+    method public static kotlin.sequences.Sequence<android.view.View> getDescendants(android.view.ViewGroup);
+    method public static inline int getSize(android.view.ViewGroup);
+    method public static inline boolean isEmpty(android.view.ViewGroup);
+    method public static inline boolean isNotEmpty(android.view.ViewGroup);
+    method public static operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+    method public static inline operator void minusAssign(android.view.ViewGroup, android.view.View view);
+    method public static inline operator void plusAssign(android.view.ViewGroup, android.view.View view);
+    method public static inline void setMargins(android.view.ViewGroup.MarginLayoutParams, @Px int size);
+    method public static inline void updateMargins(android.view.ViewGroup.MarginLayoutParams, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+    method @RequiresApi(17) public static inline void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, optional @Px int start, optional @Px int top, optional @Px int end, optional @Px int bottom);
+  }
+
+  public final class ViewKt {
+    method public static inline void doOnAttach(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnDetach(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline androidx.core.view.OneShotPreDrawListener doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static android.graphics.Bitmap drawToBitmap(android.view.View, optional android.graphics.Bitmap.Config config);
+    method public static kotlin.sequences.Sequence<android.view.View> getAllViews(android.view.View);
+    method public static kotlin.sequences.Sequence<android.view.ViewParent> getAncestors(android.view.View);
+    method public static inline int getMarginBottom(android.view.View);
+    method public static inline int getMarginEnd(android.view.View);
+    method public static inline int getMarginLeft(android.view.View);
+    method public static inline int getMarginRight(android.view.View);
+    method public static inline int getMarginStart(android.view.View);
+    method public static inline int getMarginTop(android.view.View);
+    method public static inline boolean isGone(android.view.View);
+    method public static inline boolean isInvisible(android.view.View);
+    method public static inline boolean isVisible(android.view.View);
+    method public static inline Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method @RequiresApi(16) public static inline Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static inline void setGone(android.view.View, boolean value);
+    method public static inline void setInvisible(android.view.View, boolean value);
+    method public static inline void setPadding(android.view.View, @Px int size);
+    method public static inline void setVisible(android.view.View, boolean value);
+    method public static inline void updateLayoutParams(android.view.View, kotlin.jvm.functions.Function1<? super android.view.ViewGroup.LayoutParams,kotlin.Unit> block);
+    method public static inline <reified T extends android.view.ViewGroup.LayoutParams> void updateLayoutParams(android.view.View, kotlin.jvm.functions.Function1<? super T,? extends kotlin.Unit> block);
+    method public static inline void updatePadding(android.view.View, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+    method @RequiresApi(17) public static inline void updatePaddingRelative(android.view.View, optional @Px int start, optional @Px int top, optional @Px int end, optional @Px int bottom);
+  }
+
+}
+
+package androidx.core.widget {
+
+  public final class TextViewKt {
+    method public static inline android.text.TextWatcher addTextChangedListener(android.widget.TextView, optional kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> beforeTextChanged, optional kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> onTextChanged, optional kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit> afterTextChanged);
+    method public static inline android.text.TextWatcher doAfterTextChanged(android.widget.TextView, kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit> action);
+    method public static inline android.text.TextWatcher doBeforeTextChanged(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static inline android.text.TextWatcher doOnTextChanged(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+  }
+
+}
+
diff --git a/core/core-role/README.md b/core/core-role/README.md
index b14b8f1..4247596 100644
--- a/core/core-role/README.md
+++ b/core/core-role/README.md
@@ -6,7 +6,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/core)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/core/core-role/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/core/core-role/)
 
 [Reference documentation](https://developer.android.com/reference/androidx/classes.html)
 
diff --git a/core/core/api/1.5.0-beta01.txt b/core/core/api/1.5.0-beta01.txt
new file mode 100644
index 0000000..9935027
--- /dev/null
+++ b/core/core/api/1.5.0-beta01.txt
@@ -0,0 +1,3505 @@
+// Signature format: 4.0
+package androidx.core.accessibilityservice {
+
+  public final class AccessibilityServiceInfoCompat {
+    method public static String capabilityToString(int);
+    method public static String feedbackTypeToString(int);
+    method public static String? flagToString(int);
+    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static String? loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+  }
+
+}
+
+package androidx.core.app {
+
+  public class ActivityCompat extends androidx.core.content.ContextCompat {
+    ctor protected ActivityCompat();
+    method public static void finishAffinity(android.app.Activity);
+    method public static void finishAfterTransition(android.app.Activity);
+    method public static android.net.Uri? getReferrer(android.app.Activity);
+    method @Deprecated public static boolean invalidateOptionsMenu(android.app.Activity!);
+    method public static void postponeEnterTransition(android.app.Activity);
+    method public static void recreate(android.app.Activity);
+    method public static androidx.core.view.DragAndDropPermissionsCompat? requestDragAndDropPermissions(android.app.Activity!, android.view.DragEvent!);
+    method public static void requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+    method public static <T extends android.view.View> T requireViewById(android.app.Activity, @IdRes int);
+    method public static void setEnterSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+    method public static void setExitSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+    method public static void setLocusContext(android.app.Activity, androidx.core.content.LocusIdCompat?, android.os.Bundle?);
+    method public static void setPermissionCompatDelegate(androidx.core.app.ActivityCompat.PermissionCompatDelegate?);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, String);
+    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle?);
+    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public static void startPostponedEnterTransition(android.app.Activity);
+  }
+
+  public static interface ActivityCompat.OnRequestPermissionsResultCallback {
+    method public void onRequestPermissionsResult(int, String![], int[]);
+  }
+
+  public static interface ActivityCompat.PermissionCompatDelegate {
+    method public boolean onActivityResult(android.app.Activity, @IntRange(from=0) int, int, android.content.Intent?);
+    method public boolean requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+  }
+
+  public final class ActivityManagerCompat {
+    method public static boolean isLowRamDevice(android.app.ActivityManager);
+  }
+
+  public class ActivityOptionsCompat {
+    ctor protected ActivityOptionsCompat();
+    method public android.graphics.Rect? getLaunchBounds();
+    method public static androidx.core.app.ActivityOptionsCompat makeBasic();
+    method public static androidx.core.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, String);
+    method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, androidx.core.util.Pair<android.view.View!,java.lang.String!>!...);
+    method public static androidx.core.app.ActivityOptionsCompat makeTaskLaunchBehind();
+    method public static androidx.core.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+    method public void requestUsageTimeReport(android.app.PendingIntent);
+    method public androidx.core.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect?);
+    method public android.os.Bundle? toBundle();
+    method public void update(androidx.core.app.ActivityOptionsCompat);
+    field public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+    field public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+  }
+
+  public final class AlarmManagerCompat {
+    method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+    method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+  }
+
+  @RequiresApi(28) public class AppComponentFactory extends android.app.AppComponentFactory {
+    ctor public AppComponentFactory();
+    method public final android.app.Activity instantiateActivity(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Activity instantiateActivityCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.app.Application instantiateApplication(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Application instantiateApplicationCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.content.ContentProvider instantiateProvider(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.content.ContentProvider instantiateProviderCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.content.BroadcastReceiver instantiateReceiver(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.content.BroadcastReceiver instantiateReceiverCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.app.Service instantiateService(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Service instantiateServiceCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+  }
+
+  public class AppLaunchChecker {
+    ctor @Deprecated public AppLaunchChecker();
+    method public static boolean hasStartedFromLauncher(android.content.Context);
+    method public static void onActivityCreate(android.app.Activity);
+  }
+
+  public final class AppOpsManagerCompat {
+    method public static int noteOp(android.content.Context, String, int, String);
+    method public static int noteOpNoThrow(android.content.Context, String, int, String);
+    method public static int noteProxyOp(android.content.Context, String, String);
+    method public static int noteProxyOpNoThrow(android.content.Context, String, String);
+    method public static String? permissionToOp(String);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
+    field public static final int MODE_ERRORED = 2; // 0x2
+    field public static final int MODE_IGNORED = 1; // 0x1
+  }
+
+  public final class BundleCompat {
+    method public static android.os.IBinder? getBinder(android.os.Bundle, String?);
+    method public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
+  }
+
+  public class DialogCompat {
+    method public static android.view.View requireViewById(android.app.Dialog, int);
+  }
+
+  public class FrameMetricsAggregator {
+    ctor public FrameMetricsAggregator();
+    ctor public FrameMetricsAggregator(int);
+    method public void add(android.app.Activity);
+    method public android.util.SparseIntArray![]? getMetrics();
+    method public android.util.SparseIntArray![]? remove(android.app.Activity);
+    method public android.util.SparseIntArray![]? reset();
+    method public android.util.SparseIntArray![]? stop();
+    field public static final int ANIMATION_DURATION = 256; // 0x100
+    field public static final int ANIMATION_INDEX = 8; // 0x8
+    field public static final int COMMAND_DURATION = 32; // 0x20
+    field public static final int COMMAND_INDEX = 5; // 0x5
+    field public static final int DELAY_DURATION = 128; // 0x80
+    field public static final int DELAY_INDEX = 7; // 0x7
+    field public static final int DRAW_DURATION = 8; // 0x8
+    field public static final int DRAW_INDEX = 3; // 0x3
+    field public static final int EVERY_DURATION = 511; // 0x1ff
+    field public static final int INPUT_DURATION = 2; // 0x2
+    field public static final int INPUT_INDEX = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 4; // 0x4
+    field public static final int LAYOUT_MEASURE_INDEX = 2; // 0x2
+    field public static final int SWAP_DURATION = 64; // 0x40
+    field public static final int SWAP_INDEX = 6; // 0x6
+    field public static final int SYNC_DURATION = 16; // 0x10
+    field public static final int SYNC_INDEX = 4; // 0x4
+    field public static final int TOTAL_DURATION = 1; // 0x1
+    field public static final int TOTAL_INDEX = 0; // 0x0
+  }
+
+  public abstract class JobIntentService extends android.app.Service {
+    ctor public JobIntentService();
+    method public static void enqueueWork(android.content.Context, Class<?>, int, android.content.Intent);
+    method public static void enqueueWork(android.content.Context, android.content.ComponentName, int, android.content.Intent);
+    method public boolean isStopped();
+    method public android.os.IBinder! onBind(android.content.Intent);
+    method protected abstract void onHandleWork(android.content.Intent);
+    method public boolean onStopCurrentWork();
+    method public void setInterruptIfStopped(boolean);
+  }
+
+  public final class NavUtils {
+    method public static android.content.Intent? getParentActivityIntent(android.app.Activity);
+    method public static android.content.Intent? getParentActivityIntent(android.content.Context, Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static android.content.Intent? getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static String? getParentActivityName(android.app.Activity);
+    method public static String? getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void navigateUpFromSameTask(android.app.Activity);
+    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+    field public static final String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+  }
+
+  public class NotificationChannelCompat {
+    method public boolean canBubble();
+    method public boolean canBypassDnd();
+    method public boolean canShowBadge();
+    method public android.media.AudioAttributes? getAudioAttributes();
+    method public String? getConversationId();
+    method public String? getDescription();
+    method public String? getGroup();
+    method public String getId();
+    method public int getImportance();
+    method public int getLightColor();
+    method public int getLockscreenVisibility();
+    method public CharSequence? getName();
+    method public String? getParentChannelId();
+    method public android.net.Uri? getSound();
+    method public long[]? getVibrationPattern();
+    method public boolean isImportantConversation();
+    method public boolean shouldShowLights();
+    method public boolean shouldVibrate();
+    method public androidx.core.app.NotificationChannelCompat.Builder toBuilder();
+    field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
+  }
+
+  public static class NotificationChannelCompat.Builder {
+    ctor public NotificationChannelCompat.Builder(String, int);
+    method public androidx.core.app.NotificationChannelCompat build();
+    method public androidx.core.app.NotificationChannelCompat.Builder setConversationId(String, String);
+    method public androidx.core.app.NotificationChannelCompat.Builder setDescription(String?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setGroup(String?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setImportance(int);
+    method public androidx.core.app.NotificationChannelCompat.Builder setLightColor(int);
+    method public androidx.core.app.NotificationChannelCompat.Builder setLightsEnabled(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setName(CharSequence?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setShowBadge(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setSound(android.net.Uri?, android.media.AudioAttributes?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setVibrationEnabled(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setVibrationPattern(long[]?);
+  }
+
+  public class NotificationChannelGroupCompat {
+    method public java.util.List<androidx.core.app.NotificationChannelCompat!> getChannels();
+    method public String? getDescription();
+    method public String getId();
+    method public CharSequence? getName();
+    method public boolean isBlocked();
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder toBuilder();
+  }
+
+  public static class NotificationChannelGroupCompat.Builder {
+    ctor public NotificationChannelGroupCompat.Builder(String);
+    method public androidx.core.app.NotificationChannelGroupCompat build();
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder setDescription(String?);
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder setName(CharSequence?);
+  }
+
+  public class NotificationCompat {
+    ctor @Deprecated public NotificationCompat();
+    method public static androidx.core.app.NotificationCompat.Action? getAction(android.app.Notification, int);
+    method public static int getActionCount(android.app.Notification);
+    method public static boolean getAllowSystemGeneratedContextualActions(android.app.Notification);
+    method public static boolean getAutoCancel(android.app.Notification);
+    method public static int getBadgeIconType(android.app.Notification);
+    method public static androidx.core.app.NotificationCompat.BubbleMetadata? getBubbleMetadata(android.app.Notification);
+    method public static String? getCategory(android.app.Notification);
+    method public static String? getChannelId(android.app.Notification);
+    method public static int getColor(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentInfo(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentText(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentTitle(android.app.Notification);
+    method public static android.os.Bundle? getExtras(android.app.Notification);
+    method public static String? getGroup(android.app.Notification);
+    method public static int getGroupAlertBehavior(android.app.Notification);
+    method @RequiresApi(21) public static java.util.List<androidx.core.app.NotificationCompat.Action!> getInvisibleActions(android.app.Notification);
+    method public static boolean getLocalOnly(android.app.Notification);
+    method public static androidx.core.content.LocusIdCompat? getLocusId(android.app.Notification);
+    method public static boolean getOngoing(android.app.Notification);
+    method public static boolean getOnlyAlertOnce(android.app.Notification);
+    method public static java.util.List<androidx.core.app.Person!> getPeople(android.app.Notification);
+    method public static android.app.Notification? getPublicVersion(android.app.Notification);
+    method public static CharSequence? getSettingsText(android.app.Notification);
+    method public static String? getShortcutId(android.app.Notification);
+    method @RequiresApi(19) public static boolean getShowWhen(android.app.Notification);
+    method public static String? getSortKey(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getSubText(android.app.Notification);
+    method public static long getTimeoutAfter(android.app.Notification);
+    method @RequiresApi(19) public static boolean getUsesChronometer(android.app.Notification);
+    method public static int getVisibility(android.app.Notification);
+    method public static boolean isGroupSummary(android.app.Notification);
+    field public static final int BADGE_ICON_LARGE = 2; // 0x2
+    field public static final int BADGE_ICON_NONE = 0; // 0x0
+    field public static final int BADGE_ICON_SMALL = 1; // 0x1
+    field public static final String CATEGORY_ALARM = "alarm";
+    field public static final String CATEGORY_CALL = "call";
+    field public static final String CATEGORY_EMAIL = "email";
+    field public static final String CATEGORY_ERROR = "err";
+    field public static final String CATEGORY_EVENT = "event";
+    field public static final String CATEGORY_LOCATION_SHARING = "location_sharing";
+    field public static final String CATEGORY_MESSAGE = "msg";
+    field public static final String CATEGORY_MISSED_CALL = "missed_call";
+    field public static final String CATEGORY_NAVIGATION = "navigation";
+    field public static final String CATEGORY_PROGRESS = "progress";
+    field public static final String CATEGORY_PROMO = "promo";
+    field public static final String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final String CATEGORY_REMINDER = "reminder";
+    field public static final String CATEGORY_SERVICE = "service";
+    field public static final String CATEGORY_SOCIAL = "social";
+    field public static final String CATEGORY_STATUS = "status";
+    field public static final String CATEGORY_STOPWATCH = "stopwatch";
+    field public static final String CATEGORY_SYSTEM = "sys";
+    field public static final String CATEGORY_TRANSPORT = "transport";
+    field public static final String CATEGORY_WORKOUT = "workout";
+    field @ColorInt public static final int COLOR_DEFAULT = 0; // 0x0
+    field public static final int DEFAULT_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_LIGHTS = 4; // 0x4
+    field public static final int DEFAULT_SOUND = 1; // 0x1
+    field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+    field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
+    field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
+    field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
+    field public static final String EXTRA_COLORIZED = "android.colorized";
+    field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+    field public static final String EXTRA_COMPAT_TEMPLATE = "androidx.core.app.extra.COMPAT_TEMPLATE";
+    field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final String EXTRA_HIDDEN_CONVERSATION_TITLE = "android.hiddenConversationTitle";
+    field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
+    field public static final String EXTRA_INFO_TEXT = "android.infoText";
+    field public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
+    field public static final String EXTRA_LARGE_ICON = "android.largeIcon";
+    field public static final String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+    field public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final String EXTRA_MESSAGES = "android.messages";
+    field public static final String EXTRA_MESSAGING_STYLE_USER = "android.messagingStyleUser";
+    field public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+    field public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
+    field @Deprecated public static final String EXTRA_PEOPLE = "android.people";
+    field public static final String EXTRA_PEOPLE_LIST = "android.people.list";
+    field public static final String EXTRA_PICTURE = "android.picture";
+    field public static final String EXTRA_PROGRESS = "android.progress";
+    field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    field public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    field public static final String EXTRA_SHOW_WHEN = "android.showWhen";
+    field public static final String EXTRA_SMALL_ICON = "android.icon";
+    field public static final String EXTRA_SUB_TEXT = "android.subText";
+    field public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
+    field public static final String EXTRA_TEMPLATE = "android.template";
+    field public static final String EXTRA_TEXT = "android.text";
+    field public static final String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final String EXTRA_TITLE = "android.title";
+    field public static final String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+    field public static final int FLAG_BUBBLE = 4096; // 0x1000
+    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+    field @Deprecated public static final int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final int FLAG_INSISTENT = 4; // 0x4
+    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+    field public static final int FLAG_NO_CLEAR = 32; // 0x20
+    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final int GROUP_ALERT_ALL = 0; // 0x0
+    field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
+    field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
+    field public static final String GROUP_KEY_SILENT = "silent";
+    field public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES";
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+  }
+
+  public static class NotificationCompat.Action {
+    ctor public NotificationCompat.Action(int, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+    method public android.app.PendingIntent? getActionIntent();
+    method public boolean getAllowGeneratedReplies();
+    method public androidx.core.app.RemoteInput![]? getDataOnlyRemoteInputs();
+    method public android.os.Bundle getExtras();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
+    method public androidx.core.app.RemoteInput![]? getRemoteInputs();
+    method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
+    method public boolean getShowsUserInterface();
+    method public CharSequence? getTitle();
+    method public boolean isContextual();
+    field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
+    field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
+    field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
+    field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
+    field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
+    field public static final int SEMANTIC_ACTION_MUTE = 6; // 0x6
+    field public static final int SEMANTIC_ACTION_NONE = 0; // 0x0
+    field public static final int SEMANTIC_ACTION_REPLY = 1; // 0x1
+    field public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9; // 0x9
+    field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
+    field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
+    field public android.app.PendingIntent! actionIntent;
+    field @Deprecated public int icon;
+    field public CharSequence! title;
+  }
+
+  public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action.Builder(int, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.Action.Builder addRemoteInput(androidx.core.app.RemoteInput?);
+    method public androidx.core.app.NotificationCompat.Action build();
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Extender);
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setContextual(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setSemanticAction(@androidx.core.app.NotificationCompat.Action.SemanticAction int);
+    method public androidx.core.app.NotificationCompat.Action.Builder setShowsUserInterface(boolean);
+  }
+
+  public static interface NotificationCompat.Action.Extender {
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_NONE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_REPLY, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_UNREAD, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_DELETE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_ARCHIVE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_UNMUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_UP, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_DOWN, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_CALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.Action.SemanticAction {
+  }
+
+  public static final class NotificationCompat.Action.WearableExtender implements androidx.core.app.NotificationCompat.Action.Extender {
+    ctor public NotificationCompat.Action.WearableExtender();
+    ctor public NotificationCompat.Action.WearableExtender(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender clone();
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+    method @Deprecated public CharSequence? getCancelLabel();
+    method @Deprecated public CharSequence? getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
+    method public boolean getHintLaunchesActivity();
+    method @Deprecated public CharSequence? getInProgressLabel();
+    method public boolean isAvailableOffline();
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setCancelLabel(CharSequence?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setConfirmLabel(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setInProgressLabel(CharSequence?);
+  }
+
+  public static class NotificationCompat.BigPictureStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigPictureStyle();
+    ctor public NotificationCompat.BigPictureStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle setSummaryText(CharSequence?);
+  }
+
+  public static class NotificationCompat.BigTextStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigTextStyle();
+    ctor public NotificationCompat.BigTextStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle bigText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle setSummaryText(CharSequence?);
+  }
+
+  public static final class NotificationCompat.BubbleMetadata {
+    method public static androidx.core.app.NotificationCompat.BubbleMetadata? fromPlatform(android.app.Notification.BubbleMetadata?);
+    method public boolean getAutoExpandBubble();
+    method public android.app.PendingIntent? getDeleteIntent();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getDesiredHeight();
+    method @DimenRes public int getDesiredHeightResId();
+    method public androidx.core.graphics.drawable.IconCompat? getIcon();
+    method public android.app.PendingIntent? getIntent();
+    method public String? getShortcutId();
+    method public boolean isNotificationSuppressed();
+    method public static android.app.Notification.BubbleMetadata? toPlatform(androidx.core.app.NotificationCompat.BubbleMetadata?);
+  }
+
+  public static final class NotificationCompat.BubbleMetadata.Builder {
+    ctor @Deprecated public NotificationCompat.BubbleMetadata.Builder();
+    ctor @RequiresApi(30) public NotificationCompat.BubbleMetadata.Builder(String);
+    ctor public NotificationCompat.BubbleMetadata.Builder(android.app.PendingIntent, androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata build();
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setAutoExpandBubble(boolean);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeight(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeightResId(@DimenRes int);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIcon(androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setSuppressNotification(boolean);
+  }
+
+  public static class NotificationCompat.Builder {
+    ctor @RequiresApi(19) public NotificationCompat.Builder(android.content.Context, android.app.Notification);
+    ctor public NotificationCompat.Builder(android.content.Context, String);
+    ctor @Deprecated public NotificationCompat.Builder(android.content.Context);
+    method public androidx.core.app.NotificationCompat.Builder addAction(int, CharSequence?, android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder addAction(androidx.core.app.NotificationCompat.Action?);
+    method public androidx.core.app.NotificationCompat.Builder addExtras(android.os.Bundle?);
+    method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(int, CharSequence?, android.app.PendingIntent?);
+    method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(androidx.core.app.NotificationCompat.Action?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder addPerson(String?);
+    method public androidx.core.app.NotificationCompat.Builder addPerson(androidx.core.app.Person?);
+    method public android.app.Notification build();
+    method public androidx.core.app.NotificationCompat.Builder clearActions();
+    method public androidx.core.app.NotificationCompat.Builder clearInvisibleActions();
+    method public androidx.core.app.NotificationCompat.Builder clearPeople();
+    method public android.widget.RemoteViews? createBigContentView();
+    method public android.widget.RemoteViews? createContentView();
+    method public android.widget.RemoteViews? createHeadsUpContentView();
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Extender);
+    method public android.os.Bundle getExtras();
+    method @Deprecated public android.app.Notification getNotification();
+    method protected static CharSequence? limitCharSequenceLength(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setAllowSystemGeneratedContextualActions(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setAutoCancel(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setBadgeIconType(int);
+    method public androidx.core.app.NotificationCompat.Builder setBubbleMetadata(androidx.core.app.NotificationCompat.BubbleMetadata?);
+    method public androidx.core.app.NotificationCompat.Builder setCategory(String?);
+    method public androidx.core.app.NotificationCompat.Builder setChannelId(String);
+    method @RequiresApi(24) public androidx.core.app.NotificationCompat.Builder setChronometerCountDown(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setColor(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.Builder setColorized(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setContent(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setContentInfo(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder setContentText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setDefaults(int);
+    method public androidx.core.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent?, boolean);
+    method public androidx.core.app.NotificationCompat.Builder setGroup(String?);
+    method public androidx.core.app.NotificationCompat.Builder setGroupAlertBehavior(int);
+    method public androidx.core.app.NotificationCompat.Builder setGroupSummary(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.Builder setLights(@ColorInt int, int, int);
+    method public androidx.core.app.NotificationCompat.Builder setLocalOnly(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder setNotificationSilent();
+    method public androidx.core.app.NotificationCompat.Builder setNumber(int);
+    method public androidx.core.app.NotificationCompat.Builder setOngoing(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setPriority(int);
+    method public androidx.core.app.NotificationCompat.Builder setProgress(int, int, boolean);
+    method public androidx.core.app.NotificationCompat.Builder setPublicVersion(android.app.Notification?);
+    method public androidx.core.app.NotificationCompat.Builder setRemoteInputHistory(CharSequence![]?);
+    method public androidx.core.app.NotificationCompat.Builder setSettingsText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setShortcutId(String?);
+    method public androidx.core.app.NotificationCompat.Builder setShortcutInfo(androidx.core.content.pm.ShortcutInfoCompat?);
+    method public androidx.core.app.NotificationCompat.Builder setShowWhen(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setSilent(boolean);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.Builder setSmallIcon(androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int);
+    method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int, int);
+    method public androidx.core.app.NotificationCompat.Builder setSortKey(String?);
+    method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?);
+    method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?, int);
+    method public androidx.core.app.NotificationCompat.Builder setStyle(androidx.core.app.NotificationCompat.Style?);
+    method public androidx.core.app.NotificationCompat.Builder setSubText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?, android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setTimeoutAfter(long);
+    method public androidx.core.app.NotificationCompat.Builder setUsesChronometer(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setVibrate(long[]?);
+    method public androidx.core.app.NotificationCompat.Builder setVisibility(int);
+    method public androidx.core.app.NotificationCompat.Builder setWhen(long);
+    field @Deprecated public java.util.ArrayList<java.lang.String!>! mPeople;
+  }
+
+  public static final class NotificationCompat.CarExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.CarExtender();
+    ctor public NotificationCompat.CarExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method @ColorInt public int getColor();
+    method public android.graphics.Bitmap? getLargeIcon();
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation? getUnreadConversation();
+    method public androidx.core.app.NotificationCompat.CarExtender setColor(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender setUnreadConversation(androidx.core.app.NotificationCompat.CarExtender.UnreadConversation?);
+  }
+
+  @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation {
+    method @Deprecated public long getLatestTimestamp();
+    method @Deprecated public String![]? getMessages();
+    method @Deprecated public String? getParticipant();
+    method @Deprecated public String![]? getParticipants();
+    method @Deprecated public android.app.PendingIntent? getReadPendingIntent();
+    method @Deprecated public androidx.core.app.RemoteInput? getRemoteInput();
+    method @Deprecated public android.app.PendingIntent? getReplyPendingIntent();
+  }
+
+  @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+    ctor @Deprecated public NotificationCompat.CarExtender.UnreadConversation.Builder(String);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(String?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation build();
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent?, androidx.core.app.RemoteInput?);
+  }
+
+  public static class NotificationCompat.DecoratedCustomViewStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static interface NotificationCompat.Extender {
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+  }
+
+  public static class NotificationCompat.InboxStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.InboxStyle();
+    ctor public NotificationCompat.InboxStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.InboxStyle addLine(CharSequence?);
+    method public androidx.core.app.NotificationCompat.InboxStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.InboxStyle setSummaryText(CharSequence?);
+  }
+
+  public static class NotificationCompat.MessagingStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor @Deprecated public NotificationCompat.MessagingStyle(CharSequence);
+    ctor public NotificationCompat.MessagingStyle(androidx.core.app.Person);
+    method public void addCompatExtras(android.os.Bundle);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addHistoricMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+    method @Deprecated public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, CharSequence?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, androidx.core.app.Person?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+    method public static androidx.core.app.NotificationCompat.MessagingStyle? extractMessagingStyleFromNotification(android.app.Notification);
+    method public CharSequence? getConversationTitle();
+    method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getHistoricMessages();
+    method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getMessages();
+    method public androidx.core.app.Person getUser();
+    method @Deprecated public CharSequence? getUserDisplayName();
+    method public boolean isGroupConversation();
+    method public androidx.core.app.NotificationCompat.MessagingStyle setConversationTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle setGroupConversation(boolean);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class NotificationCompat.MessagingStyle.Message {
+    ctor public NotificationCompat.MessagingStyle.Message(CharSequence?, long, androidx.core.app.Person?);
+    ctor @Deprecated public NotificationCompat.MessagingStyle.Message(CharSequence?, long, CharSequence?);
+    method public String? getDataMimeType();
+    method public android.net.Uri? getDataUri();
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.Person? getPerson();
+    method @Deprecated public CharSequence? getSender();
+    method public CharSequence getText();
+    method public long getTimestamp();
+    method public androidx.core.app.NotificationCompat.MessagingStyle.Message setData(String?, android.net.Uri?);
+  }
+
+  public abstract static class NotificationCompat.Style {
+    ctor public NotificationCompat.Style();
+    method public android.app.Notification? build();
+    method public void setBuilder(androidx.core.app.NotificationCompat.Builder?);
+  }
+
+  public static final class NotificationCompat.WearableExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.WearableExtender();
+    ctor public NotificationCompat.WearableExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.WearableExtender addAction(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.WearableExtender addActions(java.util.List<androidx.core.app.NotificationCompat.Action!>);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification!>);
+    method public androidx.core.app.NotificationCompat.WearableExtender clearActions();
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender clearPages();
+    method public androidx.core.app.NotificationCompat.WearableExtender clone();
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method public java.util.List<androidx.core.app.NotificationCompat.Action!> getActions();
+    method @Deprecated public android.graphics.Bitmap? getBackground();
+    method public String? getBridgeTag();
+    method public int getContentAction();
+    method @Deprecated public int getContentIcon();
+    method @Deprecated public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method @Deprecated public int getCustomContentHeight();
+    method @Deprecated public int getCustomSizePreset();
+    method public String? getDismissalId();
+    method @Deprecated public android.app.PendingIntent? getDisplayIntent();
+    method @Deprecated public int getGravity();
+    method @Deprecated public boolean getHintAmbientBigPicture();
+    method @Deprecated public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
+    method @Deprecated public boolean getHintHideIcon();
+    method @Deprecated public int getHintScreenTimeout();
+    method @Deprecated public boolean getHintShowBackgroundOnly();
+    method @Deprecated public java.util.List<android.app.Notification!> getPages();
+    method public boolean getStartScrollBottom();
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.WearableExtender setBridgeTag(String?);
+    method public androidx.core.app.NotificationCompat.WearableExtender setContentAction(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIcon(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+    method public androidx.core.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+    method public androidx.core.app.NotificationCompat.WearableExtender setDismissalId(String?);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent?);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setGravity(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public androidx.core.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public androidx.core.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+    field @Deprecated public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field @Deprecated public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field @Deprecated public static final int SIZE_DEFAULT = 0; // 0x0
+    field @Deprecated public static final int SIZE_FULL_SCREEN = 5; // 0x5
+    field @Deprecated public static final int SIZE_LARGE = 4; // 0x4
+    field @Deprecated public static final int SIZE_MEDIUM = 3; // 0x3
+    field @Deprecated public static final int SIZE_SMALL = 2; // 0x2
+    field @Deprecated public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public final class NotificationCompatExtras {
+    field public static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+    field public static final String EXTRA_GROUP_KEY = "android.support.groupKey";
+    field public static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+    field public static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+    field public static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+    field public static final String EXTRA_SORT_KEY = "android.support.sortKey";
+  }
+
+  public abstract class NotificationCompatSideChannelService extends android.app.Service {
+    ctor public NotificationCompatSideChannelService();
+    method public abstract void cancel(String!, int, String!);
+    method public abstract void cancelAll(String!);
+    method public abstract void notify(String!, int, String!, android.app.Notification!);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+  }
+
+  public final class NotificationManagerCompat {
+    method public boolean areNotificationsEnabled();
+    method public void cancel(int);
+    method public void cancel(String?, int);
+    method public void cancelAll();
+    method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void createNotificationChannel(androidx.core.app.NotificationChannelCompat);
+    method public void createNotificationChannelGroup(android.app.NotificationChannelGroup);
+    method public void createNotificationChannelGroup(androidx.core.app.NotificationChannelGroupCompat);
+    method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup!>);
+    method public void createNotificationChannelGroupsCompat(java.util.List<androidx.core.app.NotificationChannelGroupCompat!>);
+    method public void createNotificationChannels(java.util.List<android.app.NotificationChannel!>);
+    method public void createNotificationChannelsCompat(java.util.List<androidx.core.app.NotificationChannelCompat!>);
+    method public void deleteNotificationChannel(String);
+    method public void deleteNotificationChannelGroup(String);
+    method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
+    method public static androidx.core.app.NotificationManagerCompat from(android.content.Context);
+    method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
+    method public int getImportance();
+    method public android.app.NotificationChannel? getNotificationChannel(String);
+    method public android.app.NotificationChannel? getNotificationChannel(String, String);
+    method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String);
+    method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String, String);
+    method public android.app.NotificationChannelGroup? getNotificationChannelGroup(String);
+    method public androidx.core.app.NotificationChannelGroupCompat? getNotificationChannelGroupCompat(String);
+    method public java.util.List<android.app.NotificationChannelGroup!> getNotificationChannelGroups();
+    method public java.util.List<androidx.core.app.NotificationChannelGroupCompat!> getNotificationChannelGroupsCompat();
+    method public java.util.List<android.app.NotificationChannel!> getNotificationChannels();
+    method public java.util.List<androidx.core.app.NotificationChannelCompat!> getNotificationChannelsCompat();
+    method public void notify(int, android.app.Notification);
+    method public void notify(String?, int, android.app.Notification);
+    field public static final String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+    field public static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
+    field public static final int IMPORTANCE_NONE = 0; // 0x0
+    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+  }
+
+  public class Person {
+    method public static androidx.core.app.Person fromBundle(android.os.Bundle);
+    method public androidx.core.graphics.drawable.IconCompat? getIcon();
+    method public String? getKey();
+    method public CharSequence? getName();
+    method public String? getUri();
+    method public boolean isBot();
+    method public boolean isImportant();
+    method public androidx.core.app.Person.Builder toBuilder();
+    method public android.os.Bundle toBundle();
+  }
+
+  public static class Person.Builder {
+    ctor public Person.Builder();
+    method public androidx.core.app.Person build();
+    method public androidx.core.app.Person.Builder setBot(boolean);
+    method public androidx.core.app.Person.Builder setIcon(androidx.core.graphics.drawable.IconCompat?);
+    method public androidx.core.app.Person.Builder setImportant(boolean);
+    method public androidx.core.app.Person.Builder setKey(String?);
+    method public androidx.core.app.Person.Builder setName(CharSequence?);
+    method public androidx.core.app.Person.Builder setUri(String?);
+  }
+
+  public final class RemoteActionCompat implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public RemoteActionCompat(androidx.core.graphics.drawable.IconCompat, CharSequence, CharSequence, android.app.PendingIntent);
+    ctor public RemoteActionCompat(androidx.core.app.RemoteActionCompat);
+    method @RequiresApi(26) public static androidx.core.app.RemoteActionCompat createFromRemoteAction(android.app.RemoteAction);
+    method public android.app.PendingIntent getActionIntent();
+    method public CharSequence getContentDescription();
+    method public androidx.core.graphics.drawable.IconCompat getIcon();
+    method public CharSequence getTitle();
+    method public boolean isEnabled();
+    method public void setEnabled(boolean);
+    method public void setShouldShowIcon(boolean);
+    method public boolean shouldShowIcon();
+    method @RequiresApi(26) public android.app.RemoteAction toRemoteAction();
+  }
+
+  public final class RemoteInput {
+    method public static void addDataResultToIntent(androidx.core.app.RemoteInput!, android.content.Intent!, java.util.Map<java.lang.String!,android.net.Uri!>!);
+    method public static void addResultsToIntent(androidx.core.app.RemoteInput![]!, android.content.Intent!, android.os.Bundle!);
+    method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String!>! getAllowedDataTypes();
+    method public CharSequence![]! getChoices();
+    method public static java.util.Map<java.lang.String!,android.net.Uri!>! getDataResultsFromIntent(android.content.Intent!, String!);
+    method public int getEditChoicesBeforeSending();
+    method public android.os.Bundle! getExtras();
+    method public CharSequence! getLabel();
+    method public String! getResultKey();
+    method public static android.os.Bundle! getResultsFromIntent(android.content.Intent!);
+    method public static int getResultsSource(android.content.Intent);
+    method public boolean isDataOnly();
+    method public static void setResultsSource(android.content.Intent, int);
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0; // 0x0
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1; // 0x1
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2; // 0x2
+    field public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    field public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+    field public static final int SOURCE_CHOICE = 1; // 0x1
+    field public static final int SOURCE_FREE_FORM_INPUT = 0; // 0x0
+  }
+
+  public static final class RemoteInput.Builder {
+    ctor public RemoteInput.Builder(String);
+    method public androidx.core.app.RemoteInput.Builder addExtras(android.os.Bundle);
+    method public androidx.core.app.RemoteInput build();
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.RemoteInput.Builder setAllowDataType(String, boolean);
+    method public androidx.core.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+    method public androidx.core.app.RemoteInput.Builder setChoices(CharSequence![]?);
+    method public androidx.core.app.RemoteInput.Builder setEditChoicesBeforeSending(int);
+    method public androidx.core.app.RemoteInput.Builder setLabel(CharSequence?);
+  }
+
+  public final class ServiceCompat {
+    method public static void stopForeground(android.app.Service, int);
+    field public static final int START_STICKY = 1; // 0x1
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+  }
+
+  public final class ShareCompat {
+    method @Deprecated public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
+    method public static android.content.ComponentName? getCallingActivity(android.app.Activity);
+    method public static String? getCallingPackage(android.app.Activity);
+    field public static final String EXTRA_CALLING_ACTIVITY = "androidx.core.app.EXTRA_CALLING_ACTIVITY";
+    field public static final String EXTRA_CALLING_ACTIVITY_INTEROP = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+    field public static final String EXTRA_CALLING_PACKAGE = "androidx.core.app.EXTRA_CALLING_PACKAGE";
+    field public static final String EXTRA_CALLING_PACKAGE_INTEROP = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+  }
+
+  public static class ShareCompat.IntentBuilder {
+    ctor public ShareCompat.IntentBuilder(android.content.Context);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+    method public android.content.Intent createChooserIntent();
+    method @Deprecated public static androidx.core.app.ShareCompat.IntentBuilder from(android.app.Activity);
+    method public android.content.Intent getIntent();
+    method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(CharSequence?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(@StringRes int);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailBcc(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailCc(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailTo(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setHtmlText(String?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setStream(android.net.Uri?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setSubject(String?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setText(CharSequence?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setType(String?);
+    method public void startChooser();
+  }
+
+  public static class ShareCompat.IntentReader {
+    ctor public ShareCompat.IntentReader(android.app.Activity);
+    ctor public ShareCompat.IntentReader(android.content.Context, android.content.Intent);
+    method @Deprecated public static androidx.core.app.ShareCompat.IntentReader from(android.app.Activity);
+    method public android.content.ComponentName? getCallingActivity();
+    method public android.graphics.drawable.Drawable? getCallingActivityIcon();
+    method public android.graphics.drawable.Drawable? getCallingApplicationIcon();
+    method public CharSequence? getCallingApplicationLabel();
+    method public String? getCallingPackage();
+    method public String![]? getEmailBcc();
+    method public String![]? getEmailCc();
+    method public String![]? getEmailTo();
+    method public String? getHtmlText();
+    method public android.net.Uri? getStream();
+    method public android.net.Uri? getStream(int);
+    method public int getStreamCount();
+    method public String? getSubject();
+    method public CharSequence? getText();
+    method public String? getType();
+    method public boolean isMultipleShare();
+    method public boolean isShareIntent();
+    method public boolean isSingleShare();
+  }
+
+  public abstract class SharedElementCallback {
+    ctor public SharedElementCallback();
+    method public android.os.Parcelable! onCaptureSharedElementSnapshot(android.view.View!, android.graphics.Matrix!, android.graphics.RectF!);
+    method public android.view.View! onCreateSnapshotView(android.content.Context!, android.os.Parcelable!);
+    method public void onMapSharedElements(java.util.List<java.lang.String!>!, java.util.Map<java.lang.String!,android.view.View!>!);
+    method public void onRejectSharedElements(java.util.List<android.view.View!>!);
+    method public void onSharedElementEnd(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+    method public void onSharedElementStart(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+    method public void onSharedElementsArrived(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, androidx.core.app.SharedElementCallback.OnSharedElementsReadyListener!);
+  }
+
+  public static interface SharedElementCallback.OnSharedElementsReadyListener {
+    method public void onSharedElementsReady();
+  }
+
+  public final class TaskStackBuilder implements java.lang.Iterable<android.content.Intent> {
+    method public androidx.core.app.TaskStackBuilder addNextIntent(android.content.Intent);
+    method public androidx.core.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+    method public androidx.core.app.TaskStackBuilder addParentStack(android.app.Activity);
+    method public androidx.core.app.TaskStackBuilder addParentStack(Class<?>);
+    method public androidx.core.app.TaskStackBuilder! addParentStack(android.content.ComponentName!);
+    method public static androidx.core.app.TaskStackBuilder create(android.content.Context);
+    method public android.content.Intent? editIntentAt(int);
+    method @Deprecated public static androidx.core.app.TaskStackBuilder! from(android.content.Context!);
+    method @Deprecated public android.content.Intent! getIntent(int);
+    method public int getIntentCount();
+    method public android.content.Intent![] getIntents();
+    method public android.app.PendingIntent? getPendingIntent(int, int);
+    method public android.app.PendingIntent? getPendingIntent(int, int, android.os.Bundle?);
+    method @Deprecated public java.util.Iterator<android.content.Intent!>! iterator();
+    method public void startActivities();
+    method public void startActivities(android.os.Bundle?);
+  }
+
+  public static interface TaskStackBuilder.SupportParentable {
+    method public android.content.Intent? getSupportParentActivityIntent();
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentProviderCompat {
+    method public static android.content.Context requireContext(android.content.ContentProvider);
+  }
+
+  public final class ContentResolverCompat {
+    method public static android.database.Cursor! query(android.content.ContentResolver!, android.net.Uri!, String![]!, String!, String![]!, String!, androidx.core.os.CancellationSignal!);
+  }
+
+  public class ContextCompat {
+    ctor protected ContextCompat();
+    method public static int checkSelfPermission(android.content.Context, String);
+    method public static android.content.Context? createDeviceProtectedStorageContext(android.content.Context);
+    method public static java.io.File! getCodeCacheDir(android.content.Context);
+    method @ColorInt public static int getColor(android.content.Context, @ColorRes int);
+    method public static android.content.res.ColorStateList? getColorStateList(android.content.Context, @ColorRes int);
+    method public static java.io.File? getDataDir(android.content.Context);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+    method public static java.io.File![] getExternalCacheDirs(android.content.Context);
+    method public static java.io.File![] getExternalFilesDirs(android.content.Context, String?);
+    method public static java.util.concurrent.Executor! getMainExecutor(android.content.Context!);
+    method public static java.io.File? getNoBackupFilesDir(android.content.Context);
+    method public static java.io.File![] getObbDirs(android.content.Context);
+    method public static <T> T? getSystemService(android.content.Context, Class<T!>);
+    method public static String? getSystemServiceName(android.content.Context, Class<?>);
+    method public static boolean isDeviceProtectedStorage(android.content.Context);
+    method public static boolean startActivities(android.content.Context, android.content.Intent![]);
+    method public static boolean startActivities(android.content.Context, android.content.Intent![], android.os.Bundle?);
+    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle?);
+    method public static void startForegroundService(android.content.Context, android.content.Intent);
+  }
+
+  public class FileProvider extends android.content.ContentProvider {
+    ctor public FileProvider();
+    method public int delete(android.net.Uri, String?, String![]?);
+    method public String! getType(android.net.Uri);
+    method public static android.net.Uri! getUriForFile(android.content.Context, String, java.io.File);
+    method public static android.net.Uri getUriForFile(android.content.Context, String, java.io.File, String);
+    method public android.net.Uri! insert(android.net.Uri, android.content.ContentValues!);
+    method public boolean onCreate();
+    method public android.database.Cursor! query(android.net.Uri, String![]?, String?, String![]?, String?);
+    method public int update(android.net.Uri, android.content.ContentValues!, String?, String![]?);
+  }
+
+  public final class IntentCompat {
+    method public static android.content.Intent makeMainSelectorActivity(String, String);
+    field public static final String ACTION_CREATE_REMINDER = "android.intent.action.CREATE_REMINDER";
+    field public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    field public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+    field public static final String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
+    field public static final String EXTRA_TIME = "android.intent.extra.TIME";
+  }
+
+  public final class LocusIdCompat {
+    ctor public LocusIdCompat(String);
+    method public String getId();
+    method @RequiresApi(29) public android.content.LocusId toLocusId();
+    method @RequiresApi(29) public static androidx.core.content.LocusIdCompat toLocusIdCompat(android.content.LocusId);
+  }
+
+  public final class MimeTypeFilter {
+    method public static boolean matches(String?, String);
+    method public static String? matches(String?, String![]);
+    method public static String? matches(String![]?, String);
+    method public static String![] matchesMany(String![]?, String);
+  }
+
+  public final class PermissionChecker {
+    method public static int checkCallingOrSelfPermission(android.content.Context, String);
+    method public static int checkCallingPermission(android.content.Context, String, String?);
+    method public static int checkPermission(android.content.Context, String, int, int, String?);
+    method public static int checkSelfPermission(android.content.Context, String);
+    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+    field public static final int PERMISSION_GRANTED = 0; // 0x0
+  }
+
+  @Deprecated public final class SharedPreferencesCompat {
+  }
+
+  @Deprecated public static final class SharedPreferencesCompat.EditorCompat {
+    method @Deprecated public void apply(android.content.SharedPreferences.Editor);
+    method @Deprecated public static androidx.core.content.SharedPreferencesCompat.EditorCompat! getInstance();
+  }
+
+}
+
+package androidx.core.content.pm {
+
+  @Deprecated public final class ActivityInfoCompat {
+    field @Deprecated public static final int CONFIG_UI_MODE = 512; // 0x200
+  }
+
+  public final class PackageInfoCompat {
+    method public static long getLongVersionCode(android.content.pm.PackageInfo);
+    method public static java.util.List<android.content.pm.Signature!> getSignatures(android.content.pm.PackageManager, String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static boolean hasSignatures(android.content.pm.PackageManager, String, @Size(min=1) java.util.Map<byte[]!,java.lang.Integer!>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+  }
+
+  public final class PermissionInfoCompat {
+    method public static int getProtection(android.content.pm.PermissionInfo);
+    method public static int getProtectionFlags(android.content.pm.PermissionInfo);
+  }
+
+  public class ShortcutInfoCompat {
+    method public android.content.ComponentName? getActivity();
+    method public java.util.Set<java.lang.String!>? getCategories();
+    method public CharSequence? getDisabledMessage();
+    method public int getDisabledReason();
+    method public android.os.PersistableBundle? getExtras();
+    method public String getId();
+    method public android.content.Intent getIntent();
+    method public android.content.Intent![] getIntents();
+    method public long getLastChangedTimestamp();
+    method public androidx.core.content.LocusIdCompat? getLocusId();
+    method public CharSequence? getLongLabel();
+    method public String getPackage();
+    method public int getRank();
+    method public CharSequence getShortLabel();
+    method public android.os.UserHandle? getUserHandle();
+    method public boolean hasKeyFieldsOnly();
+    method public boolean isCached();
+    method public boolean isDeclaredInManifest();
+    method public boolean isDynamic();
+    method public boolean isEnabled();
+    method public boolean isImmutable();
+    method public boolean isPinned();
+    method @RequiresApi(25) public android.content.pm.ShortcutInfo! toShortcutInfo();
+  }
+
+  public static class ShortcutInfoCompat.Builder {
+    ctor public ShortcutInfoCompat.Builder(android.content.Context, String);
+    method public androidx.core.content.pm.ShortcutInfoCompat build();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setAlwaysBadged();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setCategories(java.util.Set<java.lang.String!>);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(CharSequence);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExtras(android.os.PersistableBundle);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIcon(androidx.core.graphics.drawable.IconCompat!);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsConversation();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
+    method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived(boolean);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPerson(androidx.core.app.Person);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPersons(androidx.core.app.Person![]);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setRank(int);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setShortLabel(CharSequence);
+  }
+
+  public class ShortcutManagerCompat {
+    method public static boolean addDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static android.content.Intent createShortcutResultIntent(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+    method public static void disableShortcuts(android.content.Context, java.util.List<java.lang.String!>, CharSequence?);
+    method public static void enableShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getDynamicShortcuts(android.content.Context);
+    method public static int getIconMaxHeight(android.content.Context);
+    method public static int getIconMaxWidth(android.content.Context);
+    method public static int getMaxShortcutCountPerActivity(android.content.Context);
+    method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getShortcuts(android.content.Context, int);
+    method public static boolean isRateLimitingActive(android.content.Context);
+    method public static boolean isRequestPinShortcutSupported(android.content.Context);
+    method public static boolean pushDynamicShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+    method public static void removeAllDynamicShortcuts(android.content.Context);
+    method public static void removeDynamicShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+    method public static void removeLongLivedShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+    method public static void reportShortcutUsed(android.content.Context, String);
+    method public static boolean requestPinShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat, android.content.IntentSender?);
+    method public static boolean setDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static boolean updateShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    field public static final String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID";
+    field public static final int FLAG_MATCH_CACHED = 8; // 0x8
+    field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
+    field public static final int FLAG_MATCH_MANIFEST = 1; // 0x1
+    field public static final int FLAG_MATCH_PINNED = 4; // 0x4
+  }
+
+}
+
+package androidx.core.content.res {
+
+  public final class ConfigurationHelper {
+    method public static int getDensityDpi(android.content.res.Resources);
+  }
+
+  public final class ResourcesCompat {
+    method public static android.graphics.Typeface? getCachedFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+    method @ColorInt public static int getColor(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.content.res.ColorStateList? getColorStateList(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable? getDrawableForDensity(android.content.res.Resources, @DrawableRes int, int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static float getFloat(android.content.res.Resources, @DimenRes int);
+    method public static android.graphics.Typeface? getFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+    method public static void getFont(android.content.Context, @FontRes int, androidx.core.content.res.ResourcesCompat.FontCallback, android.os.Handler?) throws android.content.res.Resources.NotFoundException;
+    field @AnyRes public static final int ID_NULL = 0; // 0x0
+  }
+
+  public abstract static class ResourcesCompat.FontCallback {
+    ctor public ResourcesCompat.FontCallback();
+    method public abstract void onFontRetrievalFailed(int);
+    method public abstract void onFontRetrieved(android.graphics.Typeface);
+  }
+
+  public static final class ResourcesCompat.ThemeCompat {
+    method public static void rebase(android.content.res.Resources.Theme);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorWindowCompat {
+    method public static android.database.CursorWindow create(String?, long);
+  }
+
+  @Deprecated public final class DatabaseUtilsCompat {
+    method @Deprecated public static String![]! appendSelectionArgs(String![]!, String![]!);
+    method @Deprecated public static String! concatenateWhere(String!, String!);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteCursorCompat {
+    method public static void setFillWindowForwardOnly(android.database.sqlite.SQLiteCursor, boolean);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapCompat {
+    method public static int getAllocationByteCount(android.graphics.Bitmap);
+    method public static boolean hasMipMap(android.graphics.Bitmap);
+    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+  }
+
+  public class BlendModeColorFilterCompat {
+    method public static android.graphics.ColorFilter? createBlendModeColorFilterCompat(int, androidx.core.graphics.BlendModeCompat);
+  }
+
+  public enum BlendModeCompat {
+    enum_constant public static final androidx.core.graphics.BlendModeCompat CLEAR;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_BURN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_DODGE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DARKEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat DIFFERENCE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_ATOP;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_IN;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OUT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OVER;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat EXCLUSION;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HARD_LIGHT;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HUE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat LIGHTEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat LUMINOSITY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat MODULATE;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat MULTIPLY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat OVERLAY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat PLUS;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SATURATION;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SCREEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SOFT_LIGHT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_ATOP;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_IN;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OUT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OVER;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat XOR;
+  }
+
+  public final class ColorUtils {
+    method @ColorInt public static int HSLToColor(float[]);
+    method @ColorInt public static int LABToColor(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double);
+    method public static void LABToXYZ(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double, double[]);
+    method public static void RGBToHSL(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, float[]);
+    method public static void RGBToLAB(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+    method public static void RGBToXYZ(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+    method @ColorInt public static int XYZToColor(@FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_X) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Y) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Z) double);
+    method public static void XYZToLAB(@FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_X) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Y) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Z) double, double[]);
+    method @ColorInt public static int blendARGB(@ColorInt int, @ColorInt int, @FloatRange(from=0.0, to=1.0) float);
+    method public static void blendHSL(float[], float[], @FloatRange(from=0.0, to=1.0) float, float[]);
+    method public static void blendLAB(double[], double[], @FloatRange(from=0.0, to=1.0) double, double[]);
+    method public static double calculateContrast(@ColorInt int, @ColorInt int);
+    method @FloatRange(from=0.0, to=1.0) public static double calculateLuminance(@ColorInt int);
+    method public static int calculateMinimumAlpha(@ColorInt int, @ColorInt int, float);
+    method public static void colorToHSL(@ColorInt int, float[]);
+    method public static void colorToLAB(@ColorInt int, double[]);
+    method public static void colorToXYZ(@ColorInt int, double[]);
+    method public static int compositeColors(@ColorInt int, @ColorInt int);
+    method @RequiresApi(26) public static android.graphics.Color compositeColors(android.graphics.Color, android.graphics.Color);
+    method public static double distanceEuclidean(double[], double[]);
+    method @ColorInt public static int setAlphaComponent(@ColorInt int, @IntRange(from=0, to=255) int);
+  }
+
+  public final class Insets {
+    method public static androidx.core.graphics.Insets add(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets max(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets min(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets of(int, int, int, int);
+    method public static androidx.core.graphics.Insets of(android.graphics.Rect);
+    method public static androidx.core.graphics.Insets subtract(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method @RequiresApi(api=29) public static androidx.core.graphics.Insets toCompatInsets(android.graphics.Insets);
+    method @RequiresApi(api=29) public android.graphics.Insets toPlatformInsets();
+    field public static final androidx.core.graphics.Insets NONE;
+    field public final int bottom;
+    field public final int left;
+    field public final int right;
+    field public final int top;
+  }
+
+  public final class PaintCompat {
+    method public static boolean hasGlyph(android.graphics.Paint, String);
+    method public static boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat?);
+  }
+
+  public final class PathSegment {
+    ctor public PathSegment(android.graphics.PointF, float, android.graphics.PointF, float);
+    method public android.graphics.PointF getEnd();
+    method public float getEndFraction();
+    method public android.graphics.PointF getStart();
+    method public float getStartFraction();
+  }
+
+  public final class PathUtils {
+    method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path);
+    method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path, @FloatRange(from=0) float);
+  }
+
+  public class TypefaceCompat {
+    method public static android.graphics.Typeface create(android.content.Context, android.graphics.Typeface?, int);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class DrawableCompat {
+    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+    method public static void clearColorFilter(android.graphics.drawable.Drawable);
+    method public static int getAlpha(android.graphics.drawable.Drawable);
+    method public static android.graphics.ColorFilter! getColorFilter(android.graphics.drawable.Drawable);
+    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+    method @Deprecated public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+    method public static void setTint(android.graphics.drawable.Drawable, @ColorInt int);
+    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList?);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static <T extends android.graphics.drawable.Drawable> T! unwrap(android.graphics.drawable.Drawable);
+    method public static android.graphics.drawable.Drawable! wrap(android.graphics.drawable.Drawable);
+  }
+
+  public class IconCompat implements androidx.versionedparcelable.VersionedParcelable {
+    method public static androidx.core.graphics.drawable.IconCompat? createFromBundle(android.os.Bundle);
+    method @RequiresApi(23) public static androidx.core.graphics.drawable.IconCompat? createFromIcon(android.content.Context, android.graphics.drawable.Icon);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithAdaptiveBitmap(android.graphics.Bitmap!);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(String);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(android.net.Uri);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithBitmap(android.graphics.Bitmap!);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithContentUri(String!);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithContentUri(android.net.Uri!);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithData(byte[]!, int, int);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithResource(android.content.Context!, @DrawableRes int);
+    method @IdRes public int getResId();
+    method public String getResPackage();
+    method public int getType();
+    method public android.net.Uri getUri();
+    method public android.graphics.drawable.Drawable? loadDrawable(android.content.Context);
+    method public void onPostParceling();
+    method public void onPreParceling(boolean);
+    method public androidx.core.graphics.drawable.IconCompat! setTint(@ColorInt int);
+    method public androidx.core.graphics.drawable.IconCompat! setTintList(android.content.res.ColorStateList!);
+    method public androidx.core.graphics.drawable.IconCompat! setTintMode(android.graphics.PorterDuff.Mode!);
+    method public android.os.Bundle toBundle();
+    method @Deprecated @RequiresApi(23) public android.graphics.drawable.Icon toIcon();
+    method @RequiresApi(23) public android.graphics.drawable.Icon toIcon(android.content.Context?);
+    field public static final int TYPE_ADAPTIVE_BITMAP = 5; // 0x5
+    field public static final int TYPE_BITMAP = 1; // 0x1
+    field public static final int TYPE_DATA = 3; // 0x3
+    field public static final int TYPE_RESOURCE = 2; // 0x2
+    field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+    field public static final int TYPE_URI = 4; // 0x4
+    field public static final int TYPE_URI_ADAPTIVE_BITMAP = 6; // 0x6
+  }
+
+  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.Bitmap? getBitmap();
+    method public float getCornerRadius();
+    method public int getGravity();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public boolean hasAntiAlias();
+    method public boolean hasMipMap();
+    method public boolean isCircular();
+    method public void setAlpha(int);
+    method public void setAntiAlias(boolean);
+    method public void setCircular(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setCornerRadius(float);
+    method public void setDither(boolean);
+    method public void setGravity(int);
+    method public void setMipMap(boolean);
+    method public void setTargetDensity(android.graphics.Canvas);
+    method public void setTargetDensity(android.util.DisplayMetrics);
+    method public void setTargetDensity(int);
+  }
+
+  public final class RoundedBitmapDrawableFactory {
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap?);
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, String);
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+  }
+
+}
+
+package androidx.core.hardware.display {
+
+  public final class DisplayManagerCompat {
+    method public android.view.Display? getDisplay(int);
+    method public android.view.Display![] getDisplays();
+    method public android.view.Display![] getDisplays(String?);
+    method public static androidx.core.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+    field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+  }
+
+}
+
+package androidx.core.hardware.fingerprint {
+
+  @Deprecated public class FingerprintManagerCompat {
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public void authenticate(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject?, int, androidx.core.os.CancellationSignal?, androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler?);
+    method @Deprecated public static androidx.core.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean hasEnrolledFingerprints();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean isHardwareDetected();
+  }
+
+  @Deprecated public abstract static class FingerprintManagerCompat.AuthenticationCallback {
+    ctor @Deprecated public FingerprintManagerCompat.AuthenticationCallback();
+    method @Deprecated public void onAuthenticationError(int, CharSequence!);
+    method @Deprecated public void onAuthenticationFailed();
+    method @Deprecated public void onAuthenticationHelp(int, CharSequence!);
+    method @Deprecated public void onAuthenticationSucceeded(androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult!);
+  }
+
+  @Deprecated public static final class FingerprintManagerCompat.AuthenticationResult {
+    ctor @Deprecated public FingerprintManagerCompat.AuthenticationResult(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject!);
+    method @Deprecated public androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject! getCryptoObject();
+  }
+
+  @Deprecated public static class FingerprintManagerCompat.CryptoObject {
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+    method @Deprecated public javax.crypto.Cipher? getCipher();
+    method @Deprecated public javax.crypto.Mac? getMac();
+    method @Deprecated public java.security.Signature? getSignature();
+  }
+
+}
+
+package androidx.core.location {
+
+  public abstract class GnssStatusCompat {
+    method @FloatRange(from=0, to=360) public abstract float getAzimuthDegrees(@IntRange(from=0) int);
+    method @FloatRange(from=0, to=63) public abstract float getBasebandCn0DbHz(@IntRange(from=0) int);
+    method @FloatRange(from=0) public abstract float getCarrierFrequencyHz(@IntRange(from=0) int);
+    method @FloatRange(from=0, to=63) public abstract float getCn0DbHz(@IntRange(from=0) int);
+    method public abstract int getConstellationType(@IntRange(from=0) int);
+    method @FloatRange(from=0xffffffa6, to=90) public abstract float getElevationDegrees(@IntRange(from=0) int);
+    method @IntRange(from=0) public abstract int getSatelliteCount();
+    method @IntRange(from=1, to=200) public abstract int getSvid(@IntRange(from=0) int);
+    method public abstract boolean hasAlmanacData(@IntRange(from=0) int);
+    method public abstract boolean hasBasebandCn0DbHz(@IntRange(from=0) int);
+    method public abstract boolean hasCarrierFrequencyHz(@IntRange(from=0) int);
+    method public abstract boolean hasEphemerisData(@IntRange(from=0) int);
+    method public abstract boolean usedInFix(@IntRange(from=0) int);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) public static androidx.core.location.GnssStatusCompat wrap(android.location.GnssStatus);
+    method public static androidx.core.location.GnssStatusCompat wrap(android.location.GpsStatus);
+    field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
+    field public static final int CONSTELLATION_GALILEO = 6; // 0x6
+    field public static final int CONSTELLATION_GLONASS = 3; // 0x3
+    field public static final int CONSTELLATION_GPS = 1; // 0x1
+    field public static final int CONSTELLATION_IRNSS = 7; // 0x7
+    field public static final int CONSTELLATION_QZSS = 4; // 0x4
+    field public static final int CONSTELLATION_SBAS = 2; // 0x2
+    field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
+  }
+
+  public abstract static class GnssStatusCompat.Callback {
+    ctor public GnssStatusCompat.Callback();
+    method public void onFirstFix(@IntRange(from=0) int);
+    method public void onSatelliteStatusChanged(androidx.core.location.GnssStatusCompat);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public final class LocationManagerCompat {
+    method public static boolean isLocationEnabled(android.location.LocationManager);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback, android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, java.util.concurrent.Executor, androidx.core.location.GnssStatusCompat.Callback);
+    method public static void unregisterGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback);
+  }
+
+}
+
+package androidx.core.math {
+
+  public class MathUtils {
+    method public static float clamp(float, float, float);
+    method public static double clamp(double, double, double);
+    method public static int clamp(int, int, int);
+    method public static long clamp(long, long, long);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class ConnectivityManagerCompat {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static android.net.NetworkInfo? getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+    method public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+  }
+
+  public final class MailTo {
+    method public String? getBcc();
+    method public String? getBody();
+    method public String? getCc();
+    method public java.util.Map<java.lang.String!,java.lang.String!>? getHeaders();
+    method public String? getSubject();
+    method public String? getTo();
+    method public static boolean isMailTo(String?);
+    method public static boolean isMailTo(android.net.Uri?);
+    method public static androidx.core.net.MailTo parse(String) throws androidx.core.net.ParseException;
+    method public static androidx.core.net.MailTo parse(android.net.Uri) throws androidx.core.net.ParseException;
+    field public static final String MAILTO_SCHEME = "mailto:";
+  }
+
+  public class ParseException extends java.lang.RuntimeException {
+    field public final String response;
+  }
+
+  public final class TrafficStatsCompat {
+    method @Deprecated public static void clearThreadStatsTag();
+    method @Deprecated public static int getThreadStatsTag();
+    method @Deprecated public static void incrementOperationCount(int);
+    method @Deprecated public static void incrementOperationCount(int, int);
+    method @Deprecated public static void setThreadStatsTag(int);
+    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method @Deprecated public static void tagSocket(java.net.Socket!) throws java.net.SocketException;
+    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method @Deprecated public static void untagSocket(java.net.Socket!) throws java.net.SocketException;
+  }
+
+  public final class UriCompat {
+    method public static String toSafeString(android.net.Uri);
+  }
+
+}
+
+package androidx.core.os {
+
+  public class BuildCompat {
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N) public static boolean isAtLeastN();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N_MR1) public static boolean isAtLeastNMR1();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O) public static boolean isAtLeastO();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O_MR1) public static boolean isAtLeastOMR1();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.P) public static boolean isAtLeastP();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
+    method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
+  }
+
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
+    method public void cancel();
+    method public Object? getCancellationSignalObject();
+    method public boolean isCanceled();
+    method public void setOnCancelListener(androidx.core.os.CancellationSignal.OnCancelListener?);
+    method public void throwIfCanceled();
+  }
+
+  public static interface CancellationSignal.OnCancelListener {
+    method public void onCancel();
+  }
+
+  public final class ConfigurationCompat {
+    method public static androidx.core.os.LocaleListCompat getLocales(android.content.res.Configuration);
+  }
+
+  public final class EnvironmentCompat {
+    method public static String getStorageState(java.io.File);
+    field public static final String MEDIA_UNKNOWN = "unknown";
+  }
+
+  public final class HandlerCompat {
+    method public static android.os.Handler createAsync(android.os.Looper);
+    method public static android.os.Handler createAsync(android.os.Looper, android.os.Handler.Callback);
+    method public static boolean postDelayed(android.os.Handler, Runnable, Object?, long);
+  }
+
+  public class HandlerExecutor implements java.util.concurrent.Executor {
+    ctor public HandlerExecutor(android.os.Handler);
+    method public void execute(Runnable);
+  }
+
+  public final class LocaleListCompat {
+    method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
+    method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
+    method public java.util.Locale! get(int);
+    method @Size(min=1) public static androidx.core.os.LocaleListCompat getAdjustedDefault();
+    method @Size(min=1) public static androidx.core.os.LocaleListCompat getDefault();
+    method public static androidx.core.os.LocaleListCompat getEmptyLocaleList();
+    method public java.util.Locale? getFirstMatch(String![]);
+    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale!);
+    method public boolean isEmpty();
+    method @IntRange(from=0) public int size();
+    method public String toLanguageTags();
+    method public Object? unwrap();
+    method @Deprecated @RequiresApi(24) public static androidx.core.os.LocaleListCompat! wrap(Object!);
+    method @RequiresApi(24) public static androidx.core.os.LocaleListCompat wrap(android.os.LocaleList);
+  }
+
+  public final class MessageCompat {
+    method public static boolean isAsynchronous(android.os.Message);
+    method public static void setAsynchronous(android.os.Message, boolean);
+  }
+
+  public class OperationCanceledException extends java.lang.RuntimeException {
+    ctor public OperationCanceledException();
+    ctor public OperationCanceledException(String?);
+  }
+
+  public final class ParcelCompat {
+    method public static boolean readBoolean(android.os.Parcel);
+    method public static void writeBoolean(android.os.Parcel, boolean);
+  }
+
+  @Deprecated public final class ParcelableCompat {
+    method @Deprecated public static <T> android.os.Parcelable.Creator<T!>! newCreator(androidx.core.os.ParcelableCompatCreatorCallbacks<T!>!);
+  }
+
+  @Deprecated public interface ParcelableCompatCreatorCallbacks<T> {
+    method @Deprecated public T! createFromParcel(android.os.Parcel!, ClassLoader!);
+    method @Deprecated public T![]! newArray(int);
+  }
+
+  public final class ProcessCompat {
+    method public static boolean isApplicationUid(int);
+  }
+
+  @Deprecated public final class TraceCompat {
+    method @Deprecated public static void beginAsyncSection(String, int);
+    method @Deprecated public static void beginSection(String);
+    method @Deprecated public static void endAsyncSection(String, int);
+    method @Deprecated public static void endSection();
+    method @Deprecated public static boolean isEnabled();
+    method @Deprecated public static void setCounter(String, int);
+  }
+
+  public class UserManagerCompat {
+    method public static boolean isUserUnlocked(android.content.Context);
+  }
+
+}
+
+package androidx.core.provider {
+
+  public final class FontRequest {
+    ctor public FontRequest(String, String, String, java.util.List<java.util.List<byte[]!>!>);
+    ctor public FontRequest(String, String, String, @ArrayRes int);
+    method public java.util.List<java.util.List<byte[]!>!>? getCertificates();
+    method @ArrayRes public int getCertificatesArrayResId();
+    method public String getProviderAuthority();
+    method public String getProviderPackage();
+    method public String getQuery();
+  }
+
+  public class FontsContractCompat {
+    method public static android.graphics.Typeface? buildTypeface(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![]);
+    method public static androidx.core.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+  }
+
+  public static final class FontsContractCompat.Columns implements android.provider.BaseColumns {
+    ctor public FontsContractCompat.Columns();
+    field public static final String FILE_ID = "file_id";
+    field public static final String ITALIC = "font_italic";
+    field public static final String RESULT_CODE = "result_code";
+    field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3
+    field public static final int RESULT_CODE_OK = 0; // 0x0
+    field public static final String TTC_INDEX = "font_ttc_index";
+    field public static final String VARIATION_SETTINGS = "font_variation_settings";
+    field public static final String WEIGHT = "font_weight";
+  }
+
+  public static class FontsContractCompat.FontFamilyResult {
+    method public androidx.core.provider.FontsContractCompat.FontInfo![]! getFonts();
+    method public int getStatusCode();
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
+    field public static final int STATUS_WRONG_CERTIFICATES = 1; // 0x1
+  }
+
+  public static class FontsContractCompat.FontInfo {
+    method public int getResultCode();
+    method @IntRange(from=0) public int getTtcIndex();
+    method public android.net.Uri getUri();
+    method @IntRange(from=1, to=1000) public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static class FontsContractCompat.FontRequestCallback {
+    ctor public FontsContractCompat.FontRequestCallback();
+    method public void onTypefaceRequestFailed(int);
+    method public void onTypefaceRetrieved(android.graphics.Typeface!);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+    field public static final int FAIL_REASON_SECURITY_VIOLATION = -4; // 0xfffffffc
+    field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+  }
+
+}
+
+package androidx.core.telephony.mbms {
+
+  public final class MbmsHelper {
+    method public static CharSequence? getBestNameForService(android.content.Context, android.telephony.mbms.ServiceInfo);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class BidiFormatter {
+    method public static androidx.core.text.BidiFormatter! getInstance();
+    method public static androidx.core.text.BidiFormatter! getInstance(boolean);
+    method public static androidx.core.text.BidiFormatter! getInstance(java.util.Locale!);
+    method public boolean getStereoReset();
+    method public boolean isRtl(String!);
+    method public boolean isRtl(CharSequence!);
+    method public boolean isRtlContext();
+    method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+    method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+    method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!);
+    method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!);
+    method public String! unicodeWrap(String!, boolean);
+    method public CharSequence! unicodeWrap(CharSequence!, boolean);
+    method public String! unicodeWrap(String!);
+    method public CharSequence! unicodeWrap(CharSequence!);
+  }
+
+  public static final class BidiFormatter.Builder {
+    ctor public BidiFormatter.Builder();
+    ctor public BidiFormatter.Builder(boolean);
+    ctor public BidiFormatter.Builder(java.util.Locale!);
+    method public androidx.core.text.BidiFormatter! build();
+    method public androidx.core.text.BidiFormatter.Builder! setTextDirectionHeuristic(androidx.core.text.TextDirectionHeuristicCompat!);
+    method public androidx.core.text.BidiFormatter.Builder! stereoReset(boolean);
+  }
+
+  public final class HtmlCompat {
+    method public static android.text.Spanned fromHtml(String, int);
+    method public static android.text.Spanned fromHtml(String, int, android.text.Html.ImageGetter?, android.text.Html.TagHandler?);
+    method public static String toHtml(android.text.Spanned, int);
+    field public static final int FROM_HTML_MODE_COMPACT = 63; // 0x3f
+    field public static final int FROM_HTML_MODE_LEGACY = 0; // 0x0
+    field public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 256; // 0x100
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 32; // 0x20
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 16; // 0x10
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 2; // 0x2
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 8; // 0x8
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 4; // 0x4
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 1; // 0x1
+    field public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0; // 0x0
+    field public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 1; // 0x1
+  }
+
+  public final class ICUCompat {
+    method public static String? maximizeAndGetScript(java.util.Locale!);
+  }
+
+  public class PrecomputedTextCompat implements android.text.Spannable {
+    method public char charAt(int);
+    method public static androidx.core.text.PrecomputedTextCompat! create(CharSequence, androidx.core.text.PrecomputedTextCompat.Params);
+    method @IntRange(from=0) public int getParagraphCount();
+    method @IntRange(from=0) public int getParagraphEnd(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getParagraphStart(@IntRange(from=0) int);
+    method public androidx.core.text.PrecomputedTextCompat.Params getParams();
+    method public int getSpanEnd(Object!);
+    method public int getSpanFlags(Object!);
+    method public int getSpanStart(Object!);
+    method public <T> T![]! getSpans(int, int, Class<T!>!);
+    method @UiThread public static java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>! getTextFuture(CharSequence, androidx.core.text.PrecomputedTextCompat.Params, java.util.concurrent.Executor?);
+    method public int length();
+    method public int nextSpanTransition(int, int, Class!);
+    method public void removeSpan(Object!);
+    method public void setSpan(Object!, int, int, int);
+    method public CharSequence! subSequence(int, int);
+  }
+
+  public static final class PrecomputedTextCompat.Params {
+    ctor @RequiresApi(28) public PrecomputedTextCompat.Params(android.text.PrecomputedText.Params);
+    method @RequiresApi(23) public int getBreakStrategy();
+    method @RequiresApi(23) public int getHyphenationFrequency();
+    method @RequiresApi(18) public android.text.TextDirectionHeuristic? getTextDirection();
+    method public android.text.TextPaint getTextPaint();
+  }
+
+  public static class PrecomputedTextCompat.Params.Builder {
+    ctor public PrecomputedTextCompat.Params.Builder(android.text.TextPaint);
+    method public androidx.core.text.PrecomputedTextCompat.Params build();
+    method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setBreakStrategy(int);
+    method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setHyphenationFrequency(int);
+    method @RequiresApi(18) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setTextDirection(android.text.TextDirectionHeuristic);
+  }
+
+  public interface TextDirectionHeuristicCompat {
+    method public boolean isRtl(char[]!, int, int);
+    method public boolean isRtl(CharSequence!, int, int);
+  }
+
+  public final class TextDirectionHeuristicsCompat {
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! ANYRTL_LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_RTL;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! LOCALE;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! RTL;
+  }
+
+  public final class TextUtilsCompat {
+    method public static int getLayoutDirectionFromLocale(java.util.Locale?);
+    method public static String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.text.util {
+
+  public final class LinkifyCompat {
+    method public static boolean addLinks(android.text.Spannable, int);
+    method public static boolean addLinks(android.widget.TextView, int);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+  }
+
+}
+
+package androidx.core.util {
+
+  public class AtomicFile {
+    ctor public AtomicFile(java.io.File);
+    method public void delete();
+    method public void failWrite(java.io.FileOutputStream?);
+    method public void finishWrite(java.io.FileOutputStream?);
+    method public java.io.File getBaseFile();
+    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+    method public byte[] readFully() throws java.io.IOException;
+    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+  }
+
+  public interface Consumer<T> {
+    method public void accept(T!);
+  }
+
+  public class ObjectsCompat {
+    method public static boolean equals(Object?, Object?);
+    method public static int hash(java.lang.Object!...);
+    method public static int hashCode(Object?);
+    method public static String? toString(Object?, String?);
+  }
+
+  public class Pair<F, S> {
+    ctor public Pair(F!, S!);
+    method public static <A, B> androidx.core.util.Pair<A!,B!> create(A!, B!);
+    field public final F! first;
+    field public final S! second;
+  }
+
+  public final class PatternsCompat {
+    field public static final java.util.regex.Pattern DOMAIN_NAME;
+    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field public static final java.util.regex.Pattern WEB_URL;
+  }
+
+  public final class Pools {
+  }
+
+  public static interface Pools.Pool<T> {
+    method public T? acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SimplePool<T> implements androidx.core.util.Pools.Pool<T> {
+    ctor public Pools.SimplePool(int);
+    method public T! acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SynchronizedPool<T> extends androidx.core.util.Pools.SimplePool<T> {
+    ctor public Pools.SynchronizedPool(int);
+  }
+
+  public interface Predicate<T> {
+    method public boolean test(T!);
+  }
+
+  public interface Supplier<T> {
+    method public T! get();
+  }
+
+}
+
+package androidx.core.view {
+
+  public class AccessibilityDelegateCompat {
+    ctor public AccessibilityDelegateCompat();
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat! getAccessibilityNodeProvider(android.view.View!);
+    method public void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public void onInitializeAccessibilityNodeInfo(android.view.View!, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method public void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public boolean performAccessibilityAction(android.view.View!, int, android.os.Bundle!);
+    method public void sendAccessibilityEvent(android.view.View!, int);
+    method public void sendAccessibilityEventUnchecked(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+  }
+
+  public abstract class ActionProvider {
+    ctor public ActionProvider(android.content.Context!);
+    method public android.content.Context! getContext();
+    method public boolean hasSubMenu();
+    method public boolean isVisible();
+    method public abstract android.view.View! onCreateActionView();
+    method public android.view.View! onCreateActionView(android.view.MenuItem!);
+    method public boolean onPerformDefaultAction();
+    method public void onPrepareSubMenu(android.view.SubMenu!);
+    method public boolean overridesItemVisibility();
+    method public void refreshVisibility();
+    method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener!);
+  }
+
+  public static interface ActionProvider.VisibilityListener {
+    method public void onActionProviderVisibilityChanged(boolean);
+  }
+
+  public final class ContentInfoCompat {
+    method public android.content.ClipData getClip();
+    method public android.os.Bundle? getExtras();
+    method public int getFlags();
+    method public android.net.Uri? getLinkUri();
+    method public int getSource();
+    method public android.util.Pair<androidx.core.view.ContentInfoCompat!,androidx.core.view.ContentInfoCompat!> partition(androidx.core.util.Predicate<android.content.ClipData.Item!>);
+    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+  }
+
+  public static final class ContentInfoCompat.Builder {
+    ctor public ContentInfoCompat.Builder(androidx.core.view.ContentInfoCompat);
+    ctor public ContentInfoCompat.Builder(android.content.ClipData, int);
+    method public androidx.core.view.ContentInfoCompat build();
+    method public androidx.core.view.ContentInfoCompat.Builder setClip(android.content.ClipData);
+    method public androidx.core.view.ContentInfoCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.view.ContentInfoCompat.Builder setFlags(int);
+    method public androidx.core.view.ContentInfoCompat.Builder setLinkUri(android.net.Uri?);
+    method public androidx.core.view.ContentInfoCompat.Builder setSource(int);
+  }
+
+  public final class DisplayCompat {
+    method public static androidx.core.view.DisplayCompat.ModeCompat![] getSupportedModes(android.content.Context, android.view.Display);
+  }
+
+  public static final class DisplayCompat.ModeCompat {
+    method public int getPhysicalHeight();
+    method public int getPhysicalWidth();
+    method public boolean isNative();
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public android.view.Display.Mode? toMode();
+  }
+
+  public final class DisplayCutoutCompat {
+    ctor public DisplayCutoutCompat(android.graphics.Rect!, java.util.List<android.graphics.Rect!>!);
+    ctor public DisplayCutoutCompat(androidx.core.graphics.Insets, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, androidx.core.graphics.Insets);
+    method public java.util.List<android.graphics.Rect!> getBoundingRects();
+    method public int getSafeInsetBottom();
+    method public int getSafeInsetLeft();
+    method public int getSafeInsetRight();
+    method public int getSafeInsetTop();
+    method public androidx.core.graphics.Insets getWaterfallInsets();
+  }
+
+  public final class DragAndDropPermissionsCompat {
+    method public void release();
+  }
+
+  public class DragStartHelper {
+    ctor public DragStartHelper(android.view.View!, androidx.core.view.DragStartHelper.OnDragStartListener!);
+    method public void attach();
+    method public void detach();
+    method public void getTouchPosition(android.graphics.Point!);
+    method public boolean onLongClick(android.view.View!);
+    method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+  }
+
+  public static interface DragStartHelper.OnDragStartListener {
+    method public boolean onDragStart(android.view.View!, androidx.core.view.DragStartHelper!);
+  }
+
+  public final class GestureDetectorCompat {
+    ctor public GestureDetectorCompat(android.content.Context!, android.view.GestureDetector.OnGestureListener!);
+    ctor public GestureDetectorCompat(android.content.Context!, android.view.GestureDetector.OnGestureListener!, android.os.Handler!);
+    method public boolean isLongpressEnabled();
+    method public boolean onTouchEvent(android.view.MotionEvent!);
+    method public void setIsLongpressEnabled(boolean);
+    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener!);
+  }
+
+  public final class GravityCompat {
+    method public static void apply(int, int, int, android.graphics.Rect!, android.graphics.Rect!, int);
+    method public static void apply(int, int, int, android.graphics.Rect!, int, int, android.graphics.Rect!, int);
+    method public static void applyDisplay(int, android.graphics.Rect!, android.graphics.Rect!, int);
+    method public static int getAbsoluteGravity(int, int);
+    field public static final int END = 8388613; // 0x800005
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+    field public static final int START = 8388611; // 0x800003
+  }
+
+  public final class InputDeviceCompat {
+    field public static final int SOURCE_ANY = -256; // 0xffffff00
+    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+    field public static final int SOURCE_DPAD = 513; // 0x201
+    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+    field public static final int SOURCE_KEYBOARD = 257; // 0x101
+    field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+    field public static final int SOURCE_STYLUS = 16386; // 0x4002
+    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+    field public static final int SOURCE_UNKNOWN = 0; // 0x0
+  }
+
+  public final class LayoutInflaterCompat {
+    method @Deprecated public static androidx.core.view.LayoutInflaterFactory! getFactory(android.view.LayoutInflater!);
+    method @Deprecated public static void setFactory(android.view.LayoutInflater, androidx.core.view.LayoutInflaterFactory);
+    method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+  }
+
+  @Deprecated public interface LayoutInflaterFactory {
+    method @Deprecated public android.view.View! onCreateView(android.view.View!, String!, android.content.Context!, android.util.AttributeSet!);
+  }
+
+  public final class MarginLayoutParamsCompat {
+    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams!);
+    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams!);
+    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams!);
+    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams!);
+    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams!, int);
+    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams!, int);
+    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams!, int);
+    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams!, int);
+  }
+
+  public final class MenuCompat {
+    method public static void setGroupDividerEnabled(android.view.Menu!, boolean);
+    method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+  }
+
+  public final class MenuItemCompat {
+    method @Deprecated public static boolean collapseActionView(android.view.MenuItem!);
+    method @Deprecated public static boolean expandActionView(android.view.MenuItem!);
+    method public static androidx.core.view.ActionProvider! getActionProvider(android.view.MenuItem!);
+    method @Deprecated public static android.view.View! getActionView(android.view.MenuItem!);
+    method public static int getAlphabeticModifiers(android.view.MenuItem!);
+    method public static CharSequence! getContentDescription(android.view.MenuItem!);
+    method public static android.content.res.ColorStateList! getIconTintList(android.view.MenuItem!);
+    method public static android.graphics.PorterDuff.Mode! getIconTintMode(android.view.MenuItem!);
+    method public static int getNumericModifiers(android.view.MenuItem!);
+    method public static CharSequence! getTooltipText(android.view.MenuItem!);
+    method @Deprecated public static boolean isActionViewExpanded(android.view.MenuItem!);
+    method public static android.view.MenuItem! setActionProvider(android.view.MenuItem!, androidx.core.view.ActionProvider!);
+    method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, android.view.View!);
+    method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, int);
+    method public static void setAlphabeticShortcut(android.view.MenuItem!, char, int);
+    method public static void setContentDescription(android.view.MenuItem!, CharSequence!);
+    method public static void setIconTintList(android.view.MenuItem!, android.content.res.ColorStateList!);
+    method public static void setIconTintMode(android.view.MenuItem!, android.graphics.PorterDuff.Mode!);
+    method public static void setNumericShortcut(android.view.MenuItem!, char, int);
+    method @Deprecated public static android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem!, androidx.core.view.MenuItemCompat.OnActionExpandListener!);
+    method public static void setShortcut(android.view.MenuItem!, char, char, int, int);
+    method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+    method public static void setTooltipText(android.view.MenuItem!, CharSequence!);
+    field @Deprecated public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field @Deprecated public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field @Deprecated public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field @Deprecated public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field @Deprecated public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  @Deprecated public static interface MenuItemCompat.OnActionExpandListener {
+    method @Deprecated public boolean onMenuItemActionCollapse(android.view.MenuItem!);
+    method @Deprecated public boolean onMenuItemActionExpand(android.view.MenuItem!);
+  }
+
+  public final class MotionEventCompat {
+    method @Deprecated public static int findPointerIndex(android.view.MotionEvent!, int);
+    method @Deprecated public static int getActionIndex(android.view.MotionEvent!);
+    method @Deprecated public static int getActionMasked(android.view.MotionEvent!);
+    method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int);
+    method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int, int);
+    method @Deprecated public static int getButtonState(android.view.MotionEvent!);
+    method @Deprecated public static int getPointerCount(android.view.MotionEvent!);
+    method @Deprecated public static int getPointerId(android.view.MotionEvent!, int);
+    method @Deprecated public static int getSource(android.view.MotionEvent!);
+    method @Deprecated public static float getX(android.view.MotionEvent!, int);
+    method @Deprecated public static float getY(android.view.MotionEvent!, int);
+    method public static boolean isFromSource(android.view.MotionEvent!, int);
+    field @Deprecated public static final int ACTION_HOVER_ENTER = 9; // 0x9
+    field @Deprecated public static final int ACTION_HOVER_EXIT = 10; // 0xa
+    field @Deprecated public static final int ACTION_HOVER_MOVE = 7; // 0x7
+    field @Deprecated public static final int ACTION_MASK = 255; // 0xff
+    field @Deprecated public static final int ACTION_POINTER_DOWN = 5; // 0x5
+    field @Deprecated public static final int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+    field @Deprecated public static final int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+    field @Deprecated public static final int ACTION_POINTER_UP = 6; // 0x6
+    field @Deprecated public static final int ACTION_SCROLL = 8; // 0x8
+    field @Deprecated public static final int AXIS_BRAKE = 23; // 0x17
+    field @Deprecated public static final int AXIS_DISTANCE = 24; // 0x18
+    field @Deprecated public static final int AXIS_GAS = 22; // 0x16
+    field @Deprecated public static final int AXIS_GENERIC_1 = 32; // 0x20
+    field @Deprecated public static final int AXIS_GENERIC_10 = 41; // 0x29
+    field @Deprecated public static final int AXIS_GENERIC_11 = 42; // 0x2a
+    field @Deprecated public static final int AXIS_GENERIC_12 = 43; // 0x2b
+    field @Deprecated public static final int AXIS_GENERIC_13 = 44; // 0x2c
+    field @Deprecated public static final int AXIS_GENERIC_14 = 45; // 0x2d
+    field @Deprecated public static final int AXIS_GENERIC_15 = 46; // 0x2e
+    field @Deprecated public static final int AXIS_GENERIC_16 = 47; // 0x2f
+    field @Deprecated public static final int AXIS_GENERIC_2 = 33; // 0x21
+    field @Deprecated public static final int AXIS_GENERIC_3 = 34; // 0x22
+    field @Deprecated public static final int AXIS_GENERIC_4 = 35; // 0x23
+    field @Deprecated public static final int AXIS_GENERIC_5 = 36; // 0x24
+    field @Deprecated public static final int AXIS_GENERIC_6 = 37; // 0x25
+    field @Deprecated public static final int AXIS_GENERIC_7 = 38; // 0x26
+    field @Deprecated public static final int AXIS_GENERIC_8 = 39; // 0x27
+    field @Deprecated public static final int AXIS_GENERIC_9 = 40; // 0x28
+    field @Deprecated public static final int AXIS_HAT_X = 15; // 0xf
+    field @Deprecated public static final int AXIS_HAT_Y = 16; // 0x10
+    field @Deprecated public static final int AXIS_HSCROLL = 10; // 0xa
+    field @Deprecated public static final int AXIS_LTRIGGER = 17; // 0x11
+    field @Deprecated public static final int AXIS_ORIENTATION = 8; // 0x8
+    field @Deprecated public static final int AXIS_PRESSURE = 2; // 0x2
+    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+    field @Deprecated public static final int AXIS_RTRIGGER = 18; // 0x12
+    field @Deprecated public static final int AXIS_RUDDER = 20; // 0x14
+    field @Deprecated public static final int AXIS_RX = 12; // 0xc
+    field @Deprecated public static final int AXIS_RY = 13; // 0xd
+    field @Deprecated public static final int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
+    field @Deprecated public static final int AXIS_SIZE = 3; // 0x3
+    field @Deprecated public static final int AXIS_THROTTLE = 19; // 0x13
+    field @Deprecated public static final int AXIS_TILT = 25; // 0x19
+    field @Deprecated public static final int AXIS_TOOL_MAJOR = 6; // 0x6
+    field @Deprecated public static final int AXIS_TOOL_MINOR = 7; // 0x7
+    field @Deprecated public static final int AXIS_TOUCH_MAJOR = 4; // 0x4
+    field @Deprecated public static final int AXIS_TOUCH_MINOR = 5; // 0x5
+    field @Deprecated public static final int AXIS_VSCROLL = 9; // 0x9
+    field @Deprecated public static final int AXIS_WHEEL = 21; // 0x15
+    field @Deprecated public static final int AXIS_X = 0; // 0x0
+    field @Deprecated public static final int AXIS_Y = 1; // 0x1
+    field @Deprecated public static final int AXIS_Z = 11; // 0xb
+    field @Deprecated public static final int BUTTON_PRIMARY = 1; // 0x1
+  }
+
+  public interface NestedScrollingChild {
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+    method public boolean hasNestedScrollingParent();
+    method public boolean isNestedScrollingEnabled();
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public void stopNestedScroll();
+  }
+
+  public interface NestedScrollingChild2 extends androidx.core.view.NestedScrollingChild {
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public interface NestedScrollingChild3 extends androidx.core.view.NestedScrollingChild2 {
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]);
+  }
+
+  public class NestedScrollingChildHelper {
+    ctor public NestedScrollingChildHelper(android.view.View);
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]?);
+    method public boolean hasNestedScrollingParent();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isNestedScrollingEnabled();
+    method public void onDetachedFromWindow();
+    method public void onStopNestedScroll(android.view.View);
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll();
+    method public void stopNestedScroll(int);
+  }
+
+  public interface NestedScrollingParent {
+    method public int getNestedScrollAxes();
+    method public boolean onNestedFling(android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.view.View, float, float);
+    method public void onNestedPreScroll(android.view.View, int, int, int[]);
+    method public void onNestedScroll(android.view.View, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.view.View);
+  }
+
+  public interface NestedScrollingParent2 extends androidx.core.view.NestedScrollingParent {
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+  }
+
+  public interface NestedScrollingParent3 extends androidx.core.view.NestedScrollingParent2 {
+    method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]);
+  }
+
+  public class NestedScrollingParentHelper {
+    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+    method public int getNestedScrollAxes();
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View);
+    method public void onStopNestedScroll(android.view.View, int);
+  }
+
+  public interface OnApplyWindowInsetsListener {
+    method public androidx.core.view.WindowInsetsCompat! onApplyWindowInsets(android.view.View!, androidx.core.view.WindowInsetsCompat!);
+  }
+
+  public interface OnReceiveContentListener {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+  }
+
+  public interface OnReceiveContentViewBehavior {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+  }
+
+  public final class OneShotPreDrawListener implements android.view.View.OnAttachStateChangeListener android.view.ViewTreeObserver.OnPreDrawListener {
+    method public static androidx.core.view.OneShotPreDrawListener add(android.view.View, Runnable);
+    method public boolean onPreDraw();
+    method public void onViewAttachedToWindow(android.view.View!);
+    method public void onViewDetachedFromWindow(android.view.View!);
+    method public void removeListener();
+  }
+
+  public final class PointerIconCompat {
+    method public static androidx.core.view.PointerIconCompat! create(android.graphics.Bitmap!, float, float);
+    method public static androidx.core.view.PointerIconCompat! getSystemIcon(android.content.Context!, int);
+    method public static androidx.core.view.PointerIconCompat! load(android.content.res.Resources!, int);
+    field public static final int TYPE_ALIAS = 1010; // 0x3f2
+    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int TYPE_ARROW = 1000; // 0x3e8
+    field public static final int TYPE_CELL = 1006; // 0x3ee
+    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int TYPE_COPY = 1011; // 0x3f3
+    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+    field public static final int TYPE_GRAB = 1020; // 0x3fc
+    field public static final int TYPE_GRABBING = 1021; // 0x3fd
+    field public static final int TYPE_HAND = 1002; // 0x3ea
+    field public static final int TYPE_HELP = 1003; // 0x3eb
+    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_TEXT = 1008; // 0x3f0
+    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int TYPE_WAIT = 1004; // 0x3ec
+    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
+  public final class ScaleGestureDetectorCompat {
+    method @Deprecated public static boolean isQuickScaleEnabled(Object!);
+    method public static boolean isQuickScaleEnabled(android.view.ScaleGestureDetector!);
+    method @Deprecated public static void setQuickScaleEnabled(Object!, boolean);
+    method public static void setQuickScaleEnabled(android.view.ScaleGestureDetector!, boolean);
+  }
+
+  public interface ScrollingView {
+    method public int computeHorizontalScrollExtent();
+    method public int computeHorizontalScrollOffset();
+    method public int computeHorizontalScrollRange();
+    method public int computeVerticalScrollExtent();
+    method public int computeVerticalScrollOffset();
+    method public int computeVerticalScrollRange();
+  }
+
+  public interface TintableBackgroundView {
+    method public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  @Deprecated public final class VelocityTrackerCompat {
+    method @Deprecated public static float getXVelocity(android.view.VelocityTracker!, int);
+    method @Deprecated public static float getYVelocity(android.view.VelocityTracker!, int);
+  }
+
+  public class ViewCompat {
+    ctor @Deprecated protected ViewCompat();
+    method public static int addAccessibilityAction(android.view.View, CharSequence, androidx.core.view.accessibility.AccessibilityViewCommand);
+    method public static void addKeyboardNavigationClusters(android.view.View, java.util.Collection<android.view.View!>, int);
+    method public static void addOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+    method public static androidx.core.view.ViewPropertyAnimatorCompat animate(android.view.View);
+    method @Deprecated public static boolean canScrollHorizontally(android.view.View!, int);
+    method @Deprecated public static boolean canScrollVertically(android.view.View!, int);
+    method public static void cancelDragAndDrop(android.view.View);
+    method @Deprecated public static int combineMeasuredStates(int, int);
+    method public static androidx.core.view.WindowInsetsCompat computeSystemWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat, android.graphics.Rect);
+    method public static androidx.core.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+    method public static void dispatchFinishTemporaryDetach(android.view.View);
+    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?, int);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?);
+    method public static void dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, int, int[]);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, int);
+    method public static void dispatchStartTemporaryDetach(android.view.View);
+    method public static void enableAccessibleClickableSpanSupport(android.view.View!);
+    method public static int generateViewId();
+    method public static androidx.core.view.AccessibilityDelegateCompat? getAccessibilityDelegate(android.view.View);
+    method public static int getAccessibilityLiveRegion(android.view.View);
+    method public static androidx.core.view.accessibility.AccessibilityNodeProviderCompat! getAccessibilityNodeProvider(android.view.View);
+    method @UiThread public static CharSequence! getAccessibilityPaneTitle(android.view.View!);
+    method @Deprecated public static float getAlpha(android.view.View!);
+    method public static android.content.res.ColorStateList! getBackgroundTintList(android.view.View);
+    method public static android.graphics.PorterDuff.Mode! getBackgroundTintMode(android.view.View);
+    method public static android.graphics.Rect? getClipBounds(android.view.View);
+    method public static android.view.Display? getDisplay(android.view.View);
+    method public static float getElevation(android.view.View);
+    method public static boolean getFitsSystemWindows(android.view.View);
+    method public static int getImportantForAccessibility(android.view.View);
+    method public static int getImportantForAutofill(android.view.View);
+    method public static int getLabelFor(android.view.View);
+    method @Deprecated public static int getLayerType(android.view.View!);
+    method public static int getLayoutDirection(android.view.View);
+    method @Deprecated public static android.graphics.Matrix? getMatrix(android.view.View!);
+    method @Deprecated public static int getMeasuredHeightAndState(android.view.View!);
+    method @Deprecated public static int getMeasuredState(android.view.View!);
+    method @Deprecated public static int getMeasuredWidthAndState(android.view.View!);
+    method public static int getMinimumHeight(android.view.View);
+    method public static int getMinimumWidth(android.view.View);
+    method public static int getNextClusterForwardId(android.view.View);
+    method public static String![]? getOnReceiveContentMimeTypes(android.view.View);
+    method @Deprecated public static int getOverScrollMode(android.view.View!);
+    method @Px public static int getPaddingEnd(android.view.View);
+    method @Px public static int getPaddingStart(android.view.View);
+    method public static android.view.ViewParent! getParentForAccessibility(android.view.View);
+    method @Deprecated public static float getPivotX(android.view.View!);
+    method @Deprecated public static float getPivotY(android.view.View!);
+    method public static androidx.core.view.WindowInsetsCompat? getRootWindowInsets(android.view.View);
+    method @Deprecated public static float getRotation(android.view.View!);
+    method @Deprecated public static float getRotationX(android.view.View!);
+    method @Deprecated public static float getRotationY(android.view.View!);
+    method @Deprecated public static float getScaleX(android.view.View!);
+    method @Deprecated public static float getScaleY(android.view.View!);
+    method public static int getScrollIndicators(android.view.View);
+    method @UiThread public static final CharSequence? getStateDescription(android.view.View);
+    method public static java.util.List<android.graphics.Rect!> getSystemGestureExclusionRects(android.view.View);
+    method public static String? getTransitionName(android.view.View);
+    method @Deprecated public static float getTranslationX(android.view.View!);
+    method @Deprecated public static float getTranslationY(android.view.View!);
+    method public static float getTranslationZ(android.view.View);
+    method public static androidx.core.view.WindowInsetsControllerCompat? getWindowInsetsController(android.view.View);
+    method public static int getWindowSystemUiVisibility(android.view.View);
+    method @Deprecated public static float getX(android.view.View!);
+    method @Deprecated public static float getY(android.view.View!);
+    method public static float getZ(android.view.View);
+    method public static boolean hasAccessibilityDelegate(android.view.View);
+    method public static boolean hasExplicitFocusable(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View, int);
+    method public static boolean hasOnClickListeners(android.view.View);
+    method public static boolean hasOverlappingRendering(android.view.View);
+    method public static boolean hasTransientState(android.view.View);
+    method @UiThread public static boolean isAccessibilityHeading(android.view.View!);
+    method public static boolean isAttachedToWindow(android.view.View);
+    method public static boolean isFocusedByDefault(android.view.View);
+    method public static boolean isImportantForAccessibility(android.view.View);
+    method public static boolean isImportantForAutofill(android.view.View);
+    method public static boolean isInLayout(android.view.View);
+    method public static boolean isKeyboardNavigationCluster(android.view.View);
+    method public static boolean isLaidOut(android.view.View);
+    method public static boolean isLayoutDirectionResolved(android.view.View);
+    method public static boolean isNestedScrollingEnabled(android.view.View);
+    method @Deprecated public static boolean isOpaque(android.view.View!);
+    method public static boolean isPaddingRelative(android.view.View);
+    method @UiThread public static boolean isScreenReaderFocusable(android.view.View!);
+    method @Deprecated public static void jumpDrawablesToCurrentState(android.view.View!);
+    method public static android.view.View! keyboardNavigationClusterSearch(android.view.View, android.view.View!, int);
+    method public static void offsetLeftAndRight(android.view.View, int);
+    method public static void offsetTopAndBottom(android.view.View, int);
+    method public static androidx.core.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+    method @Deprecated public static void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method @Deprecated public static void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle!);
+    method public static androidx.core.view.ContentInfoCompat? performReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+    method public static void postInvalidateOnAnimation(android.view.View);
+    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+    method public static void postOnAnimation(android.view.View, Runnable!);
+    method public static void postOnAnimationDelayed(android.view.View, Runnable!, long);
+    method public static void removeAccessibilityAction(android.view.View, int);
+    method public static void removeOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+    method public static void replaceAccessibilityAction(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat, CharSequence?, androidx.core.view.accessibility.AccessibilityViewCommand?);
+    method public static void requestApplyInsets(android.view.View);
+    method public static <T extends android.view.View> T requireViewById(android.view.View, @IdRes int);
+    method @Deprecated public static int resolveSizeAndState(int, int, int);
+    method public static boolean restoreDefaultFocus(android.view.View);
+    method public static void saveAttributeDataForStyleable(android.view.View, android.content.Context, int[], android.util.AttributeSet?, android.content.res.TypedArray, int, int);
+    method public static void setAccessibilityDelegate(android.view.View, androidx.core.view.AccessibilityDelegateCompat!);
+    method @UiThread public static void setAccessibilityHeading(android.view.View!, boolean);
+    method public static void setAccessibilityLiveRegion(android.view.View, int);
+    method @UiThread public static void setAccessibilityPaneTitle(android.view.View!, CharSequence!);
+    method @Deprecated public static void setActivated(android.view.View!, boolean);
+    method @Deprecated public static void setAlpha(android.view.View!, @FloatRange(from=0.0, to=1.0) float);
+    method public static void setAutofillHints(android.view.View, java.lang.String!...);
+    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable?);
+    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList!);
+    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode!);
+    method @Deprecated public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup!, boolean);
+    method public static void setClipBounds(android.view.View, android.graphics.Rect!);
+    method public static void setElevation(android.view.View, float);
+    method @Deprecated public static void setFitsSystemWindows(android.view.View!, boolean);
+    method public static void setFocusedByDefault(android.view.View, boolean);
+    method public static void setHasTransientState(android.view.View, boolean);
+    method public static void setImportantForAccessibility(android.view.View, int);
+    method public static void setImportantForAutofill(android.view.View, int);
+    method public static void setKeyboardNavigationCluster(android.view.View, boolean);
+    method public static void setLabelFor(android.view.View, @IdRes int);
+    method public static void setLayerPaint(android.view.View, android.graphics.Paint!);
+    method @Deprecated public static void setLayerType(android.view.View!, int, android.graphics.Paint!);
+    method public static void setLayoutDirection(android.view.View, int);
+    method public static void setNestedScrollingEnabled(android.view.View, boolean);
+    method public static void setNextClusterForwardId(android.view.View, int);
+    method public static void setOnApplyWindowInsetsListener(android.view.View, androidx.core.view.OnApplyWindowInsetsListener?);
+    method public static void setOnReceiveContentListener(android.view.View, String![]?, androidx.core.view.OnReceiveContentListener?);
+    method @Deprecated public static void setOverScrollMode(android.view.View!, int);
+    method public static void setPaddingRelative(android.view.View, @Px int, @Px int, @Px int, @Px int);
+    method @Deprecated public static void setPivotX(android.view.View!, float);
+    method @Deprecated public static void setPivotY(android.view.View!, float);
+    method public static void setPointerIcon(android.view.View, androidx.core.view.PointerIconCompat!);
+    method @Deprecated public static void setRotation(android.view.View!, float);
+    method @Deprecated public static void setRotationX(android.view.View!, float);
+    method @Deprecated public static void setRotationY(android.view.View!, float);
+    method @Deprecated public static void setSaveFromParentEnabled(android.view.View!, boolean);
+    method @Deprecated public static void setScaleX(android.view.View!, float);
+    method @Deprecated public static void setScaleY(android.view.View!, float);
+    method @UiThread public static void setScreenReaderFocusable(android.view.View!, boolean);
+    method public static void setScrollIndicators(android.view.View, int);
+    method public static void setScrollIndicators(android.view.View, int, int);
+    method @UiThread public static void setStateDescription(android.view.View, CharSequence?);
+    method public static void setSystemGestureExclusionRects(android.view.View, java.util.List<android.graphics.Rect!>);
+    method public static void setTooltipText(android.view.View, CharSequence?);
+    method public static void setTransitionName(android.view.View, String!);
+    method @Deprecated public static void setTranslationX(android.view.View!, float);
+    method @Deprecated public static void setTranslationY(android.view.View!, float);
+    method public static void setTranslationZ(android.view.View, float);
+    method public static void setWindowInsetsAnimationCallback(android.view.View, androidx.core.view.WindowInsetsAnimationCompat.Callback?);
+    method @Deprecated public static void setX(android.view.View!, float);
+    method @Deprecated public static void setY(android.view.View!, float);
+    method public static void setZ(android.view.View, float);
+    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData!, android.view.View.DragShadowBuilder!, Object!, int);
+    method public static boolean startNestedScroll(android.view.View, int);
+    method public static boolean startNestedScroll(android.view.View, int, int);
+    method public static void stopNestedScroll(android.view.View);
+    method public static void stopNestedScroll(android.view.View, int);
+    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder!);
+    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+    field @Deprecated public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
+    field @Deprecated public static final int LAYER_TYPE_NONE = 0; // 0x0
+    field @Deprecated public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+    field @Deprecated public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+    field @Deprecated public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+    field @Deprecated public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
+    field @Deprecated public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field @Deprecated public static final int OVER_SCROLL_ALWAYS = 0; // 0x0
+    field @Deprecated public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+    field @Deprecated public static final int OVER_SCROLL_NEVER = 2; // 0x2
+    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+    field public static final int TYPE_NON_TOUCH = 1; // 0x1
+    field public static final int TYPE_TOUCH = 0; // 0x0
+  }
+
+  public static interface ViewCompat.OnUnhandledKeyEventListenerCompat {
+    method public boolean onUnhandledKeyEvent(android.view.View!, android.view.KeyEvent!);
+  }
+
+  public final class ViewConfigurationCompat {
+    method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static int getScaledHoverSlop(android.view.ViewConfiguration!);
+    method @Deprecated public static int getScaledPagingTouchSlop(android.view.ViewConfiguration!);
+    method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method @Deprecated public static boolean hasPermanentMenuKey(android.view.ViewConfiguration!);
+    method public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(android.view.ViewConfiguration!, android.content.Context);
+  }
+
+  public final class ViewGroupCompat {
+    method public static int getLayoutMode(android.view.ViewGroup);
+    method public static int getNestedScrollAxes(android.view.ViewGroup);
+    method public static boolean isTransitionGroup(android.view.ViewGroup);
+    method @Deprecated public static boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static void setLayoutMode(android.view.ViewGroup, int);
+    method @Deprecated public static void setMotionEventSplittingEnabled(android.view.ViewGroup!, boolean);
+    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+  }
+
+  public final class ViewParentCompat {
+    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent!, android.view.View!, android.view.View!, int);
+    method public static boolean onNestedFling(android.view.ViewParent!, android.view.View!, float, float, boolean);
+    method public static boolean onNestedPreFling(android.view.ViewParent!, android.view.View!, float, float);
+    method public static void onNestedPreScroll(android.view.ViewParent!, android.view.View!, int, int, int[]!);
+    method public static void onNestedPreScroll(android.view.ViewParent!, android.view.View!, int, int, int[]!, int);
+    method public static void onNestedScroll(android.view.ViewParent!, android.view.View!, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent!, android.view.View!, int, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent!, android.view.View!, int, int, int, int, int, int[]);
+    method public static void onNestedScrollAccepted(android.view.ViewParent!, android.view.View!, android.view.View!, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent!, android.view.View!, android.view.View!, int, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent!, android.view.View!, android.view.View!, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent!, android.view.View!, android.view.View!, int, int);
+    method public static void onStopNestedScroll(android.view.ViewParent!, android.view.View!);
+    method public static void onStopNestedScroll(android.view.ViewParent!, android.view.View!, int);
+    method @Deprecated public static boolean requestSendAccessibilityEvent(android.view.ViewParent!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+  }
+
+  public final class ViewPropertyAnimatorCompat {
+    method public androidx.core.view.ViewPropertyAnimatorCompat! alpha(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public android.view.animation.Interpolator! getInterpolator();
+    method public long getStartDelay();
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotation(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setDuration(long);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setInterpolator(android.view.animation.Interpolator!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setListener(androidx.core.view.ViewPropertyAnimatorListener!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setStartDelay(long);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setUpdateListener(androidx.core.view.ViewPropertyAnimatorUpdateListener!);
+    method public void start();
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationZ(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationZBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! withEndAction(Runnable!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! withLayer();
+    method public androidx.core.view.ViewPropertyAnimatorCompat! withStartAction(Runnable!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! x(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! xBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! y(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! yBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! z(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! zBy(float);
+  }
+
+  public interface ViewPropertyAnimatorListener {
+    method public void onAnimationCancel(android.view.View!);
+    method public void onAnimationEnd(android.view.View!);
+    method public void onAnimationStart(android.view.View!);
+  }
+
+  public class ViewPropertyAnimatorListenerAdapter implements androidx.core.view.ViewPropertyAnimatorListener {
+    ctor public ViewPropertyAnimatorListenerAdapter();
+    method public void onAnimationCancel(android.view.View!);
+    method public void onAnimationEnd(android.view.View!);
+    method public void onAnimationStart(android.view.View!);
+  }
+
+  public interface ViewPropertyAnimatorUpdateListener {
+    method public void onAnimationUpdate(android.view.View!);
+  }
+
+  public final class WindowCompat {
+    method public static androidx.core.view.WindowInsetsControllerCompat? getInsetsController(android.view.Window, android.view.View);
+    method public static <T extends android.view.View> T requireViewById(android.view.Window, @IdRes int);
+    method public static void setDecorFitsSystemWindows(android.view.Window, boolean);
+    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+  }
+
+  public final class WindowInsetsAnimationCompat {
+    ctor public WindowInsetsAnimationCompat(int, android.view.animation.Interpolator?, long);
+    method @FloatRange(from=0.0f, to=1.0f) public float getAlpha();
+    method public long getDurationMillis();
+    method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+    method public float getInterpolatedFraction();
+    method public android.view.animation.Interpolator? getInterpolator();
+    method public int getTypeMask();
+    method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float);
+    method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public static final class WindowInsetsAnimationCompat.Bounds {
+    ctor public WindowInsetsAnimationCompat.Bounds(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    ctor @RequiresApi(30) public WindowInsetsAnimationCompat.Bounds(android.view.WindowInsetsAnimation.Bounds);
+    method public androidx.core.graphics.Insets getLowerBound();
+    method public androidx.core.graphics.Insets getUpperBound();
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds inset(androidx.core.graphics.Insets);
+    method @RequiresApi(30) public android.view.WindowInsetsAnimation.Bounds toPlatformBounds();
+  }
+
+  public abstract static class WindowInsetsAnimationCompat.Callback {
+    ctor public WindowInsetsAnimationCompat.Callback(int);
+    method public final int getDispatchMode();
+    method public void onEnd(androidx.core.view.WindowInsetsAnimationCompat);
+    method public void onPrepare(androidx.core.view.WindowInsetsAnimationCompat);
+    method public abstract androidx.core.view.WindowInsetsCompat onProgress(androidx.core.view.WindowInsetsCompat, java.util.List<androidx.core.view.WindowInsetsAnimationCompat!>);
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds onStart(androidx.core.view.WindowInsetsAnimationCompat, androidx.core.view.WindowInsetsAnimationCompat.Bounds);
+    field public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1; // 0x1
+    field public static final int DISPATCH_MODE_STOP = 0; // 0x0
+  }
+
+  public interface WindowInsetsAnimationControlListenerCompat {
+    method public void onCancelled(androidx.core.view.WindowInsetsAnimationControllerCompat?);
+    method public void onFinished(androidx.core.view.WindowInsetsAnimationControllerCompat);
+    method public void onReady(androidx.core.view.WindowInsetsAnimationControllerCompat, int);
+  }
+
+  public final class WindowInsetsAnimationControllerCompat {
+    method public void finish(boolean);
+    method public float getCurrentAlpha();
+    method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+    method public androidx.core.graphics.Insets getCurrentInsets();
+    method public androidx.core.graphics.Insets getHiddenStateInsets();
+    method public androidx.core.graphics.Insets getShownStateInsets();
+    method public int getTypes();
+    method public boolean isCancelled();
+    method public boolean isFinished();
+    method public boolean isReady();
+    method public void setInsetsAndAlpha(androidx.core.graphics.Insets?, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public class WindowInsetsCompat {
+    ctor public WindowInsetsCompat(androidx.core.view.WindowInsetsCompat?);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeDisplayCutout();
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeStableInsets();
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeSystemWindowInsets();
+    method public androidx.core.view.DisplayCutoutCompat? getDisplayCutout();
+    method public androidx.core.graphics.Insets getInsets(int);
+    method public androidx.core.graphics.Insets getInsetsIgnoringVisibility(int);
+    method @Deprecated public androidx.core.graphics.Insets getMandatorySystemGestureInsets();
+    method @Deprecated public int getStableInsetBottom();
+    method @Deprecated public int getStableInsetLeft();
+    method @Deprecated public int getStableInsetRight();
+    method @Deprecated public int getStableInsetTop();
+    method @Deprecated public androidx.core.graphics.Insets getStableInsets();
+    method @Deprecated public androidx.core.graphics.Insets getSystemGestureInsets();
+    method @Deprecated public int getSystemWindowInsetBottom();
+    method @Deprecated public int getSystemWindowInsetLeft();
+    method @Deprecated public int getSystemWindowInsetRight();
+    method @Deprecated public int getSystemWindowInsetTop();
+    method @Deprecated public androidx.core.graphics.Insets getSystemWindowInsets();
+    method @Deprecated public androidx.core.graphics.Insets getTappableElementInsets();
+    method public boolean hasInsets();
+    method @Deprecated public boolean hasStableInsets();
+    method @Deprecated public boolean hasSystemWindowInsets();
+    method public androidx.core.view.WindowInsetsCompat inset(androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean isConsumed();
+    method public boolean isRound();
+    method public boolean isVisible(int);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+    method @RequiresApi(20) public android.view.WindowInsets? toWindowInsets();
+    method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets);
+    method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets, android.view.View?);
+    field public static final androidx.core.view.WindowInsetsCompat CONSUMED;
+  }
+
+  public static final class WindowInsetsCompat.Builder {
+    ctor public WindowInsetsCompat.Builder();
+    ctor public WindowInsetsCompat.Builder(androidx.core.view.WindowInsetsCompat);
+    method public androidx.core.view.WindowInsetsCompat build();
+    method public androidx.core.view.WindowInsetsCompat.Builder setDisplayCutout(androidx.core.view.DisplayCutoutCompat?);
+    method public androidx.core.view.WindowInsetsCompat.Builder setInsets(int, androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat.Builder setInsetsIgnoringVisibility(int, androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setMandatorySystemGestureInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setStableInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemGestureInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemWindowInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setTappableElementInsets(androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat.Builder setVisible(int, boolean);
+  }
+
+  public static final class WindowInsetsCompat.Type {
+    method public static int captionBar();
+    method public static int displayCutout();
+    method public static int ime();
+    method public static int mandatorySystemGestures();
+    method public static int navigationBars();
+    method public static int statusBars();
+    method public static int systemBars();
+    method public static int systemGestures();
+    method public static int tappableElement();
+  }
+
+  public final class WindowInsetsControllerCompat {
+    ctor public WindowInsetsControllerCompat(android.view.Window, android.view.View);
+    method public void addOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void controlWindowInsetsAnimation(int, long, android.view.animation.Interpolator?, android.os.CancellationSignal?, androidx.core.view.WindowInsetsAnimationControlListenerCompat);
+    method public int getSystemBarsBehavior();
+    method public void hide(int);
+    method public boolean isAppearanceLightNavigationBars();
+    method public boolean isAppearanceLightStatusBars();
+    method public void removeOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void setAppearanceLightNavigationBars(boolean);
+    method public void setAppearanceLightStatusBars(boolean);
+    method public void setSystemBarsBehavior(int);
+    method public void show(int);
+    method @RequiresApi(30) public static androidx.core.view.WindowInsetsControllerCompat toWindowInsetsControllerCompat(android.view.WindowInsetsController);
+    field public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
+    field public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0
+    field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
+  }
+
+  public static interface WindowInsetsControllerCompat.OnControllableInsetsChangedListener {
+    method public void onControllableInsetsChanged(androidx.core.view.WindowInsetsControllerCompat, int);
+  }
+
+}
+
+package androidx.core.view.accessibility {
+
+  public final class AccessibilityClickableSpanCompat extends android.text.style.ClickableSpan {
+    method public void onClick(android.view.View);
+  }
+
+  public final class AccessibilityEventCompat {
+    method @Deprecated public static void appendRecord(android.view.accessibility.AccessibilityEvent!, androidx.core.view.accessibility.AccessibilityRecordCompat!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! asRecord(android.view.accessibility.AccessibilityEvent!);
+    method public static int getAction(android.view.accessibility.AccessibilityEvent!);
+    method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent!);
+    method public static int getMovementGranularity(android.view.accessibility.AccessibilityEvent!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! getRecord(android.view.accessibility.AccessibilityEvent!, int);
+    method @Deprecated public static int getRecordCount(android.view.accessibility.AccessibilityEvent!);
+    method public static void setAction(android.view.accessibility.AccessibilityEvent!, int);
+    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent!, int);
+    method public static void setMovementGranularity(android.view.accessibility.AccessibilityEvent!, int);
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
+    field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
+    field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
+    field public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 64; // 0x40
+    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+    field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+    field @Deprecated public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field @Deprecated public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+    field @Deprecated public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+    field @Deprecated public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+    field @Deprecated public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+  }
+
+  public final class AccessibilityManagerCompat {
+    method @Deprecated public static boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener!);
+    method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!, int);
+    method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!);
+    method @Deprecated public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager!);
+    method @Deprecated public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener!);
+  }
+
+  @Deprecated public static interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    method @Deprecated public void onAccessibilityStateChanged(boolean);
+  }
+
+  @Deprecated public abstract static class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    ctor @Deprecated public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+  }
+
+  public static interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+    method public void onTouchExplorationStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfoCompat {
+    ctor @Deprecated public AccessibilityNodeInfoCompat(Object!);
+    method public void addAction(int);
+    method public void addAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+    method public void addChild(android.view.View!);
+    method public void addChild(android.view.View!, int);
+    method public boolean canOpenPopup();
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByText(String!);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByViewId(String!);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! findFocus(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! focusSearch(int);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!>! getActionList();
+    method public int getActions();
+    method @Deprecated public void getBoundsInParent(android.graphics.Rect!);
+    method public void getBoundsInScreen(android.graphics.Rect!);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getChild(int);
+    method public int getChildCount();
+    method public CharSequence! getClassName();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! getCollectionInfo();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! getCollectionItemInfo();
+    method public CharSequence! getContentDescription();
+    method public int getDrawingOrder();
+    method public CharSequence! getError();
+    method public android.os.Bundle! getExtras();
+    method public CharSequence? getHintText();
+    method @Deprecated public Object! getInfo();
+    method public int getInputType();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabelFor();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabeledBy();
+    method public int getLiveRegion();
+    method public int getMaxTextLength();
+    method public int getMovementGranularities();
+    method public CharSequence! getPackageName();
+    method public CharSequence? getPaneTitle();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getParent();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! getRangeInfo();
+    method public CharSequence? getRoleDescription();
+    method public CharSequence? getStateDescription();
+    method public CharSequence! getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public CharSequence? getTooltipText();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat? getTouchDelegateInfo();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalAfter();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalBefore();
+    method public String! getViewIdResourceName();
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getWindow();
+    method public int getWindowId();
+    method public boolean isAccessibilityFocused();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isContentInvalid();
+    method public boolean isContextClickable();
+    method public boolean isDismissable();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isHeading();
+    method public boolean isImportantForAccessibility();
+    method public boolean isLongClickable();
+    method public boolean isMultiLine();
+    method public boolean isPassword();
+    method public boolean isScreenReaderFocusable();
+    method public boolean isScrollable();
+    method public boolean isSelected();
+    method public boolean isShowingHintText();
+    method public boolean isTextEntryKey();
+    method public boolean isVisibleToUser();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!, int);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle!);
+    method public void recycle();
+    method public boolean refresh();
+    method public boolean removeAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+    method public boolean removeChild(android.view.View!);
+    method public boolean removeChild(android.view.View!, int);
+    method public void setAccessibilityFocused(boolean);
+    method @Deprecated public void setBoundsInParent(android.graphics.Rect!);
+    method public void setBoundsInScreen(android.graphics.Rect!);
+    method public void setCanOpenPopup(boolean);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(CharSequence!);
+    method public void setClickable(boolean);
+    method public void setCollectionInfo(Object!);
+    method public void setCollectionItemInfo(Object!);
+    method public void setContentDescription(CharSequence!);
+    method public void setContentInvalid(boolean);
+    method public void setContextClickable(boolean);
+    method public void setDismissable(boolean);
+    method public void setDrawingOrder(int);
+    method public void setEditable(boolean);
+    method public void setEnabled(boolean);
+    method public void setError(CharSequence!);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setHeading(boolean);
+    method public void setHintText(CharSequence?);
+    method public void setImportantForAccessibility(boolean);
+    method public void setInputType(int);
+    method public void setLabelFor(android.view.View!);
+    method public void setLabelFor(android.view.View!, int);
+    method public void setLabeledBy(android.view.View!);
+    method public void setLabeledBy(android.view.View!, int);
+    method public void setLiveRegion(int);
+    method public void setLongClickable(boolean);
+    method public void setMaxTextLength(int);
+    method public void setMovementGranularities(int);
+    method public void setMultiLine(boolean);
+    method public void setPackageName(CharSequence!);
+    method public void setPaneTitle(CharSequence?);
+    method public void setParent(android.view.View!);
+    method public void setParent(android.view.View!, int);
+    method public void setPassword(boolean);
+    method public void setRangeInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat!);
+    method public void setRoleDescription(CharSequence?);
+    method public void setScreenReaderFocusable(boolean);
+    method public void setScrollable(boolean);
+    method public void setSelected(boolean);
+    method public void setShowingHintText(boolean);
+    method public void setSource(android.view.View!);
+    method public void setSource(android.view.View!, int);
+    method public void setStateDescription(CharSequence?);
+    method public void setText(CharSequence!);
+    method public void setTextEntryKey(boolean);
+    method public void setTextSelection(int, int);
+    method public void setTooltipText(CharSequence?);
+    method public void setTouchDelegateInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat);
+    method public void setTraversalAfter(android.view.View!);
+    method public void setTraversalAfter(android.view.View!, int);
+    method public void setTraversalBefore(android.view.View!);
+    method public void setTraversalBefore(android.view.View!, int);
+    method public void setViewIdResourceName(String!);
+    method public void setVisibleToUser(boolean);
+    method public android.view.accessibility.AccessibilityNodeInfo! unwrap();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! wrap(android.view.accessibility.AccessibilityNodeInfo);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+    field public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+    field public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+    field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final String ACTION_ARGUMENT_MOVE_WINDOW_X = "ACTION_ARGUMENT_MOVE_WINDOW_X";
+    field public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y = "ACTION_ARGUMENT_MOVE_WINDOW_Y";
+    field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
+    field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+    field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+    field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+    field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 16; // 0x10
+    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+    field public static final int ACTION_COPY = 16384; // 0x4000
+    field public static final int ACTION_CUT = 65536; // 0x10000
+    field public static final int ACTION_DISMISS = 1048576; // 0x100000
+    field public static final int ACTION_EXPAND = 262144; // 0x40000
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+    field public static final int ACTION_PASTE = 32768; // 0x8000
+    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+  }
+
+  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, CharSequence!);
+    method public int getId();
+    method public CharSequence! getLabel();
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_ACCESSIBILITY_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_SELECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COLLAPSE;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CONTEXT_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COPY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CUT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_DISMISS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_EXPAND;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_HIDE_TOOLTIP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_IME_ENTER;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_LONG_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_MOVE_WINDOW;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_HTML_ELEMENT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_DOWN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_LEFT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_RIGHT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_UP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PASTE;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PRESS_AND_HOLD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_HTML_ELEMENT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_BACKWARD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_DOWN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_FORWARD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_LEFT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_RIGHT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_TO_POSITION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_UP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SELECT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_PROGRESS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_SELECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_TEXT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_ON_SCREEN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_TOOLTIP;
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+    method public int getColumnCount();
+    method public int getRowCount();
+    method public int getSelectionMode();
+    method public boolean isHierarchical();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean, int);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean);
+    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+    field public static final int SELECTION_MODE_NONE = 0; // 0x0
+    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    method @Deprecated public boolean isHeading();
+    method public boolean isSelected();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean, boolean);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean);
+  }
+
+  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    method public float getCurrent();
+    method public float getMax();
+    method public float getMin();
+    method public int getType();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! obtain(int, float, float, float);
+    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field public static final int RANGE_TYPE_INT = 0; // 0x0
+    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+  }
+
+  public static final class AccessibilityNodeInfoCompat.TouchDelegateInfoCompat {
+    ctor public AccessibilityNodeInfoCompat.TouchDelegateInfoCompat(java.util.Map<android.graphics.Region!,android.view.View!>);
+    method public android.graphics.Region? getRegionAt(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getRegionCount();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getTargetForRegion(android.graphics.Region);
+  }
+
+  public class AccessibilityNodeProviderCompat {
+    ctor public AccessibilityNodeProviderCompat();
+    ctor public AccessibilityNodeProviderCompat(Object!);
+    method public void addExtraDataToAccessibilityNodeInfo(int, androidx.core.view.accessibility.AccessibilityNodeInfoCompat, String, android.os.Bundle?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? createAccessibilityNodeInfo(int);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>? findAccessibilityNodeInfosByText(String!, int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? findFocus(int);
+    method public Object! getProvider();
+    method public boolean performAction(int, int, android.os.Bundle!);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+  }
+
+  public class AccessibilityRecordCompat {
+    ctor @Deprecated public AccessibilityRecordCompat(Object!);
+    method @Deprecated public boolean equals(Object?);
+    method @Deprecated public int getAddedCount();
+    method @Deprecated public CharSequence! getBeforeText();
+    method @Deprecated public CharSequence! getClassName();
+    method @Deprecated public CharSequence! getContentDescription();
+    method @Deprecated public int getCurrentItemIndex();
+    method @Deprecated public int getFromIndex();
+    method @Deprecated public Object! getImpl();
+    method @Deprecated public int getItemCount();
+    method @Deprecated public int getMaxScrollX();
+    method public static int getMaxScrollX(android.view.accessibility.AccessibilityRecord!);
+    method @Deprecated public int getMaxScrollY();
+    method public static int getMaxScrollY(android.view.accessibility.AccessibilityRecord!);
+    method @Deprecated public android.os.Parcelable! getParcelableData();
+    method @Deprecated public int getRemovedCount();
+    method @Deprecated public int getScrollX();
+    method @Deprecated public int getScrollY();
+    method @Deprecated public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getSource();
+    method @Deprecated public java.util.List<java.lang.CharSequence!>! getText();
+    method @Deprecated public int getToIndex();
+    method @Deprecated public int getWindowId();
+    method @Deprecated public int hashCode();
+    method @Deprecated public boolean isChecked();
+    method @Deprecated public boolean isEnabled();
+    method @Deprecated public boolean isFullScreen();
+    method @Deprecated public boolean isPassword();
+    method @Deprecated public boolean isScrollable();
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain(androidx.core.view.accessibility.AccessibilityRecordCompat!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain();
+    method @Deprecated public void recycle();
+    method @Deprecated public void setAddedCount(int);
+    method @Deprecated public void setBeforeText(CharSequence!);
+    method @Deprecated public void setChecked(boolean);
+    method @Deprecated public void setClassName(CharSequence!);
+    method @Deprecated public void setContentDescription(CharSequence!);
+    method @Deprecated public void setCurrentItemIndex(int);
+    method @Deprecated public void setEnabled(boolean);
+    method @Deprecated public void setFromIndex(int);
+    method @Deprecated public void setFullScreen(boolean);
+    method @Deprecated public void setItemCount(int);
+    method @Deprecated public void setMaxScrollX(int);
+    method public static void setMaxScrollX(android.view.accessibility.AccessibilityRecord!, int);
+    method @Deprecated public void setMaxScrollY(int);
+    method public static void setMaxScrollY(android.view.accessibility.AccessibilityRecord!, int);
+    method @Deprecated public void setParcelableData(android.os.Parcelable!);
+    method @Deprecated public void setPassword(boolean);
+    method @Deprecated public void setRemovedCount(int);
+    method @Deprecated public void setScrollX(int);
+    method @Deprecated public void setScrollY(int);
+    method @Deprecated public void setScrollable(boolean);
+    method @Deprecated public void setSource(android.view.View!);
+    method @Deprecated public void setSource(android.view.View!, int);
+    method public static void setSource(android.view.accessibility.AccessibilityRecord, android.view.View!, int);
+    method @Deprecated public void setToIndex(int);
+  }
+
+  public interface AccessibilityViewCommand {
+    method public boolean perform(android.view.View, androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments?);
+  }
+
+  public abstract static class AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.CommandArguments();
+  }
+
+  public static final class AccessibilityViewCommand.MoveAtGranularityArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveAtGranularityArguments();
+    method public boolean getExtendSelection();
+    method public int getGranularity();
+  }
+
+  public static final class AccessibilityViewCommand.MoveHtmlArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveHtmlArguments();
+    method public String! getHTMLElement();
+  }
+
+  public static final class AccessibilityViewCommand.MoveWindowArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveWindowArguments();
+    method public int getX();
+    method public int getY();
+  }
+
+  public static final class AccessibilityViewCommand.ScrollToPositionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.ScrollToPositionArguments();
+    method public int getColumn();
+    method public int getRow();
+  }
+
+  public static final class AccessibilityViewCommand.SetProgressArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetProgressArguments();
+    method public float getProgress();
+  }
+
+  public static final class AccessibilityViewCommand.SetSelectionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetSelectionArguments();
+    method public int getEnd();
+    method public int getStart();
+  }
+
+  public static final class AccessibilityViewCommand.SetTextArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetTextArguments();
+    method public CharSequence! getText();
+  }
+
+  public class AccessibilityWindowInfoCompat {
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getAnchor();
+    method public void getBoundsInScreen(android.graphics.Rect!);
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getChild(int);
+    method public int getChildCount();
+    method public int getId();
+    method public int getLayer();
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getParent();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getRoot();
+    method public CharSequence! getTitle();
+    method public int getType();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat! obtain();
+    method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat! obtain(androidx.core.view.accessibility.AccessibilityWindowInfoCompat!);
+    method public void recycle();
+    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
+}
+
+package androidx.core.view.animation {
+
+  public final class PathInterpolatorCompat {
+    method public static android.view.animation.Interpolator! create(android.graphics.Path!);
+    method public static android.view.animation.Interpolator! create(float, float);
+    method public static android.view.animation.Interpolator! create(float, float, float, float);
+  }
+
+}
+
+package androidx.core.view.inputmethod {
+
+  public final class EditorInfoCompat {
+    ctor @Deprecated public EditorInfoCompat();
+    method public static String![] getContentMimeTypes(android.view.inputmethod.EditorInfo!);
+    method public static CharSequence? getInitialSelectedText(android.view.inputmethod.EditorInfo, int);
+    method public static CharSequence? getInitialTextAfterCursor(android.view.inputmethod.EditorInfo, int, int);
+    method public static CharSequence? getInitialTextBeforeCursor(android.view.inputmethod.EditorInfo, int, int);
+    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, String![]?);
+    method public static void setInitialSurroundingSubText(android.view.inputmethod.EditorInfo, CharSequence, int);
+    method public static void setInitialSurroundingText(android.view.inputmethod.EditorInfo, CharSequence);
+    field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+    field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+  }
+
+  public final class InputConnectionCompat {
+    ctor @Deprecated public InputConnectionCompat();
+    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle?);
+    method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+    field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
+  }
+
+  public static interface InputConnectionCompat.OnCommitContentListener {
+    method public boolean onCommitContent(androidx.core.view.inputmethod.InputContentInfoCompat!, int, android.os.Bundle!);
+  }
+
+  public final class InputContentInfoCompat {
+    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri?);
+    method public android.net.Uri getContentUri();
+    method public android.content.ClipDescription getDescription();
+    method public android.net.Uri? getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
+    method public Object? unwrap();
+    method public static androidx.core.view.inputmethod.InputContentInfoCompat? wrap(Object?);
+  }
+
+}
+
+package androidx.core.widget {
+
+  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+    ctor public AutoScrollHelper(android.view.View);
+    method public abstract boolean canTargetScrollHorizontally(int);
+    method public abstract boolean canTargetScrollVertically(int);
+    method public boolean isEnabled();
+    method public boolean isExclusive();
+    method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+    method public abstract void scrollTargetBy(int, int);
+    method public androidx.core.widget.AutoScrollHelper setActivationDelay(int);
+    method public androidx.core.widget.AutoScrollHelper setEdgeType(int);
+    method public androidx.core.widget.AutoScrollHelper! setEnabled(boolean);
+    method public androidx.core.widget.AutoScrollHelper! setExclusive(boolean);
+    method public androidx.core.widget.AutoScrollHelper setMaximumEdges(float, float);
+    method public androidx.core.widget.AutoScrollHelper setMaximumVelocity(float, float);
+    method public androidx.core.widget.AutoScrollHelper setMinimumVelocity(float, float);
+    method public androidx.core.widget.AutoScrollHelper setRampDownDuration(int);
+    method public androidx.core.widget.AutoScrollHelper setRampUpDuration(int);
+    method public androidx.core.widget.AutoScrollHelper setRelativeEdges(float, float);
+    method public androidx.core.widget.AutoScrollHelper setRelativeVelocity(float, float);
+    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+    field public static final float NO_MAX = 3.4028235E38f;
+    field public static final float NO_MIN = 0.0f;
+    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+  }
+
+  public final class CompoundButtonCompat {
+    method public static android.graphics.drawable.Drawable? getButtonDrawable(android.widget.CompoundButton);
+    method public static android.content.res.ColorStateList? getButtonTintList(android.widget.CompoundButton);
+    method public static android.graphics.PorterDuff.Mode? getButtonTintMode(android.widget.CompoundButton);
+    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList?);
+    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode?);
+  }
+
+  public class ContentLoadingProgressBar extends android.widget.ProgressBar {
+    ctor public ContentLoadingProgressBar(android.content.Context);
+    ctor public ContentLoadingProgressBar(android.content.Context, android.util.AttributeSet?);
+    method public void hide();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void show();
+  }
+
+  public final class EdgeEffectCompat {
+    ctor @Deprecated public EdgeEffectCompat(android.content.Context!);
+    method @Deprecated public boolean draw(android.graphics.Canvas!);
+    method @Deprecated public void finish();
+    method @Deprecated public boolean isFinished();
+    method @Deprecated public boolean onAbsorb(int);
+    method @Deprecated public boolean onPull(float);
+    method @Deprecated public boolean onPull(float, float);
+    method public static void onPull(android.widget.EdgeEffect, float, float);
+    method @Deprecated public boolean onRelease();
+    method @Deprecated public void setSize(int, int);
+  }
+
+  public class ImageViewCompat {
+    method public static android.content.res.ColorStateList? getImageTintList(android.widget.ImageView);
+    method public static android.graphics.PorterDuff.Mode? getImageTintMode(android.widget.ImageView);
+    method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList?);
+    method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode?);
+  }
+
+  public final class ListPopupWindowCompat {
+    method @Deprecated public static android.view.View.OnTouchListener! createDragToOpenListener(Object!, android.view.View!);
+    method public static android.view.View.OnTouchListener? createDragToOpenListener(android.widget.ListPopupWindow, android.view.View);
+  }
+
+  public class ListViewAutoScrollHelper extends androidx.core.widget.AutoScrollHelper {
+    ctor public ListViewAutoScrollHelper(android.widget.ListView);
+    method public boolean canTargetScrollHorizontally(int);
+    method public boolean canTargetScrollVertically(int);
+    method public void scrollTargetBy(int, int);
+  }
+
+  public final class ListViewCompat {
+    method public static boolean canScrollList(android.widget.ListView, int);
+    method public static void scrollListBy(android.widget.ListView, int);
+  }
+
+  public class NestedScrollView extends android.widget.FrameLayout implements androidx.core.view.NestedScrollingChild3 androidx.core.view.NestedScrollingParent3 androidx.core.view.ScrollingView {
+    ctor public NestedScrollView(android.content.Context);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?, int);
+    method public boolean arrowScroll(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollExtent();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollOffset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollRange();
+    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollExtent();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollOffset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollRange();
+    method public boolean dispatchNestedPreScroll(int, int, int[]!, int[]!, int);
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]!, int);
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fling(int);
+    method public boolean fullScroll(int);
+    method public int getMaxScrollAmount();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isFillViewport();
+    method public boolean isSmoothScrollingEnabled();
+    method public void onAttachedToWindow();
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+    method public boolean pageScroll(int);
+    method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(androidx.core.widget.NestedScrollView.OnScrollChangeListener?);
+    method public void setSmoothScrollingEnabled(boolean);
+    method public final void smoothScrollBy(int, int);
+    method public final void smoothScrollBy(int, int, int);
+    method public final void smoothScrollTo(int, int);
+    method public final void smoothScrollTo(int, int, int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public static interface NestedScrollView.OnScrollChangeListener {
+    method public void onScrollChange(androidx.core.widget.NestedScrollView!, int, int, int, int);
+  }
+
+  public final class PopupMenuCompat {
+    method public static android.view.View.OnTouchListener? getDragToOpenListener(Object);
+  }
+
+  public final class PopupWindowCompat {
+    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+    method public static int getWindowLayoutType(android.widget.PopupWindow);
+    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+  }
+
+  @Deprecated public final class ScrollerCompat {
+    method @Deprecated public void abortAnimation();
+    method @Deprecated public boolean computeScrollOffset();
+    method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!);
+    method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!, android.view.animation.Interpolator!);
+    method @Deprecated public void fling(int, int, int, int, int, int, int, int);
+    method @Deprecated public void fling(int, int, int, int, int, int, int, int, int, int);
+    method @Deprecated public float getCurrVelocity();
+    method @Deprecated public int getCurrX();
+    method @Deprecated public int getCurrY();
+    method @Deprecated public int getFinalX();
+    method @Deprecated public int getFinalY();
+    method @Deprecated public boolean isFinished();
+    method @Deprecated public boolean isOverScrolled();
+    method @Deprecated public void notifyHorizontalEdgeReached(int, int, int);
+    method @Deprecated public void notifyVerticalEdgeReached(int, int, int);
+    method @Deprecated public boolean springBack(int, int, int, int, int, int);
+    method @Deprecated public void startScroll(int, int, int, int);
+    method @Deprecated public void startScroll(int, int, int, int, int);
+  }
+
+  public final class TextViewCompat {
+    method public static int getAutoSizeMaxTextSize(android.widget.TextView);
+    method public static int getAutoSizeMinTextSize(android.widget.TextView);
+    method public static int getAutoSizeStepGranularity(android.widget.TextView);
+    method public static int[] getAutoSizeTextAvailableSizes(android.widget.TextView);
+    method public static int getAutoSizeTextType(android.widget.TextView);
+    method public static android.content.res.ColorStateList? getCompoundDrawableTintList(android.widget.TextView);
+    method public static android.graphics.PorterDuff.Mode? getCompoundDrawableTintMode(android.widget.TextView);
+    method public static android.graphics.drawable.Drawable![] getCompoundDrawablesRelative(android.widget.TextView);
+    method public static int getFirstBaselineToTopHeight(android.widget.TextView);
+    method public static int getLastBaselineToBottomHeight(android.widget.TextView);
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
+    method public static androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParams(android.widget.TextView);
+    method public static void setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeWithDefaults(android.widget.TextView, int);
+    method public static void setCompoundDrawableTintList(android.widget.TextView, android.content.res.ColorStateList?);
+    method public static void setCompoundDrawableTintMode(android.widget.TextView, android.graphics.PorterDuff.Mode?);
+    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, @DrawableRes int, @DrawableRes int, @DrawableRes int, @DrawableRes int);
+    method public static void setCustomSelectionActionModeCallback(android.widget.TextView, android.view.ActionMode.Callback);
+    method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
+    method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
+    method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
+  }
+
+  public interface TintableCompoundButton {
+    method public android.content.res.ColorStateList? getSupportButtonTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public interface TintableCompoundDrawablesView {
+    method public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+}
+
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 2e71858..9935027 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -842,8 +842,8 @@
   }
 
   public final class ShareCompat {
-    method public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
-    method public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
     method public static android.content.ComponentName? getCallingActivity(android.app.Activity);
     method public static String? getCallingPackage(android.app.Activity);
     field public static final String EXTRA_CALLING_ACTIVITY = "androidx.core.app.EXTRA_CALLING_ACTIVITY";
@@ -1037,6 +1037,8 @@
 
   public final class PackageInfoCompat {
     method public static long getLongVersionCode(android.content.pm.PackageInfo);
+    method public static java.util.List<android.content.pm.Signature!> getSignatures(android.content.pm.PackageManager, String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static boolean hasSignatures(android.content.pm.PackageManager, String, @Size(min=1) java.util.Map<byte[]!,java.lang.Integer!>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
   }
 
   public final class PermissionInfoCompat {
@@ -1524,7 +1526,7 @@
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O_MR1) public static boolean isAtLeastOMR1();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.P) public static boolean isAtLeastP();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
-    method @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
     method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
   }
 
@@ -1912,6 +1914,31 @@
     method public void onActionProviderVisibilityChanged(boolean);
   }
 
+  public final class ContentInfoCompat {
+    method public android.content.ClipData getClip();
+    method public android.os.Bundle? getExtras();
+    method public int getFlags();
+    method public android.net.Uri? getLinkUri();
+    method public int getSource();
+    method public android.util.Pair<androidx.core.view.ContentInfoCompat!,androidx.core.view.ContentInfoCompat!> partition(androidx.core.util.Predicate<android.content.ClipData.Item!>);
+    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+  }
+
+  public static final class ContentInfoCompat.Builder {
+    ctor public ContentInfoCompat.Builder(androidx.core.view.ContentInfoCompat);
+    ctor public ContentInfoCompat.Builder(android.content.ClipData, int);
+    method public androidx.core.view.ContentInfoCompat build();
+    method public androidx.core.view.ContentInfoCompat.Builder setClip(android.content.ClipData);
+    method public androidx.core.view.ContentInfoCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.view.ContentInfoCompat.Builder setFlags(int);
+    method public androidx.core.view.ContentInfoCompat.Builder setLinkUri(android.net.Uri?);
+    method public androidx.core.view.ContentInfoCompat.Builder setSource(int);
+  }
+
   public final class DisplayCompat {
     method public static androidx.core.view.DisplayCompat.ModeCompat![] getSupportedModes(android.content.Context, android.view.Display);
   }
@@ -2208,6 +2235,14 @@
     method public androidx.core.view.WindowInsetsCompat! onApplyWindowInsets(android.view.View!, androidx.core.view.WindowInsetsCompat!);
   }
 
+  public interface OnReceiveContentListener {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+  }
+
+  public interface OnReceiveContentViewBehavior {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+  }
+
   public final class OneShotPreDrawListener implements android.view.View.OnAttachStateChangeListener android.view.ViewTreeObserver.OnPreDrawListener {
     method public static androidx.core.view.OneShotPreDrawListener add(android.view.View, Runnable);
     method public boolean onPreDraw();
@@ -2319,6 +2354,7 @@
     method public static int getMinimumHeight(android.view.View);
     method public static int getMinimumWidth(android.view.View);
     method public static int getNextClusterForwardId(android.view.View);
+    method public static String![]? getOnReceiveContentMimeTypes(android.view.View);
     method @Deprecated public static int getOverScrollMode(android.view.View!);
     method @Px public static int getPaddingEnd(android.view.View);
     method @Px public static int getPaddingStart(android.view.View);
@@ -2372,6 +2408,7 @@
     method public static void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
     method @Deprecated public static void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
     method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle!);
+    method public static androidx.core.view.ContentInfoCompat? performReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
     method public static void postInvalidateOnAnimation(android.view.View);
     method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
     method public static void postOnAnimation(android.view.View, Runnable!);
@@ -2410,6 +2447,7 @@
     method public static void setNestedScrollingEnabled(android.view.View, boolean);
     method public static void setNextClusterForwardId(android.view.View, int);
     method public static void setOnApplyWindowInsetsListener(android.view.View, androidx.core.view.OnApplyWindowInsetsListener?);
+    method public static void setOnReceiveContentListener(android.view.View, String![]?, androidx.core.view.OnReceiveContentListener?);
     method @Deprecated public static void setOverScrollMode(android.view.View!, int);
     method public static void setPaddingRelative(android.view.View, @Px int, @Px int, @Px int, @Px int);
     method @Deprecated public static void setPivotX(android.view.View!, float);
@@ -2431,6 +2469,7 @@
     method @Deprecated public static void setTranslationX(android.view.View!, float);
     method @Deprecated public static void setTranslationY(android.view.View!, float);
     method public static void setTranslationZ(android.view.View, float);
+    method public static void setWindowInsetsAnimationCallback(android.view.View, androidx.core.view.WindowInsetsAnimationCompat.Callback?);
     method @Deprecated public static void setX(android.view.View!, float);
     method @Deprecated public static void setY(android.view.View!, float);
     method public static void setZ(android.view.View, float);
@@ -2583,6 +2622,58 @@
     field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
   }
 
+  public final class WindowInsetsAnimationCompat {
+    ctor public WindowInsetsAnimationCompat(int, android.view.animation.Interpolator?, long);
+    method @FloatRange(from=0.0f, to=1.0f) public float getAlpha();
+    method public long getDurationMillis();
+    method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+    method public float getInterpolatedFraction();
+    method public android.view.animation.Interpolator? getInterpolator();
+    method public int getTypeMask();
+    method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float);
+    method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public static final class WindowInsetsAnimationCompat.Bounds {
+    ctor public WindowInsetsAnimationCompat.Bounds(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    ctor @RequiresApi(30) public WindowInsetsAnimationCompat.Bounds(android.view.WindowInsetsAnimation.Bounds);
+    method public androidx.core.graphics.Insets getLowerBound();
+    method public androidx.core.graphics.Insets getUpperBound();
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds inset(androidx.core.graphics.Insets);
+    method @RequiresApi(30) public android.view.WindowInsetsAnimation.Bounds toPlatformBounds();
+  }
+
+  public abstract static class WindowInsetsAnimationCompat.Callback {
+    ctor public WindowInsetsAnimationCompat.Callback(int);
+    method public final int getDispatchMode();
+    method public void onEnd(androidx.core.view.WindowInsetsAnimationCompat);
+    method public void onPrepare(androidx.core.view.WindowInsetsAnimationCompat);
+    method public abstract androidx.core.view.WindowInsetsCompat onProgress(androidx.core.view.WindowInsetsCompat, java.util.List<androidx.core.view.WindowInsetsAnimationCompat!>);
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds onStart(androidx.core.view.WindowInsetsAnimationCompat, androidx.core.view.WindowInsetsAnimationCompat.Bounds);
+    field public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1; // 0x1
+    field public static final int DISPATCH_MODE_STOP = 0; // 0x0
+  }
+
+  public interface WindowInsetsAnimationControlListenerCompat {
+    method public void onCancelled(androidx.core.view.WindowInsetsAnimationControllerCompat?);
+    method public void onFinished(androidx.core.view.WindowInsetsAnimationControllerCompat);
+    method public void onReady(androidx.core.view.WindowInsetsAnimationControllerCompat, int);
+  }
+
+  public final class WindowInsetsAnimationControllerCompat {
+    method public void finish(boolean);
+    method public float getCurrentAlpha();
+    method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+    method public androidx.core.graphics.Insets getCurrentInsets();
+    method public androidx.core.graphics.Insets getHiddenStateInsets();
+    method public androidx.core.graphics.Insets getShownStateInsets();
+    method public int getTypes();
+    method public boolean isCancelled();
+    method public boolean isFinished();
+    method public boolean isReady();
+    method public void setInsetsAndAlpha(androidx.core.graphics.Insets?, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+  }
+
   public class WindowInsetsCompat {
     ctor public WindowInsetsCompat(androidx.core.view.WindowInsetsCompat?);
     method @Deprecated public androidx.core.view.WindowInsetsCompat consumeDisplayCutout();
@@ -2649,10 +2740,13 @@
 
   public final class WindowInsetsControllerCompat {
     ctor public WindowInsetsControllerCompat(android.view.Window, android.view.View);
+    method public void addOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void controlWindowInsetsAnimation(int, long, android.view.animation.Interpolator?, android.os.CancellationSignal?, androidx.core.view.WindowInsetsAnimationControlListenerCompat);
     method public int getSystemBarsBehavior();
     method public void hide(int);
     method public boolean isAppearanceLightNavigationBars();
     method public boolean isAppearanceLightStatusBars();
+    method public void removeOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
     method public void setAppearanceLightNavigationBars(boolean);
     method public void setAppearanceLightStatusBars(boolean);
     method public void setSystemBarsBehavior(int);
@@ -2663,6 +2757,10 @@
     field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
   }
 
+  public static interface WindowInsetsControllerCompat.OnControllableInsetsChangedListener {
+    method public void onControllableInsetsChanged(androidx.core.view.WindowInsetsControllerCompat, int);
+  }
+
 }
 
 package androidx.core.view.accessibility {
@@ -3335,15 +3433,6 @@
     method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
   }
 
-  public abstract class RichContentReceiverCompat<T extends android.view.View> {
-    ctor public RichContentReceiverCompat();
-    method public abstract java.util.Set<java.lang.String!> getSupportedMimeTypes();
-    method public abstract boolean onReceive(T, android.content.ClipData, int, int);
-    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
-    field public static final int SOURCE_CLIPBOARD = 0; // 0x0
-    field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
-  }
-
   @Deprecated public final class ScrollerCompat {
     method @Deprecated public void abortAnimation();
     method @Deprecated public boolean computeScrollOffset();
@@ -3398,12 +3487,6 @@
     field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
   }
 
-  public abstract class TextViewRichContentReceiverCompat extends androidx.core.widget.RichContentReceiverCompat<android.widget.TextView> {
-    ctor public TextViewRichContentReceiverCompat();
-    method public java.util.Set<java.lang.String!> getSupportedMimeTypes();
-    method public boolean onReceive(android.widget.TextView, android.content.ClipData, int, int);
-  }
-
   public interface TintableCompoundButton {
     method public android.content.res.ColorStateList? getSupportButtonTintList();
     method public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
diff --git a/core/core/api/public_plus_experimental_1.5.0-beta01.txt b/core/core/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..dc43509
--- /dev/null
+++ b/core/core/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,3503 @@
+// Signature format: 4.0
+package androidx.core.accessibilityservice {
+
+  public final class AccessibilityServiceInfoCompat {
+    method public static String capabilityToString(int);
+    method public static String feedbackTypeToString(int);
+    method public static String? flagToString(int);
+    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static String? loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+  }
+
+}
+
+package androidx.core.app {
+
+  public class ActivityCompat extends androidx.core.content.ContextCompat {
+    ctor protected ActivityCompat();
+    method public static void finishAffinity(android.app.Activity);
+    method public static void finishAfterTransition(android.app.Activity);
+    method public static android.net.Uri? getReferrer(android.app.Activity);
+    method @Deprecated public static boolean invalidateOptionsMenu(android.app.Activity!);
+    method public static void postponeEnterTransition(android.app.Activity);
+    method public static void recreate(android.app.Activity);
+    method public static androidx.core.view.DragAndDropPermissionsCompat? requestDragAndDropPermissions(android.app.Activity!, android.view.DragEvent!);
+    method public static void requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+    method public static <T extends android.view.View> T requireViewById(android.app.Activity, @IdRes int);
+    method public static void setEnterSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+    method public static void setExitSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+    method public static void setLocusContext(android.app.Activity, androidx.core.content.LocusIdCompat?, android.os.Bundle?);
+    method public static void setPermissionCompatDelegate(androidx.core.app.ActivityCompat.PermissionCompatDelegate?);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, String);
+    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle?);
+    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public static void startPostponedEnterTransition(android.app.Activity);
+  }
+
+  public static interface ActivityCompat.OnRequestPermissionsResultCallback {
+    method public void onRequestPermissionsResult(int, String![], int[]);
+  }
+
+  public static interface ActivityCompat.PermissionCompatDelegate {
+    method public boolean onActivityResult(android.app.Activity, @IntRange(from=0) int, int, android.content.Intent?);
+    method public boolean requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+  }
+
+  public final class ActivityManagerCompat {
+    method public static boolean isLowRamDevice(android.app.ActivityManager);
+  }
+
+  public class ActivityOptionsCompat {
+    ctor protected ActivityOptionsCompat();
+    method public android.graphics.Rect? getLaunchBounds();
+    method public static androidx.core.app.ActivityOptionsCompat makeBasic();
+    method public static androidx.core.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, String);
+    method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, androidx.core.util.Pair<android.view.View!,java.lang.String!>!...);
+    method public static androidx.core.app.ActivityOptionsCompat makeTaskLaunchBehind();
+    method public static androidx.core.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+    method public void requestUsageTimeReport(android.app.PendingIntent);
+    method public androidx.core.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect?);
+    method public android.os.Bundle? toBundle();
+    method public void update(androidx.core.app.ActivityOptionsCompat);
+    field public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+    field public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+  }
+
+  public final class AlarmManagerCompat {
+    method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+    method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+  }
+
+  @RequiresApi(28) public class AppComponentFactory extends android.app.AppComponentFactory {
+    ctor public AppComponentFactory();
+    method public final android.app.Activity instantiateActivity(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Activity instantiateActivityCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.app.Application instantiateApplication(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Application instantiateApplicationCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.content.ContentProvider instantiateProvider(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.content.ContentProvider instantiateProviderCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.content.BroadcastReceiver instantiateReceiver(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.content.BroadcastReceiver instantiateReceiverCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.app.Service instantiateService(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Service instantiateServiceCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+  }
+
+  public class AppLaunchChecker {
+    ctor @Deprecated public AppLaunchChecker();
+    method public static boolean hasStartedFromLauncher(android.content.Context);
+    method public static void onActivityCreate(android.app.Activity);
+  }
+
+  public final class AppOpsManagerCompat {
+    method public static int noteOp(android.content.Context, String, int, String);
+    method public static int noteOpNoThrow(android.content.Context, String, int, String);
+    method public static int noteProxyOp(android.content.Context, String, String);
+    method public static int noteProxyOpNoThrow(android.content.Context, String, String);
+    method public static String? permissionToOp(String);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
+    field public static final int MODE_ERRORED = 2; // 0x2
+    field public static final int MODE_IGNORED = 1; // 0x1
+  }
+
+  public final class BundleCompat {
+    method public static android.os.IBinder? getBinder(android.os.Bundle, String?);
+    method public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
+  }
+
+  public class DialogCompat {
+    method public static android.view.View requireViewById(android.app.Dialog, int);
+  }
+
+  public class FrameMetricsAggregator {
+    ctor public FrameMetricsAggregator();
+    ctor public FrameMetricsAggregator(int);
+    method public void add(android.app.Activity);
+    method public android.util.SparseIntArray![]? getMetrics();
+    method public android.util.SparseIntArray![]? remove(android.app.Activity);
+    method public android.util.SparseIntArray![]? reset();
+    method public android.util.SparseIntArray![]? stop();
+    field public static final int ANIMATION_DURATION = 256; // 0x100
+    field public static final int ANIMATION_INDEX = 8; // 0x8
+    field public static final int COMMAND_DURATION = 32; // 0x20
+    field public static final int COMMAND_INDEX = 5; // 0x5
+    field public static final int DELAY_DURATION = 128; // 0x80
+    field public static final int DELAY_INDEX = 7; // 0x7
+    field public static final int DRAW_DURATION = 8; // 0x8
+    field public static final int DRAW_INDEX = 3; // 0x3
+    field public static final int EVERY_DURATION = 511; // 0x1ff
+    field public static final int INPUT_DURATION = 2; // 0x2
+    field public static final int INPUT_INDEX = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 4; // 0x4
+    field public static final int LAYOUT_MEASURE_INDEX = 2; // 0x2
+    field public static final int SWAP_DURATION = 64; // 0x40
+    field public static final int SWAP_INDEX = 6; // 0x6
+    field public static final int SYNC_DURATION = 16; // 0x10
+    field public static final int SYNC_INDEX = 4; // 0x4
+    field public static final int TOTAL_DURATION = 1; // 0x1
+    field public static final int TOTAL_INDEX = 0; // 0x0
+  }
+
+  public abstract class JobIntentService extends android.app.Service {
+    ctor public JobIntentService();
+    method public static void enqueueWork(android.content.Context, Class<?>, int, android.content.Intent);
+    method public static void enqueueWork(android.content.Context, android.content.ComponentName, int, android.content.Intent);
+    method public boolean isStopped();
+    method public android.os.IBinder! onBind(android.content.Intent);
+    method protected abstract void onHandleWork(android.content.Intent);
+    method public boolean onStopCurrentWork();
+    method public void setInterruptIfStopped(boolean);
+  }
+
+  public final class NavUtils {
+    method public static android.content.Intent? getParentActivityIntent(android.app.Activity);
+    method public static android.content.Intent? getParentActivityIntent(android.content.Context, Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static android.content.Intent? getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static String? getParentActivityName(android.app.Activity);
+    method public static String? getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void navigateUpFromSameTask(android.app.Activity);
+    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+    field public static final String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+  }
+
+  public class NotificationChannelCompat {
+    method public boolean canBubble();
+    method public boolean canBypassDnd();
+    method public boolean canShowBadge();
+    method public android.media.AudioAttributes? getAudioAttributes();
+    method public String? getConversationId();
+    method public String? getDescription();
+    method public String? getGroup();
+    method public String getId();
+    method public int getImportance();
+    method public int getLightColor();
+    method public int getLockscreenVisibility();
+    method public CharSequence? getName();
+    method public String? getParentChannelId();
+    method public android.net.Uri? getSound();
+    method public long[]? getVibrationPattern();
+    method public boolean isImportantConversation();
+    method public boolean shouldShowLights();
+    method public boolean shouldVibrate();
+    method public androidx.core.app.NotificationChannelCompat.Builder toBuilder();
+    field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
+  }
+
+  public static class NotificationChannelCompat.Builder {
+    ctor public NotificationChannelCompat.Builder(String, int);
+    method public androidx.core.app.NotificationChannelCompat build();
+    method public androidx.core.app.NotificationChannelCompat.Builder setConversationId(String, String);
+    method public androidx.core.app.NotificationChannelCompat.Builder setDescription(String?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setGroup(String?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setImportance(int);
+    method public androidx.core.app.NotificationChannelCompat.Builder setLightColor(int);
+    method public androidx.core.app.NotificationChannelCompat.Builder setLightsEnabled(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setName(CharSequence?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setShowBadge(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setSound(android.net.Uri?, android.media.AudioAttributes?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setVibrationEnabled(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setVibrationPattern(long[]?);
+  }
+
+  public class NotificationChannelGroupCompat {
+    method public java.util.List<androidx.core.app.NotificationChannelCompat!> getChannels();
+    method public String? getDescription();
+    method public String getId();
+    method public CharSequence? getName();
+    method public boolean isBlocked();
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder toBuilder();
+  }
+
+  public static class NotificationChannelGroupCompat.Builder {
+    ctor public NotificationChannelGroupCompat.Builder(String);
+    method public androidx.core.app.NotificationChannelGroupCompat build();
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder setDescription(String?);
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder setName(CharSequence?);
+  }
+
+  public class NotificationCompat {
+    ctor @Deprecated public NotificationCompat();
+    method public static androidx.core.app.NotificationCompat.Action? getAction(android.app.Notification, int);
+    method public static int getActionCount(android.app.Notification);
+    method public static boolean getAllowSystemGeneratedContextualActions(android.app.Notification);
+    method public static boolean getAutoCancel(android.app.Notification);
+    method public static int getBadgeIconType(android.app.Notification);
+    method public static androidx.core.app.NotificationCompat.BubbleMetadata? getBubbleMetadata(android.app.Notification);
+    method public static String? getCategory(android.app.Notification);
+    method public static String? getChannelId(android.app.Notification);
+    method public static int getColor(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentInfo(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentText(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentTitle(android.app.Notification);
+    method public static android.os.Bundle? getExtras(android.app.Notification);
+    method public static String? getGroup(android.app.Notification);
+    method public static int getGroupAlertBehavior(android.app.Notification);
+    method @RequiresApi(21) public static java.util.List<androidx.core.app.NotificationCompat.Action!> getInvisibleActions(android.app.Notification);
+    method public static boolean getLocalOnly(android.app.Notification);
+    method public static androidx.core.content.LocusIdCompat? getLocusId(android.app.Notification);
+    method public static boolean getOngoing(android.app.Notification);
+    method public static boolean getOnlyAlertOnce(android.app.Notification);
+    method public static java.util.List<androidx.core.app.Person!> getPeople(android.app.Notification);
+    method public static android.app.Notification? getPublicVersion(android.app.Notification);
+    method public static CharSequence? getSettingsText(android.app.Notification);
+    method public static String? getShortcutId(android.app.Notification);
+    method @RequiresApi(19) public static boolean getShowWhen(android.app.Notification);
+    method public static String? getSortKey(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getSubText(android.app.Notification);
+    method public static long getTimeoutAfter(android.app.Notification);
+    method @RequiresApi(19) public static boolean getUsesChronometer(android.app.Notification);
+    method public static int getVisibility(android.app.Notification);
+    method public static boolean isGroupSummary(android.app.Notification);
+    field public static final int BADGE_ICON_LARGE = 2; // 0x2
+    field public static final int BADGE_ICON_NONE = 0; // 0x0
+    field public static final int BADGE_ICON_SMALL = 1; // 0x1
+    field public static final String CATEGORY_ALARM = "alarm";
+    field public static final String CATEGORY_CALL = "call";
+    field public static final String CATEGORY_EMAIL = "email";
+    field public static final String CATEGORY_ERROR = "err";
+    field public static final String CATEGORY_EVENT = "event";
+    field public static final String CATEGORY_LOCATION_SHARING = "location_sharing";
+    field public static final String CATEGORY_MESSAGE = "msg";
+    field public static final String CATEGORY_MISSED_CALL = "missed_call";
+    field public static final String CATEGORY_NAVIGATION = "navigation";
+    field public static final String CATEGORY_PROGRESS = "progress";
+    field public static final String CATEGORY_PROMO = "promo";
+    field public static final String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final String CATEGORY_REMINDER = "reminder";
+    field public static final String CATEGORY_SERVICE = "service";
+    field public static final String CATEGORY_SOCIAL = "social";
+    field public static final String CATEGORY_STATUS = "status";
+    field public static final String CATEGORY_STOPWATCH = "stopwatch";
+    field public static final String CATEGORY_SYSTEM = "sys";
+    field public static final String CATEGORY_TRANSPORT = "transport";
+    field public static final String CATEGORY_WORKOUT = "workout";
+    field @ColorInt public static final int COLOR_DEFAULT = 0; // 0x0
+    field public static final int DEFAULT_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_LIGHTS = 4; // 0x4
+    field public static final int DEFAULT_SOUND = 1; // 0x1
+    field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+    field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
+    field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
+    field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
+    field public static final String EXTRA_COLORIZED = "android.colorized";
+    field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+    field public static final String EXTRA_COMPAT_TEMPLATE = "androidx.core.app.extra.COMPAT_TEMPLATE";
+    field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final String EXTRA_HIDDEN_CONVERSATION_TITLE = "android.hiddenConversationTitle";
+    field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
+    field public static final String EXTRA_INFO_TEXT = "android.infoText";
+    field public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
+    field public static final String EXTRA_LARGE_ICON = "android.largeIcon";
+    field public static final String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+    field public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final String EXTRA_MESSAGES = "android.messages";
+    field public static final String EXTRA_MESSAGING_STYLE_USER = "android.messagingStyleUser";
+    field public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+    field public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
+    field @Deprecated public static final String EXTRA_PEOPLE = "android.people";
+    field public static final String EXTRA_PEOPLE_LIST = "android.people.list";
+    field public static final String EXTRA_PICTURE = "android.picture";
+    field public static final String EXTRA_PROGRESS = "android.progress";
+    field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    field public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    field public static final String EXTRA_SHOW_WHEN = "android.showWhen";
+    field public static final String EXTRA_SMALL_ICON = "android.icon";
+    field public static final String EXTRA_SUB_TEXT = "android.subText";
+    field public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
+    field public static final String EXTRA_TEMPLATE = "android.template";
+    field public static final String EXTRA_TEXT = "android.text";
+    field public static final String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final String EXTRA_TITLE = "android.title";
+    field public static final String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+    field public static final int FLAG_BUBBLE = 4096; // 0x1000
+    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+    field @Deprecated public static final int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final int FLAG_INSISTENT = 4; // 0x4
+    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+    field public static final int FLAG_NO_CLEAR = 32; // 0x20
+    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final int GROUP_ALERT_ALL = 0; // 0x0
+    field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
+    field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
+    field public static final String GROUP_KEY_SILENT = "silent";
+    field public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES";
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+  }
+
+  public static class NotificationCompat.Action {
+    ctor public NotificationCompat.Action(int, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+    method public android.app.PendingIntent? getActionIntent();
+    method public boolean getAllowGeneratedReplies();
+    method public androidx.core.app.RemoteInput![]? getDataOnlyRemoteInputs();
+    method public android.os.Bundle getExtras();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
+    method public androidx.core.app.RemoteInput![]? getRemoteInputs();
+    method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
+    method public boolean getShowsUserInterface();
+    method public CharSequence? getTitle();
+    method public boolean isContextual();
+    field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
+    field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
+    field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
+    field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
+    field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
+    field public static final int SEMANTIC_ACTION_MUTE = 6; // 0x6
+    field public static final int SEMANTIC_ACTION_NONE = 0; // 0x0
+    field public static final int SEMANTIC_ACTION_REPLY = 1; // 0x1
+    field public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9; // 0x9
+    field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
+    field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
+    field public android.app.PendingIntent! actionIntent;
+    field @Deprecated public int icon;
+    field public CharSequence! title;
+  }
+
+  public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action.Builder(int, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.Action.Builder addRemoteInput(androidx.core.app.RemoteInput?);
+    method public androidx.core.app.NotificationCompat.Action build();
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Extender);
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setContextual(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setSemanticAction(@androidx.core.app.NotificationCompat.Action.SemanticAction int);
+    method public androidx.core.app.NotificationCompat.Action.Builder setShowsUserInterface(boolean);
+  }
+
+  public static interface NotificationCompat.Action.Extender {
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_NONE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_REPLY, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_UNREAD, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_DELETE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_ARCHIVE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_UNMUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_UP, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_DOWN, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_CALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.Action.SemanticAction {
+  }
+
+  public static final class NotificationCompat.Action.WearableExtender implements androidx.core.app.NotificationCompat.Action.Extender {
+    ctor public NotificationCompat.Action.WearableExtender();
+    ctor public NotificationCompat.Action.WearableExtender(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender clone();
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+    method @Deprecated public CharSequence? getCancelLabel();
+    method @Deprecated public CharSequence? getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
+    method public boolean getHintLaunchesActivity();
+    method @Deprecated public CharSequence? getInProgressLabel();
+    method public boolean isAvailableOffline();
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setCancelLabel(CharSequence?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setConfirmLabel(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setInProgressLabel(CharSequence?);
+  }
+
+  public static class NotificationCompat.BigPictureStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigPictureStyle();
+    ctor public NotificationCompat.BigPictureStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle setSummaryText(CharSequence?);
+  }
+
+  public static class NotificationCompat.BigTextStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigTextStyle();
+    ctor public NotificationCompat.BigTextStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle bigText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle setSummaryText(CharSequence?);
+  }
+
+  public static final class NotificationCompat.BubbleMetadata {
+    method public static androidx.core.app.NotificationCompat.BubbleMetadata? fromPlatform(android.app.Notification.BubbleMetadata?);
+    method public boolean getAutoExpandBubble();
+    method public android.app.PendingIntent? getDeleteIntent();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getDesiredHeight();
+    method @DimenRes public int getDesiredHeightResId();
+    method public androidx.core.graphics.drawable.IconCompat? getIcon();
+    method public android.app.PendingIntent? getIntent();
+    method public String? getShortcutId();
+    method public boolean isNotificationSuppressed();
+    method public static android.app.Notification.BubbleMetadata? toPlatform(androidx.core.app.NotificationCompat.BubbleMetadata?);
+  }
+
+  public static final class NotificationCompat.BubbleMetadata.Builder {
+    ctor @Deprecated public NotificationCompat.BubbleMetadata.Builder();
+    ctor @RequiresApi(30) public NotificationCompat.BubbleMetadata.Builder(String);
+    ctor public NotificationCompat.BubbleMetadata.Builder(android.app.PendingIntent, androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata build();
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setAutoExpandBubble(boolean);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeight(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeightResId(@DimenRes int);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIcon(androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setSuppressNotification(boolean);
+  }
+
+  public static class NotificationCompat.Builder {
+    ctor @RequiresApi(19) public NotificationCompat.Builder(android.content.Context, android.app.Notification);
+    ctor public NotificationCompat.Builder(android.content.Context, String);
+    ctor @Deprecated public NotificationCompat.Builder(android.content.Context);
+    method public androidx.core.app.NotificationCompat.Builder addAction(int, CharSequence?, android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder addAction(androidx.core.app.NotificationCompat.Action?);
+    method public androidx.core.app.NotificationCompat.Builder addExtras(android.os.Bundle?);
+    method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(int, CharSequence?, android.app.PendingIntent?);
+    method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(androidx.core.app.NotificationCompat.Action?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder addPerson(String?);
+    method public androidx.core.app.NotificationCompat.Builder addPerson(androidx.core.app.Person?);
+    method public android.app.Notification build();
+    method public androidx.core.app.NotificationCompat.Builder clearActions();
+    method public androidx.core.app.NotificationCompat.Builder clearInvisibleActions();
+    method public androidx.core.app.NotificationCompat.Builder clearPeople();
+    method public android.widget.RemoteViews? createBigContentView();
+    method public android.widget.RemoteViews? createContentView();
+    method public android.widget.RemoteViews? createHeadsUpContentView();
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Extender);
+    method public android.os.Bundle getExtras();
+    method @Deprecated public android.app.Notification getNotification();
+    method protected static CharSequence? limitCharSequenceLength(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setAllowSystemGeneratedContextualActions(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setAutoCancel(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setBadgeIconType(int);
+    method public androidx.core.app.NotificationCompat.Builder setBubbleMetadata(androidx.core.app.NotificationCompat.BubbleMetadata?);
+    method public androidx.core.app.NotificationCompat.Builder setCategory(String?);
+    method public androidx.core.app.NotificationCompat.Builder setChannelId(String);
+    method @RequiresApi(24) public androidx.core.app.NotificationCompat.Builder setChronometerCountDown(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setColor(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.Builder setColorized(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setContent(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setContentInfo(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder setContentText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setDefaults(int);
+    method public androidx.core.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent?, boolean);
+    method public androidx.core.app.NotificationCompat.Builder setGroup(String?);
+    method public androidx.core.app.NotificationCompat.Builder setGroupAlertBehavior(int);
+    method public androidx.core.app.NotificationCompat.Builder setGroupSummary(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.Builder setLights(@ColorInt int, int, int);
+    method public androidx.core.app.NotificationCompat.Builder setLocalOnly(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder setNotificationSilent();
+    method public androidx.core.app.NotificationCompat.Builder setNumber(int);
+    method public androidx.core.app.NotificationCompat.Builder setOngoing(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setPriority(int);
+    method public androidx.core.app.NotificationCompat.Builder setProgress(int, int, boolean);
+    method public androidx.core.app.NotificationCompat.Builder setPublicVersion(android.app.Notification?);
+    method public androidx.core.app.NotificationCompat.Builder setRemoteInputHistory(CharSequence![]?);
+    method public androidx.core.app.NotificationCompat.Builder setSettingsText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setShortcutId(String?);
+    method public androidx.core.app.NotificationCompat.Builder setShortcutInfo(androidx.core.content.pm.ShortcutInfoCompat?);
+    method public androidx.core.app.NotificationCompat.Builder setShowWhen(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setSilent(boolean);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.Builder setSmallIcon(androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int);
+    method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int, int);
+    method public androidx.core.app.NotificationCompat.Builder setSortKey(String?);
+    method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?);
+    method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?, int);
+    method public androidx.core.app.NotificationCompat.Builder setStyle(androidx.core.app.NotificationCompat.Style?);
+    method public androidx.core.app.NotificationCompat.Builder setSubText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?, android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setTimeoutAfter(long);
+    method public androidx.core.app.NotificationCompat.Builder setUsesChronometer(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setVibrate(long[]?);
+    method public androidx.core.app.NotificationCompat.Builder setVisibility(int);
+    method public androidx.core.app.NotificationCompat.Builder setWhen(long);
+    field @Deprecated public java.util.ArrayList<java.lang.String!>! mPeople;
+  }
+
+  public static final class NotificationCompat.CarExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.CarExtender();
+    ctor public NotificationCompat.CarExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method @ColorInt public int getColor();
+    method public android.graphics.Bitmap? getLargeIcon();
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation? getUnreadConversation();
+    method public androidx.core.app.NotificationCompat.CarExtender setColor(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender setUnreadConversation(androidx.core.app.NotificationCompat.CarExtender.UnreadConversation?);
+  }
+
+  @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation {
+    method @Deprecated public long getLatestTimestamp();
+    method @Deprecated public String![]? getMessages();
+    method @Deprecated public String? getParticipant();
+    method @Deprecated public String![]? getParticipants();
+    method @Deprecated public android.app.PendingIntent? getReadPendingIntent();
+    method @Deprecated public androidx.core.app.RemoteInput? getRemoteInput();
+    method @Deprecated public android.app.PendingIntent? getReplyPendingIntent();
+  }
+
+  @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+    ctor @Deprecated public NotificationCompat.CarExtender.UnreadConversation.Builder(String);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(String?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation build();
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent?, androidx.core.app.RemoteInput?);
+  }
+
+  public static class NotificationCompat.DecoratedCustomViewStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static interface NotificationCompat.Extender {
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+  }
+
+  public static class NotificationCompat.InboxStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.InboxStyle();
+    ctor public NotificationCompat.InboxStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.InboxStyle addLine(CharSequence?);
+    method public androidx.core.app.NotificationCompat.InboxStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.InboxStyle setSummaryText(CharSequence?);
+  }
+
+  public static class NotificationCompat.MessagingStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor @Deprecated public NotificationCompat.MessagingStyle(CharSequence);
+    ctor public NotificationCompat.MessagingStyle(androidx.core.app.Person);
+    method public void addCompatExtras(android.os.Bundle);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addHistoricMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+    method @Deprecated public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, CharSequence?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, androidx.core.app.Person?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+    method public static androidx.core.app.NotificationCompat.MessagingStyle? extractMessagingStyleFromNotification(android.app.Notification);
+    method public CharSequence? getConversationTitle();
+    method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getHistoricMessages();
+    method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getMessages();
+    method public androidx.core.app.Person getUser();
+    method @Deprecated public CharSequence? getUserDisplayName();
+    method public boolean isGroupConversation();
+    method public androidx.core.app.NotificationCompat.MessagingStyle setConversationTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle setGroupConversation(boolean);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class NotificationCompat.MessagingStyle.Message {
+    ctor public NotificationCompat.MessagingStyle.Message(CharSequence?, long, androidx.core.app.Person?);
+    ctor @Deprecated public NotificationCompat.MessagingStyle.Message(CharSequence?, long, CharSequence?);
+    method public String? getDataMimeType();
+    method public android.net.Uri? getDataUri();
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.Person? getPerson();
+    method @Deprecated public CharSequence? getSender();
+    method public CharSequence getText();
+    method public long getTimestamp();
+    method public androidx.core.app.NotificationCompat.MessagingStyle.Message setData(String?, android.net.Uri?);
+  }
+
+  public abstract static class NotificationCompat.Style {
+    ctor public NotificationCompat.Style();
+    method public android.app.Notification? build();
+    method public void setBuilder(androidx.core.app.NotificationCompat.Builder?);
+  }
+
+  public static final class NotificationCompat.WearableExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.WearableExtender();
+    ctor public NotificationCompat.WearableExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.WearableExtender addAction(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.WearableExtender addActions(java.util.List<androidx.core.app.NotificationCompat.Action!>);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification!>);
+    method public androidx.core.app.NotificationCompat.WearableExtender clearActions();
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender clearPages();
+    method public androidx.core.app.NotificationCompat.WearableExtender clone();
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method public java.util.List<androidx.core.app.NotificationCompat.Action!> getActions();
+    method @Deprecated public android.graphics.Bitmap? getBackground();
+    method public String? getBridgeTag();
+    method public int getContentAction();
+    method @Deprecated public int getContentIcon();
+    method @Deprecated public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method @Deprecated public int getCustomContentHeight();
+    method @Deprecated public int getCustomSizePreset();
+    method public String? getDismissalId();
+    method @Deprecated public android.app.PendingIntent? getDisplayIntent();
+    method @Deprecated public int getGravity();
+    method @Deprecated public boolean getHintAmbientBigPicture();
+    method @Deprecated public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
+    method @Deprecated public boolean getHintHideIcon();
+    method @Deprecated public int getHintScreenTimeout();
+    method @Deprecated public boolean getHintShowBackgroundOnly();
+    method @Deprecated public java.util.List<android.app.Notification!> getPages();
+    method public boolean getStartScrollBottom();
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.WearableExtender setBridgeTag(String?);
+    method public androidx.core.app.NotificationCompat.WearableExtender setContentAction(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIcon(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+    method public androidx.core.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+    method public androidx.core.app.NotificationCompat.WearableExtender setDismissalId(String?);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent?);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setGravity(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public androidx.core.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public androidx.core.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+    field @Deprecated public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field @Deprecated public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field @Deprecated public static final int SIZE_DEFAULT = 0; // 0x0
+    field @Deprecated public static final int SIZE_FULL_SCREEN = 5; // 0x5
+    field @Deprecated public static final int SIZE_LARGE = 4; // 0x4
+    field @Deprecated public static final int SIZE_MEDIUM = 3; // 0x3
+    field @Deprecated public static final int SIZE_SMALL = 2; // 0x2
+    field @Deprecated public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public final class NotificationCompatExtras {
+    field public static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+    field public static final String EXTRA_GROUP_KEY = "android.support.groupKey";
+    field public static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+    field public static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+    field public static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+    field public static final String EXTRA_SORT_KEY = "android.support.sortKey";
+  }
+
+  public abstract class NotificationCompatSideChannelService extends android.app.Service {
+    ctor public NotificationCompatSideChannelService();
+    method public abstract void cancel(String!, int, String!);
+    method public abstract void cancelAll(String!);
+    method public abstract void notify(String!, int, String!, android.app.Notification!);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+  }
+
+  public final class NotificationManagerCompat {
+    method public boolean areNotificationsEnabled();
+    method public void cancel(int);
+    method public void cancel(String?, int);
+    method public void cancelAll();
+    method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void createNotificationChannel(androidx.core.app.NotificationChannelCompat);
+    method public void createNotificationChannelGroup(android.app.NotificationChannelGroup);
+    method public void createNotificationChannelGroup(androidx.core.app.NotificationChannelGroupCompat);
+    method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup!>);
+    method public void createNotificationChannelGroupsCompat(java.util.List<androidx.core.app.NotificationChannelGroupCompat!>);
+    method public void createNotificationChannels(java.util.List<android.app.NotificationChannel!>);
+    method public void createNotificationChannelsCompat(java.util.List<androidx.core.app.NotificationChannelCompat!>);
+    method public void deleteNotificationChannel(String);
+    method public void deleteNotificationChannelGroup(String);
+    method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
+    method public static androidx.core.app.NotificationManagerCompat from(android.content.Context);
+    method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
+    method public int getImportance();
+    method public android.app.NotificationChannel? getNotificationChannel(String);
+    method public android.app.NotificationChannel? getNotificationChannel(String, String);
+    method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String);
+    method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String, String);
+    method public android.app.NotificationChannelGroup? getNotificationChannelGroup(String);
+    method public androidx.core.app.NotificationChannelGroupCompat? getNotificationChannelGroupCompat(String);
+    method public java.util.List<android.app.NotificationChannelGroup!> getNotificationChannelGroups();
+    method public java.util.List<androidx.core.app.NotificationChannelGroupCompat!> getNotificationChannelGroupsCompat();
+    method public java.util.List<android.app.NotificationChannel!> getNotificationChannels();
+    method public java.util.List<androidx.core.app.NotificationChannelCompat!> getNotificationChannelsCompat();
+    method public void notify(int, android.app.Notification);
+    method public void notify(String?, int, android.app.Notification);
+    field public static final String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+    field public static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
+    field public static final int IMPORTANCE_NONE = 0; // 0x0
+    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+  }
+
+  public class Person {
+    method public static androidx.core.app.Person fromBundle(android.os.Bundle);
+    method public androidx.core.graphics.drawable.IconCompat? getIcon();
+    method public String? getKey();
+    method public CharSequence? getName();
+    method public String? getUri();
+    method public boolean isBot();
+    method public boolean isImportant();
+    method public androidx.core.app.Person.Builder toBuilder();
+    method public android.os.Bundle toBundle();
+  }
+
+  public static class Person.Builder {
+    ctor public Person.Builder();
+    method public androidx.core.app.Person build();
+    method public androidx.core.app.Person.Builder setBot(boolean);
+    method public androidx.core.app.Person.Builder setIcon(androidx.core.graphics.drawable.IconCompat?);
+    method public androidx.core.app.Person.Builder setImportant(boolean);
+    method public androidx.core.app.Person.Builder setKey(String?);
+    method public androidx.core.app.Person.Builder setName(CharSequence?);
+    method public androidx.core.app.Person.Builder setUri(String?);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(jetifyAs="android.support.v4.app.RemoteActionCompat") public final class RemoteActionCompat implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public RemoteActionCompat(androidx.core.graphics.drawable.IconCompat, CharSequence, CharSequence, android.app.PendingIntent);
+    ctor public RemoteActionCompat(androidx.core.app.RemoteActionCompat);
+    method @RequiresApi(26) public static androidx.core.app.RemoteActionCompat createFromRemoteAction(android.app.RemoteAction);
+    method public android.app.PendingIntent getActionIntent();
+    method public CharSequence getContentDescription();
+    method public androidx.core.graphics.drawable.IconCompat getIcon();
+    method public CharSequence getTitle();
+    method public boolean isEnabled();
+    method public void setEnabled(boolean);
+    method public void setShouldShowIcon(boolean);
+    method public boolean shouldShowIcon();
+    method @RequiresApi(26) public android.app.RemoteAction toRemoteAction();
+  }
+
+  public final class RemoteInput {
+    method public static void addDataResultToIntent(androidx.core.app.RemoteInput!, android.content.Intent!, java.util.Map<java.lang.String!,android.net.Uri!>!);
+    method public static void addResultsToIntent(androidx.core.app.RemoteInput![]!, android.content.Intent!, android.os.Bundle!);
+    method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String!>! getAllowedDataTypes();
+    method public CharSequence![]! getChoices();
+    method public static java.util.Map<java.lang.String!,android.net.Uri!>! getDataResultsFromIntent(android.content.Intent!, String!);
+    method public int getEditChoicesBeforeSending();
+    method public android.os.Bundle! getExtras();
+    method public CharSequence! getLabel();
+    method public String! getResultKey();
+    method public static android.os.Bundle! getResultsFromIntent(android.content.Intent!);
+    method public static int getResultsSource(android.content.Intent);
+    method public boolean isDataOnly();
+    method public static void setResultsSource(android.content.Intent, int);
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0; // 0x0
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1; // 0x1
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2; // 0x2
+    field public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    field public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+    field public static final int SOURCE_CHOICE = 1; // 0x1
+    field public static final int SOURCE_FREE_FORM_INPUT = 0; // 0x0
+  }
+
+  public static final class RemoteInput.Builder {
+    ctor public RemoteInput.Builder(String);
+    method public androidx.core.app.RemoteInput.Builder addExtras(android.os.Bundle);
+    method public androidx.core.app.RemoteInput build();
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.RemoteInput.Builder setAllowDataType(String, boolean);
+    method public androidx.core.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+    method public androidx.core.app.RemoteInput.Builder setChoices(CharSequence![]?);
+    method public androidx.core.app.RemoteInput.Builder setEditChoicesBeforeSending(int);
+    method public androidx.core.app.RemoteInput.Builder setLabel(CharSequence?);
+  }
+
+  public final class ServiceCompat {
+    method public static void stopForeground(android.app.Service, int);
+    field public static final int START_STICKY = 1; // 0x1
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+  }
+
+  public final class ShareCompat {
+    method @Deprecated public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
+    method public static android.content.ComponentName? getCallingActivity(android.app.Activity);
+    method public static String? getCallingPackage(android.app.Activity);
+    field public static final String EXTRA_CALLING_ACTIVITY = "androidx.core.app.EXTRA_CALLING_ACTIVITY";
+    field public static final String EXTRA_CALLING_ACTIVITY_INTEROP = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+    field public static final String EXTRA_CALLING_PACKAGE = "androidx.core.app.EXTRA_CALLING_PACKAGE";
+    field public static final String EXTRA_CALLING_PACKAGE_INTEROP = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+  }
+
+  public static class ShareCompat.IntentBuilder {
+    ctor public ShareCompat.IntentBuilder(android.content.Context);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+    method public android.content.Intent createChooserIntent();
+    method @Deprecated public static androidx.core.app.ShareCompat.IntentBuilder from(android.app.Activity);
+    method public android.content.Intent getIntent();
+    method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(CharSequence?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(@StringRes int);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailBcc(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailCc(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailTo(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setHtmlText(String?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setStream(android.net.Uri?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setSubject(String?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setText(CharSequence?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setType(String?);
+    method public void startChooser();
+  }
+
+  public static class ShareCompat.IntentReader {
+    ctor public ShareCompat.IntentReader(android.app.Activity);
+    ctor public ShareCompat.IntentReader(android.content.Context, android.content.Intent);
+    method @Deprecated public static androidx.core.app.ShareCompat.IntentReader from(android.app.Activity);
+    method public android.content.ComponentName? getCallingActivity();
+    method public android.graphics.drawable.Drawable? getCallingActivityIcon();
+    method public android.graphics.drawable.Drawable? getCallingApplicationIcon();
+    method public CharSequence? getCallingApplicationLabel();
+    method public String? getCallingPackage();
+    method public String![]? getEmailBcc();
+    method public String![]? getEmailCc();
+    method public String![]? getEmailTo();
+    method public String? getHtmlText();
+    method public android.net.Uri? getStream();
+    method public android.net.Uri? getStream(int);
+    method public int getStreamCount();
+    method public String? getSubject();
+    method public CharSequence? getText();
+    method public String? getType();
+    method public boolean isMultipleShare();
+    method public boolean isShareIntent();
+    method public boolean isSingleShare();
+  }
+
+  public abstract class SharedElementCallback {
+    ctor public SharedElementCallback();
+    method public android.os.Parcelable! onCaptureSharedElementSnapshot(android.view.View!, android.graphics.Matrix!, android.graphics.RectF!);
+    method public android.view.View! onCreateSnapshotView(android.content.Context!, android.os.Parcelable!);
+    method public void onMapSharedElements(java.util.List<java.lang.String!>!, java.util.Map<java.lang.String!,android.view.View!>!);
+    method public void onRejectSharedElements(java.util.List<android.view.View!>!);
+    method public void onSharedElementEnd(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+    method public void onSharedElementStart(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+    method public void onSharedElementsArrived(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, androidx.core.app.SharedElementCallback.OnSharedElementsReadyListener!);
+  }
+
+  public static interface SharedElementCallback.OnSharedElementsReadyListener {
+    method public void onSharedElementsReady();
+  }
+
+  public final class TaskStackBuilder implements java.lang.Iterable<android.content.Intent> {
+    method public androidx.core.app.TaskStackBuilder addNextIntent(android.content.Intent);
+    method public androidx.core.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+    method public androidx.core.app.TaskStackBuilder addParentStack(android.app.Activity);
+    method public androidx.core.app.TaskStackBuilder addParentStack(Class<?>);
+    method public androidx.core.app.TaskStackBuilder! addParentStack(android.content.ComponentName!);
+    method public static androidx.core.app.TaskStackBuilder create(android.content.Context);
+    method public android.content.Intent? editIntentAt(int);
+    method @Deprecated public static androidx.core.app.TaskStackBuilder! from(android.content.Context!);
+    method @Deprecated public android.content.Intent! getIntent(int);
+    method public int getIntentCount();
+    method public android.content.Intent![] getIntents();
+    method public android.app.PendingIntent? getPendingIntent(int, int);
+    method public android.app.PendingIntent? getPendingIntent(int, int, android.os.Bundle?);
+    method @Deprecated public java.util.Iterator<android.content.Intent!>! iterator();
+    method public void startActivities();
+    method public void startActivities(android.os.Bundle?);
+  }
+
+  public static interface TaskStackBuilder.SupportParentable {
+    method public android.content.Intent? getSupportParentActivityIntent();
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentProviderCompat {
+    method public static android.content.Context requireContext(android.content.ContentProvider);
+  }
+
+  public final class ContentResolverCompat {
+    method public static android.database.Cursor! query(android.content.ContentResolver!, android.net.Uri!, String![]!, String!, String![]!, String!, androidx.core.os.CancellationSignal!);
+  }
+
+  public class ContextCompat {
+    ctor protected ContextCompat();
+    method public static int checkSelfPermission(android.content.Context, String);
+    method public static android.content.Context? createDeviceProtectedStorageContext(android.content.Context);
+    method public static java.io.File! getCodeCacheDir(android.content.Context);
+    method @ColorInt public static int getColor(android.content.Context, @ColorRes int);
+    method public static android.content.res.ColorStateList? getColorStateList(android.content.Context, @ColorRes int);
+    method public static java.io.File? getDataDir(android.content.Context);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+    method public static java.io.File![] getExternalCacheDirs(android.content.Context);
+    method public static java.io.File![] getExternalFilesDirs(android.content.Context, String?);
+    method public static java.util.concurrent.Executor! getMainExecutor(android.content.Context!);
+    method public static java.io.File? getNoBackupFilesDir(android.content.Context);
+    method public static java.io.File![] getObbDirs(android.content.Context);
+    method public static <T> T? getSystemService(android.content.Context, Class<T!>);
+    method public static String? getSystemServiceName(android.content.Context, Class<?>);
+    method public static boolean isDeviceProtectedStorage(android.content.Context);
+    method public static boolean startActivities(android.content.Context, android.content.Intent![]);
+    method public static boolean startActivities(android.content.Context, android.content.Intent![], android.os.Bundle?);
+    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle?);
+    method public static void startForegroundService(android.content.Context, android.content.Intent);
+  }
+
+  public class FileProvider extends android.content.ContentProvider {
+    ctor public FileProvider();
+    method public int delete(android.net.Uri, String?, String![]?);
+    method public String! getType(android.net.Uri);
+    method public static android.net.Uri! getUriForFile(android.content.Context, String, java.io.File);
+    method public static android.net.Uri getUriForFile(android.content.Context, String, java.io.File, String);
+    method public android.net.Uri! insert(android.net.Uri, android.content.ContentValues!);
+    method public boolean onCreate();
+    method public android.database.Cursor! query(android.net.Uri, String![]?, String?, String![]?, String?);
+    method public int update(android.net.Uri, android.content.ContentValues!, String?, String![]?);
+  }
+
+  public final class IntentCompat {
+    method public static android.content.Intent makeMainSelectorActivity(String, String);
+    field public static final String ACTION_CREATE_REMINDER = "android.intent.action.CREATE_REMINDER";
+    field public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    field public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+    field public static final String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
+    field public static final String EXTRA_TIME = "android.intent.extra.TIME";
+  }
+
+  public final class LocusIdCompat {
+    ctor public LocusIdCompat(String);
+    method public String getId();
+    method @RequiresApi(29) public android.content.LocusId toLocusId();
+    method @RequiresApi(29) public static androidx.core.content.LocusIdCompat toLocusIdCompat(android.content.LocusId);
+  }
+
+  public final class MimeTypeFilter {
+    method public static boolean matches(String?, String);
+    method public static String? matches(String?, String![]);
+    method public static String? matches(String![]?, String);
+    method public static String![] matchesMany(String![]?, String);
+  }
+
+  public final class PermissionChecker {
+    method public static int checkCallingOrSelfPermission(android.content.Context, String);
+    method public static int checkCallingPermission(android.content.Context, String, String?);
+    method public static int checkPermission(android.content.Context, String, int, int, String?);
+    method public static int checkSelfPermission(android.content.Context, String);
+    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+    field public static final int PERMISSION_GRANTED = 0; // 0x0
+  }
+
+  @Deprecated public final class SharedPreferencesCompat {
+  }
+
+  @Deprecated public static final class SharedPreferencesCompat.EditorCompat {
+    method @Deprecated public void apply(android.content.SharedPreferences.Editor);
+    method @Deprecated public static androidx.core.content.SharedPreferencesCompat.EditorCompat! getInstance();
+  }
+
+}
+
+package androidx.core.content.pm {
+
+  @Deprecated public final class ActivityInfoCompat {
+    field @Deprecated public static final int CONFIG_UI_MODE = 512; // 0x200
+  }
+
+  public final class PackageInfoCompat {
+    method public static long getLongVersionCode(android.content.pm.PackageInfo);
+    method public static java.util.List<android.content.pm.Signature!> getSignatures(android.content.pm.PackageManager, String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static boolean hasSignatures(android.content.pm.PackageManager, String, @Size(min=1) java.util.Map<byte[]!,java.lang.Integer!>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+  }
+
+  public final class PermissionInfoCompat {
+    method public static int getProtection(android.content.pm.PermissionInfo);
+    method public static int getProtectionFlags(android.content.pm.PermissionInfo);
+  }
+
+  public class ShortcutInfoCompat {
+    method public android.content.ComponentName? getActivity();
+    method public java.util.Set<java.lang.String!>? getCategories();
+    method public CharSequence? getDisabledMessage();
+    method public int getDisabledReason();
+    method public android.os.PersistableBundle? getExtras();
+    method public String getId();
+    method public android.content.Intent getIntent();
+    method public android.content.Intent![] getIntents();
+    method public long getLastChangedTimestamp();
+    method public androidx.core.content.LocusIdCompat? getLocusId();
+    method public CharSequence? getLongLabel();
+    method public String getPackage();
+    method public int getRank();
+    method public CharSequence getShortLabel();
+    method public android.os.UserHandle? getUserHandle();
+    method public boolean hasKeyFieldsOnly();
+    method public boolean isCached();
+    method public boolean isDeclaredInManifest();
+    method public boolean isDynamic();
+    method public boolean isEnabled();
+    method public boolean isImmutable();
+    method public boolean isPinned();
+    method @RequiresApi(25) public android.content.pm.ShortcutInfo! toShortcutInfo();
+  }
+
+  public static class ShortcutInfoCompat.Builder {
+    ctor public ShortcutInfoCompat.Builder(android.content.Context, String);
+    method public androidx.core.content.pm.ShortcutInfoCompat build();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setAlwaysBadged();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setCategories(java.util.Set<java.lang.String!>);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(CharSequence);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExtras(android.os.PersistableBundle);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIcon(androidx.core.graphics.drawable.IconCompat!);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsConversation();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
+    method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived(boolean);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPerson(androidx.core.app.Person);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPersons(androidx.core.app.Person![]);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setRank(int);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setShortLabel(CharSequence);
+  }
+
+  public class ShortcutManagerCompat {
+    method public static boolean addDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static android.content.Intent createShortcutResultIntent(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+    method public static void disableShortcuts(android.content.Context, java.util.List<java.lang.String!>, CharSequence?);
+    method public static void enableShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getDynamicShortcuts(android.content.Context);
+    method public static int getIconMaxHeight(android.content.Context);
+    method public static int getIconMaxWidth(android.content.Context);
+    method public static int getMaxShortcutCountPerActivity(android.content.Context);
+    method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getShortcuts(android.content.Context, int);
+    method public static boolean isRateLimitingActive(android.content.Context);
+    method public static boolean isRequestPinShortcutSupported(android.content.Context);
+    method public static boolean pushDynamicShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+    method public static void removeAllDynamicShortcuts(android.content.Context);
+    method public static void removeDynamicShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+    method public static void removeLongLivedShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+    method public static void reportShortcutUsed(android.content.Context, String);
+    method public static boolean requestPinShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat, android.content.IntentSender?);
+    method public static boolean setDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static boolean updateShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    field public static final String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID";
+    field public static final int FLAG_MATCH_CACHED = 8; // 0x8
+    field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
+    field public static final int FLAG_MATCH_MANIFEST = 1; // 0x1
+    field public static final int FLAG_MATCH_PINNED = 4; // 0x4
+  }
+
+}
+
+package androidx.core.content.res {
+
+  public final class ConfigurationHelper {
+    method public static int getDensityDpi(android.content.res.Resources);
+  }
+
+  public final class ResourcesCompat {
+    method public static android.graphics.Typeface? getCachedFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+    method @ColorInt public static int getColor(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.content.res.ColorStateList? getColorStateList(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable? getDrawableForDensity(android.content.res.Resources, @DrawableRes int, int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static float getFloat(android.content.res.Resources, @DimenRes int);
+    method public static android.graphics.Typeface? getFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+    method public static void getFont(android.content.Context, @FontRes int, androidx.core.content.res.ResourcesCompat.FontCallback, android.os.Handler?) throws android.content.res.Resources.NotFoundException;
+    field @AnyRes public static final int ID_NULL = 0; // 0x0
+  }
+
+  public abstract static class ResourcesCompat.FontCallback {
+    ctor public ResourcesCompat.FontCallback();
+    method public abstract void onFontRetrievalFailed(int);
+    method public abstract void onFontRetrieved(android.graphics.Typeface);
+  }
+
+  public static final class ResourcesCompat.ThemeCompat {
+    method public static void rebase(android.content.res.Resources.Theme);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorWindowCompat {
+    method public static android.database.CursorWindow create(String?, long);
+  }
+
+  @Deprecated public final class DatabaseUtilsCompat {
+    method @Deprecated public static String![]! appendSelectionArgs(String![]!, String![]!);
+    method @Deprecated public static String! concatenateWhere(String!, String!);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteCursorCompat {
+    method public static void setFillWindowForwardOnly(android.database.sqlite.SQLiteCursor, boolean);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapCompat {
+    method public static int getAllocationByteCount(android.graphics.Bitmap);
+    method public static boolean hasMipMap(android.graphics.Bitmap);
+    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+  }
+
+  public class BlendModeColorFilterCompat {
+    method public static android.graphics.ColorFilter? createBlendModeColorFilterCompat(int, androidx.core.graphics.BlendModeCompat);
+  }
+
+  public enum BlendModeCompat {
+    enum_constant public static final androidx.core.graphics.BlendModeCompat CLEAR;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_BURN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_DODGE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DARKEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat DIFFERENCE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_ATOP;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_IN;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OUT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OVER;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat EXCLUSION;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HARD_LIGHT;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HUE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat LIGHTEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat LUMINOSITY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat MODULATE;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat MULTIPLY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat OVERLAY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat PLUS;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SATURATION;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SCREEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SOFT_LIGHT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_ATOP;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_IN;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OUT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OVER;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat XOR;
+  }
+
+  public final class ColorUtils {
+    method @ColorInt public static int HSLToColor(float[]);
+    method @ColorInt public static int LABToColor(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double);
+    method public static void LABToXYZ(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double, double[]);
+    method public static void RGBToHSL(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, float[]);
+    method public static void RGBToLAB(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+    method public static void RGBToXYZ(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+    method @ColorInt public static int XYZToColor(@FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_X) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Y) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Z) double);
+    method public static void XYZToLAB(@FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_X) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Y) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Z) double, double[]);
+    method @ColorInt public static int blendARGB(@ColorInt int, @ColorInt int, @FloatRange(from=0.0, to=1.0) float);
+    method public static void blendHSL(float[], float[], @FloatRange(from=0.0, to=1.0) float, float[]);
+    method public static void blendLAB(double[], double[], @FloatRange(from=0.0, to=1.0) double, double[]);
+    method public static double calculateContrast(@ColorInt int, @ColorInt int);
+    method @FloatRange(from=0.0, to=1.0) public static double calculateLuminance(@ColorInt int);
+    method public static int calculateMinimumAlpha(@ColorInt int, @ColorInt int, float);
+    method public static void colorToHSL(@ColorInt int, float[]);
+    method public static void colorToLAB(@ColorInt int, double[]);
+    method public static void colorToXYZ(@ColorInt int, double[]);
+    method public static int compositeColors(@ColorInt int, @ColorInt int);
+    method @RequiresApi(26) public static android.graphics.Color compositeColors(android.graphics.Color, android.graphics.Color);
+    method public static double distanceEuclidean(double[], double[]);
+    method @ColorInt public static int setAlphaComponent(@ColorInt int, @IntRange(from=0, to=255) int);
+  }
+
+  public final class Insets {
+    method public static androidx.core.graphics.Insets add(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets max(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets min(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets of(int, int, int, int);
+    method public static androidx.core.graphics.Insets of(android.graphics.Rect);
+    method public static androidx.core.graphics.Insets subtract(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method @RequiresApi(api=29) public static androidx.core.graphics.Insets toCompatInsets(android.graphics.Insets);
+    method @RequiresApi(api=29) public android.graphics.Insets toPlatformInsets();
+    field public static final androidx.core.graphics.Insets NONE;
+    field public final int bottom;
+    field public final int left;
+    field public final int right;
+    field public final int top;
+  }
+
+  public final class PaintCompat {
+    method public static boolean hasGlyph(android.graphics.Paint, String);
+    method public static boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat?);
+  }
+
+  public final class PathSegment {
+    ctor public PathSegment(android.graphics.PointF, float, android.graphics.PointF, float);
+    method public android.graphics.PointF getEnd();
+    method public float getEndFraction();
+    method public android.graphics.PointF getStart();
+    method public float getStartFraction();
+  }
+
+  public final class PathUtils {
+    method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path);
+    method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path, @FloatRange(from=0) float);
+  }
+
+  public class TypefaceCompat {
+    method public static android.graphics.Typeface create(android.content.Context, android.graphics.Typeface?, int);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class DrawableCompat {
+    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+    method public static void clearColorFilter(android.graphics.drawable.Drawable);
+    method public static int getAlpha(android.graphics.drawable.Drawable);
+    method public static android.graphics.ColorFilter! getColorFilter(android.graphics.drawable.Drawable);
+    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+    method @Deprecated public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+    method public static void setTint(android.graphics.drawable.Drawable, @ColorInt int);
+    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList?);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static <T extends android.graphics.drawable.Drawable> T! unwrap(android.graphics.drawable.Drawable);
+    method public static android.graphics.drawable.Drawable! wrap(android.graphics.drawable.Drawable);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(allowSerialization=true, ignoreParcelables=true, isCustom=true, jetifyAs="android.support.v4.graphics.drawable.IconCompat") public class IconCompat extends androidx.versionedparcelable.CustomVersionedParcelable {
+    method public static androidx.core.graphics.drawable.IconCompat? createFromBundle(android.os.Bundle);
+    method @RequiresApi(23) public static androidx.core.graphics.drawable.IconCompat? createFromIcon(android.content.Context, android.graphics.drawable.Icon);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithAdaptiveBitmap(android.graphics.Bitmap!);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(String);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(android.net.Uri);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithBitmap(android.graphics.Bitmap!);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithContentUri(String!);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithContentUri(android.net.Uri!);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithData(byte[]!, int, int);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithResource(android.content.Context!, @DrawableRes int);
+    method @IdRes public int getResId();
+    method public String getResPackage();
+    method public int getType();
+    method public android.net.Uri getUri();
+    method public android.graphics.drawable.Drawable? loadDrawable(android.content.Context);
+    method public androidx.core.graphics.drawable.IconCompat! setTint(@ColorInt int);
+    method public androidx.core.graphics.drawable.IconCompat! setTintList(android.content.res.ColorStateList!);
+    method public androidx.core.graphics.drawable.IconCompat! setTintMode(android.graphics.PorterDuff.Mode!);
+    method public android.os.Bundle toBundle();
+    method @Deprecated @RequiresApi(23) public android.graphics.drawable.Icon toIcon();
+    method @RequiresApi(23) public android.graphics.drawable.Icon toIcon(android.content.Context?);
+    field public static final int TYPE_ADAPTIVE_BITMAP = 5; // 0x5
+    field public static final int TYPE_BITMAP = 1; // 0x1
+    field public static final int TYPE_DATA = 3; // 0x3
+    field public static final int TYPE_RESOURCE = 2; // 0x2
+    field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+    field public static final int TYPE_URI = 4; // 0x4
+    field public static final int TYPE_URI_ADAPTIVE_BITMAP = 6; // 0x6
+  }
+
+  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.Bitmap? getBitmap();
+    method public float getCornerRadius();
+    method public int getGravity();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public boolean hasAntiAlias();
+    method public boolean hasMipMap();
+    method public boolean isCircular();
+    method public void setAlpha(int);
+    method public void setAntiAlias(boolean);
+    method public void setCircular(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setCornerRadius(float);
+    method public void setDither(boolean);
+    method public void setGravity(int);
+    method public void setMipMap(boolean);
+    method public void setTargetDensity(android.graphics.Canvas);
+    method public void setTargetDensity(android.util.DisplayMetrics);
+    method public void setTargetDensity(int);
+  }
+
+  public final class RoundedBitmapDrawableFactory {
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap?);
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, String);
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+  }
+
+}
+
+package androidx.core.hardware.display {
+
+  public final class DisplayManagerCompat {
+    method public android.view.Display? getDisplay(int);
+    method public android.view.Display![] getDisplays();
+    method public android.view.Display![] getDisplays(String?);
+    method public static androidx.core.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+    field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+  }
+
+}
+
+package androidx.core.hardware.fingerprint {
+
+  @Deprecated public class FingerprintManagerCompat {
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public void authenticate(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject?, int, androidx.core.os.CancellationSignal?, androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler?);
+    method @Deprecated public static androidx.core.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean hasEnrolledFingerprints();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean isHardwareDetected();
+  }
+
+  @Deprecated public abstract static class FingerprintManagerCompat.AuthenticationCallback {
+    ctor @Deprecated public FingerprintManagerCompat.AuthenticationCallback();
+    method @Deprecated public void onAuthenticationError(int, CharSequence!);
+    method @Deprecated public void onAuthenticationFailed();
+    method @Deprecated public void onAuthenticationHelp(int, CharSequence!);
+    method @Deprecated public void onAuthenticationSucceeded(androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult!);
+  }
+
+  @Deprecated public static final class FingerprintManagerCompat.AuthenticationResult {
+    ctor @Deprecated public FingerprintManagerCompat.AuthenticationResult(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject!);
+    method @Deprecated public androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject! getCryptoObject();
+  }
+
+  @Deprecated public static class FingerprintManagerCompat.CryptoObject {
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+    method @Deprecated public javax.crypto.Cipher? getCipher();
+    method @Deprecated public javax.crypto.Mac? getMac();
+    method @Deprecated public java.security.Signature? getSignature();
+  }
+
+}
+
+package androidx.core.location {
+
+  public abstract class GnssStatusCompat {
+    method @FloatRange(from=0, to=360) public abstract float getAzimuthDegrees(@IntRange(from=0) int);
+    method @FloatRange(from=0, to=63) public abstract float getBasebandCn0DbHz(@IntRange(from=0) int);
+    method @FloatRange(from=0) public abstract float getCarrierFrequencyHz(@IntRange(from=0) int);
+    method @FloatRange(from=0, to=63) public abstract float getCn0DbHz(@IntRange(from=0) int);
+    method public abstract int getConstellationType(@IntRange(from=0) int);
+    method @FloatRange(from=0xffffffa6, to=90) public abstract float getElevationDegrees(@IntRange(from=0) int);
+    method @IntRange(from=0) public abstract int getSatelliteCount();
+    method @IntRange(from=1, to=200) public abstract int getSvid(@IntRange(from=0) int);
+    method public abstract boolean hasAlmanacData(@IntRange(from=0) int);
+    method public abstract boolean hasBasebandCn0DbHz(@IntRange(from=0) int);
+    method public abstract boolean hasCarrierFrequencyHz(@IntRange(from=0) int);
+    method public abstract boolean hasEphemerisData(@IntRange(from=0) int);
+    method public abstract boolean usedInFix(@IntRange(from=0) int);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) public static androidx.core.location.GnssStatusCompat wrap(android.location.GnssStatus);
+    method public static androidx.core.location.GnssStatusCompat wrap(android.location.GpsStatus);
+    field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
+    field public static final int CONSTELLATION_GALILEO = 6; // 0x6
+    field public static final int CONSTELLATION_GLONASS = 3; // 0x3
+    field public static final int CONSTELLATION_GPS = 1; // 0x1
+    field public static final int CONSTELLATION_IRNSS = 7; // 0x7
+    field public static final int CONSTELLATION_QZSS = 4; // 0x4
+    field public static final int CONSTELLATION_SBAS = 2; // 0x2
+    field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
+  }
+
+  public abstract static class GnssStatusCompat.Callback {
+    ctor public GnssStatusCompat.Callback();
+    method public void onFirstFix(@IntRange(from=0) int);
+    method public void onSatelliteStatusChanged(androidx.core.location.GnssStatusCompat);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public final class LocationManagerCompat {
+    method public static boolean isLocationEnabled(android.location.LocationManager);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback, android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, java.util.concurrent.Executor, androidx.core.location.GnssStatusCompat.Callback);
+    method public static void unregisterGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback);
+  }
+
+}
+
+package androidx.core.math {
+
+  public class MathUtils {
+    method public static float clamp(float, float, float);
+    method public static double clamp(double, double, double);
+    method public static int clamp(int, int, int);
+    method public static long clamp(long, long, long);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class ConnectivityManagerCompat {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static android.net.NetworkInfo? getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+    method public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+  }
+
+  public final class MailTo {
+    method public String? getBcc();
+    method public String? getBody();
+    method public String? getCc();
+    method public java.util.Map<java.lang.String!,java.lang.String!>? getHeaders();
+    method public String? getSubject();
+    method public String? getTo();
+    method public static boolean isMailTo(String?);
+    method public static boolean isMailTo(android.net.Uri?);
+    method public static androidx.core.net.MailTo parse(String) throws androidx.core.net.ParseException;
+    method public static androidx.core.net.MailTo parse(android.net.Uri) throws androidx.core.net.ParseException;
+    field public static final String MAILTO_SCHEME = "mailto:";
+  }
+
+  public class ParseException extends java.lang.RuntimeException {
+    field public final String response;
+  }
+
+  public final class TrafficStatsCompat {
+    method @Deprecated public static void clearThreadStatsTag();
+    method @Deprecated public static int getThreadStatsTag();
+    method @Deprecated public static void incrementOperationCount(int);
+    method @Deprecated public static void incrementOperationCount(int, int);
+    method @Deprecated public static void setThreadStatsTag(int);
+    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method @Deprecated public static void tagSocket(java.net.Socket!) throws java.net.SocketException;
+    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method @Deprecated public static void untagSocket(java.net.Socket!) throws java.net.SocketException;
+  }
+
+  public final class UriCompat {
+    method public static String toSafeString(android.net.Uri);
+  }
+
+}
+
+package androidx.core.os {
+
+  public class BuildCompat {
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N) public static boolean isAtLeastN();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N_MR1) public static boolean isAtLeastNMR1();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O) public static boolean isAtLeastO();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O_MR1) public static boolean isAtLeastOMR1();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.P) public static boolean isAtLeastP();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
+    method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
+  }
+
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
+    method public void cancel();
+    method public Object? getCancellationSignalObject();
+    method public boolean isCanceled();
+    method public void setOnCancelListener(androidx.core.os.CancellationSignal.OnCancelListener?);
+    method public void throwIfCanceled();
+  }
+
+  public static interface CancellationSignal.OnCancelListener {
+    method public void onCancel();
+  }
+
+  public final class ConfigurationCompat {
+    method public static androidx.core.os.LocaleListCompat getLocales(android.content.res.Configuration);
+  }
+
+  public final class EnvironmentCompat {
+    method public static String getStorageState(java.io.File);
+    field public static final String MEDIA_UNKNOWN = "unknown";
+  }
+
+  public final class HandlerCompat {
+    method public static android.os.Handler createAsync(android.os.Looper);
+    method public static android.os.Handler createAsync(android.os.Looper, android.os.Handler.Callback);
+    method public static boolean postDelayed(android.os.Handler, Runnable, Object?, long);
+  }
+
+  public class HandlerExecutor implements java.util.concurrent.Executor {
+    ctor public HandlerExecutor(android.os.Handler);
+    method public void execute(Runnable);
+  }
+
+  public final class LocaleListCompat {
+    method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
+    method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
+    method public java.util.Locale! get(int);
+    method @Size(min=1) public static androidx.core.os.LocaleListCompat getAdjustedDefault();
+    method @Size(min=1) public static androidx.core.os.LocaleListCompat getDefault();
+    method public static androidx.core.os.LocaleListCompat getEmptyLocaleList();
+    method public java.util.Locale? getFirstMatch(String![]);
+    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale!);
+    method public boolean isEmpty();
+    method @IntRange(from=0) public int size();
+    method public String toLanguageTags();
+    method public Object? unwrap();
+    method @Deprecated @RequiresApi(24) public static androidx.core.os.LocaleListCompat! wrap(Object!);
+    method @RequiresApi(24) public static androidx.core.os.LocaleListCompat wrap(android.os.LocaleList);
+  }
+
+  public final class MessageCompat {
+    method public static boolean isAsynchronous(android.os.Message);
+    method public static void setAsynchronous(android.os.Message, boolean);
+  }
+
+  public class OperationCanceledException extends java.lang.RuntimeException {
+    ctor public OperationCanceledException();
+    ctor public OperationCanceledException(String?);
+  }
+
+  public final class ParcelCompat {
+    method public static boolean readBoolean(android.os.Parcel);
+    method public static void writeBoolean(android.os.Parcel, boolean);
+  }
+
+  @Deprecated public final class ParcelableCompat {
+    method @Deprecated public static <T> android.os.Parcelable.Creator<T!>! newCreator(androidx.core.os.ParcelableCompatCreatorCallbacks<T!>!);
+  }
+
+  @Deprecated public interface ParcelableCompatCreatorCallbacks<T> {
+    method @Deprecated public T! createFromParcel(android.os.Parcel!, ClassLoader!);
+    method @Deprecated public T![]! newArray(int);
+  }
+
+  public final class ProcessCompat {
+    method public static boolean isApplicationUid(int);
+  }
+
+  @Deprecated public final class TraceCompat {
+    method @Deprecated public static void beginAsyncSection(String, int);
+    method @Deprecated public static void beginSection(String);
+    method @Deprecated public static void endAsyncSection(String, int);
+    method @Deprecated public static void endSection();
+    method @Deprecated public static boolean isEnabled();
+    method @Deprecated public static void setCounter(String, int);
+  }
+
+  public class UserManagerCompat {
+    method public static boolean isUserUnlocked(android.content.Context);
+  }
+
+}
+
+package androidx.core.provider {
+
+  public final class FontRequest {
+    ctor public FontRequest(String, String, String, java.util.List<java.util.List<byte[]!>!>);
+    ctor public FontRequest(String, String, String, @ArrayRes int);
+    method public java.util.List<java.util.List<byte[]!>!>? getCertificates();
+    method @ArrayRes public int getCertificatesArrayResId();
+    method public String getProviderAuthority();
+    method public String getProviderPackage();
+    method public String getQuery();
+  }
+
+  public class FontsContractCompat {
+    method public static android.graphics.Typeface? buildTypeface(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![]);
+    method public static androidx.core.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+  }
+
+  public static final class FontsContractCompat.Columns implements android.provider.BaseColumns {
+    ctor public FontsContractCompat.Columns();
+    field public static final String FILE_ID = "file_id";
+    field public static final String ITALIC = "font_italic";
+    field public static final String RESULT_CODE = "result_code";
+    field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3
+    field public static final int RESULT_CODE_OK = 0; // 0x0
+    field public static final String TTC_INDEX = "font_ttc_index";
+    field public static final String VARIATION_SETTINGS = "font_variation_settings";
+    field public static final String WEIGHT = "font_weight";
+  }
+
+  public static class FontsContractCompat.FontFamilyResult {
+    method public androidx.core.provider.FontsContractCompat.FontInfo![]! getFonts();
+    method public int getStatusCode();
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
+    field public static final int STATUS_WRONG_CERTIFICATES = 1; // 0x1
+  }
+
+  public static class FontsContractCompat.FontInfo {
+    method public int getResultCode();
+    method @IntRange(from=0) public int getTtcIndex();
+    method public android.net.Uri getUri();
+    method @IntRange(from=1, to=1000) public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static class FontsContractCompat.FontRequestCallback {
+    ctor public FontsContractCompat.FontRequestCallback();
+    method public void onTypefaceRequestFailed(int);
+    method public void onTypefaceRetrieved(android.graphics.Typeface!);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+    field public static final int FAIL_REASON_SECURITY_VIOLATION = -4; // 0xfffffffc
+    field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+  }
+
+}
+
+package androidx.core.telephony.mbms {
+
+  public final class MbmsHelper {
+    method public static CharSequence? getBestNameForService(android.content.Context, android.telephony.mbms.ServiceInfo);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class BidiFormatter {
+    method public static androidx.core.text.BidiFormatter! getInstance();
+    method public static androidx.core.text.BidiFormatter! getInstance(boolean);
+    method public static androidx.core.text.BidiFormatter! getInstance(java.util.Locale!);
+    method public boolean getStereoReset();
+    method public boolean isRtl(String!);
+    method public boolean isRtl(CharSequence!);
+    method public boolean isRtlContext();
+    method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+    method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+    method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!);
+    method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!);
+    method public String! unicodeWrap(String!, boolean);
+    method public CharSequence! unicodeWrap(CharSequence!, boolean);
+    method public String! unicodeWrap(String!);
+    method public CharSequence! unicodeWrap(CharSequence!);
+  }
+
+  public static final class BidiFormatter.Builder {
+    ctor public BidiFormatter.Builder();
+    ctor public BidiFormatter.Builder(boolean);
+    ctor public BidiFormatter.Builder(java.util.Locale!);
+    method public androidx.core.text.BidiFormatter! build();
+    method public androidx.core.text.BidiFormatter.Builder! setTextDirectionHeuristic(androidx.core.text.TextDirectionHeuristicCompat!);
+    method public androidx.core.text.BidiFormatter.Builder! stereoReset(boolean);
+  }
+
+  public final class HtmlCompat {
+    method public static android.text.Spanned fromHtml(String, int);
+    method public static android.text.Spanned fromHtml(String, int, android.text.Html.ImageGetter?, android.text.Html.TagHandler?);
+    method public static String toHtml(android.text.Spanned, int);
+    field public static final int FROM_HTML_MODE_COMPACT = 63; // 0x3f
+    field public static final int FROM_HTML_MODE_LEGACY = 0; // 0x0
+    field public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 256; // 0x100
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 32; // 0x20
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 16; // 0x10
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 2; // 0x2
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 8; // 0x8
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 4; // 0x4
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 1; // 0x1
+    field public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0; // 0x0
+    field public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 1; // 0x1
+  }
+
+  public final class ICUCompat {
+    method public static String? maximizeAndGetScript(java.util.Locale!);
+  }
+
+  public class PrecomputedTextCompat implements android.text.Spannable {
+    method public char charAt(int);
+    method public static androidx.core.text.PrecomputedTextCompat! create(CharSequence, androidx.core.text.PrecomputedTextCompat.Params);
+    method @IntRange(from=0) public int getParagraphCount();
+    method @IntRange(from=0) public int getParagraphEnd(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getParagraphStart(@IntRange(from=0) int);
+    method public androidx.core.text.PrecomputedTextCompat.Params getParams();
+    method public int getSpanEnd(Object!);
+    method public int getSpanFlags(Object!);
+    method public int getSpanStart(Object!);
+    method public <T> T![]! getSpans(int, int, Class<T!>!);
+    method @UiThread public static java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>! getTextFuture(CharSequence, androidx.core.text.PrecomputedTextCompat.Params, java.util.concurrent.Executor?);
+    method public int length();
+    method public int nextSpanTransition(int, int, Class!);
+    method public void removeSpan(Object!);
+    method public void setSpan(Object!, int, int, int);
+    method public CharSequence! subSequence(int, int);
+  }
+
+  public static final class PrecomputedTextCompat.Params {
+    ctor @RequiresApi(28) public PrecomputedTextCompat.Params(android.text.PrecomputedText.Params);
+    method @RequiresApi(23) public int getBreakStrategy();
+    method @RequiresApi(23) public int getHyphenationFrequency();
+    method @RequiresApi(18) public android.text.TextDirectionHeuristic? getTextDirection();
+    method public android.text.TextPaint getTextPaint();
+  }
+
+  public static class PrecomputedTextCompat.Params.Builder {
+    ctor public PrecomputedTextCompat.Params.Builder(android.text.TextPaint);
+    method public androidx.core.text.PrecomputedTextCompat.Params build();
+    method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setBreakStrategy(int);
+    method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setHyphenationFrequency(int);
+    method @RequiresApi(18) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setTextDirection(android.text.TextDirectionHeuristic);
+  }
+
+  public interface TextDirectionHeuristicCompat {
+    method public boolean isRtl(char[]!, int, int);
+    method public boolean isRtl(CharSequence!, int, int);
+  }
+
+  public final class TextDirectionHeuristicsCompat {
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! ANYRTL_LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_RTL;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! LOCALE;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! RTL;
+  }
+
+  public final class TextUtilsCompat {
+    method public static int getLayoutDirectionFromLocale(java.util.Locale?);
+    method public static String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.text.util {
+
+  public final class LinkifyCompat {
+    method public static boolean addLinks(android.text.Spannable, int);
+    method public static boolean addLinks(android.widget.TextView, int);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+  }
+
+}
+
+package androidx.core.util {
+
+  public class AtomicFile {
+    ctor public AtomicFile(java.io.File);
+    method public void delete();
+    method public void failWrite(java.io.FileOutputStream?);
+    method public void finishWrite(java.io.FileOutputStream?);
+    method public java.io.File getBaseFile();
+    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+    method public byte[] readFully() throws java.io.IOException;
+    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+  }
+
+  public interface Consumer<T> {
+    method public void accept(T!);
+  }
+
+  public class ObjectsCompat {
+    method public static boolean equals(Object?, Object?);
+    method public static int hash(java.lang.Object!...);
+    method public static int hashCode(Object?);
+    method public static String? toString(Object?, String?);
+  }
+
+  public class Pair<F, S> {
+    ctor public Pair(F!, S!);
+    method public static <A, B> androidx.core.util.Pair<A!,B!> create(A!, B!);
+    field public final F! first;
+    field public final S! second;
+  }
+
+  public final class PatternsCompat {
+    field public static final java.util.regex.Pattern DOMAIN_NAME;
+    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field public static final java.util.regex.Pattern WEB_URL;
+  }
+
+  public final class Pools {
+  }
+
+  public static interface Pools.Pool<T> {
+    method public T? acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SimplePool<T> implements androidx.core.util.Pools.Pool<T> {
+    ctor public Pools.SimplePool(int);
+    method public T! acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SynchronizedPool<T> extends androidx.core.util.Pools.SimplePool<T> {
+    ctor public Pools.SynchronizedPool(int);
+  }
+
+  public interface Predicate<T> {
+    method public boolean test(T!);
+  }
+
+  public interface Supplier<T> {
+    method public T! get();
+  }
+
+}
+
+package androidx.core.view {
+
+  public class AccessibilityDelegateCompat {
+    ctor public AccessibilityDelegateCompat();
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat! getAccessibilityNodeProvider(android.view.View!);
+    method public void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public void onInitializeAccessibilityNodeInfo(android.view.View!, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method public void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public boolean performAccessibilityAction(android.view.View!, int, android.os.Bundle!);
+    method public void sendAccessibilityEvent(android.view.View!, int);
+    method public void sendAccessibilityEventUnchecked(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+  }
+
+  public abstract class ActionProvider {
+    ctor public ActionProvider(android.content.Context!);
+    method public android.content.Context! getContext();
+    method public boolean hasSubMenu();
+    method public boolean isVisible();
+    method public abstract android.view.View! onCreateActionView();
+    method public android.view.View! onCreateActionView(android.view.MenuItem!);
+    method public boolean onPerformDefaultAction();
+    method public void onPrepareSubMenu(android.view.SubMenu!);
+    method public boolean overridesItemVisibility();
+    method public void refreshVisibility();
+    method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener!);
+  }
+
+  public static interface ActionProvider.VisibilityListener {
+    method public void onActionProviderVisibilityChanged(boolean);
+  }
+
+  public final class ContentInfoCompat {
+    method public android.content.ClipData getClip();
+    method public android.os.Bundle? getExtras();
+    method public int getFlags();
+    method public android.net.Uri? getLinkUri();
+    method public int getSource();
+    method public android.util.Pair<androidx.core.view.ContentInfoCompat!,androidx.core.view.ContentInfoCompat!> partition(androidx.core.util.Predicate<android.content.ClipData.Item!>);
+    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+  }
+
+  public static final class ContentInfoCompat.Builder {
+    ctor public ContentInfoCompat.Builder(androidx.core.view.ContentInfoCompat);
+    ctor public ContentInfoCompat.Builder(android.content.ClipData, int);
+    method public androidx.core.view.ContentInfoCompat build();
+    method public androidx.core.view.ContentInfoCompat.Builder setClip(android.content.ClipData);
+    method public androidx.core.view.ContentInfoCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.view.ContentInfoCompat.Builder setFlags(int);
+    method public androidx.core.view.ContentInfoCompat.Builder setLinkUri(android.net.Uri?);
+    method public androidx.core.view.ContentInfoCompat.Builder setSource(int);
+  }
+
+  public final class DisplayCompat {
+    method public static androidx.core.view.DisplayCompat.ModeCompat![] getSupportedModes(android.content.Context, android.view.Display);
+  }
+
+  public static final class DisplayCompat.ModeCompat {
+    method public int getPhysicalHeight();
+    method public int getPhysicalWidth();
+    method public boolean isNative();
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public android.view.Display.Mode? toMode();
+  }
+
+  public final class DisplayCutoutCompat {
+    ctor public DisplayCutoutCompat(android.graphics.Rect!, java.util.List<android.graphics.Rect!>!);
+    ctor public DisplayCutoutCompat(androidx.core.graphics.Insets, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, androidx.core.graphics.Insets);
+    method public java.util.List<android.graphics.Rect!> getBoundingRects();
+    method public int getSafeInsetBottom();
+    method public int getSafeInsetLeft();
+    method public int getSafeInsetRight();
+    method public int getSafeInsetTop();
+    method public androidx.core.graphics.Insets getWaterfallInsets();
+  }
+
+  public final class DragAndDropPermissionsCompat {
+    method public void release();
+  }
+
+  public class DragStartHelper {
+    ctor public DragStartHelper(android.view.View!, androidx.core.view.DragStartHelper.OnDragStartListener!);
+    method public void attach();
+    method public void detach();
+    method public void getTouchPosition(android.graphics.Point!);
+    method public boolean onLongClick(android.view.View!);
+    method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+  }
+
+  public static interface DragStartHelper.OnDragStartListener {
+    method public boolean onDragStart(android.view.View!, androidx.core.view.DragStartHelper!);
+  }
+
+  public final class GestureDetectorCompat {
+    ctor public GestureDetectorCompat(android.content.Context!, android.view.GestureDetector.OnGestureListener!);
+    ctor public GestureDetectorCompat(android.content.Context!, android.view.GestureDetector.OnGestureListener!, android.os.Handler!);
+    method public boolean isLongpressEnabled();
+    method public boolean onTouchEvent(android.view.MotionEvent!);
+    method public void setIsLongpressEnabled(boolean);
+    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener!);
+  }
+
+  public final class GravityCompat {
+    method public static void apply(int, int, int, android.graphics.Rect!, android.graphics.Rect!, int);
+    method public static void apply(int, int, int, android.graphics.Rect!, int, int, android.graphics.Rect!, int);
+    method public static void applyDisplay(int, android.graphics.Rect!, android.graphics.Rect!, int);
+    method public static int getAbsoluteGravity(int, int);
+    field public static final int END = 8388613; // 0x800005
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+    field public static final int START = 8388611; // 0x800003
+  }
+
+  public final class InputDeviceCompat {
+    field public static final int SOURCE_ANY = -256; // 0xffffff00
+    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+    field public static final int SOURCE_DPAD = 513; // 0x201
+    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+    field public static final int SOURCE_KEYBOARD = 257; // 0x101
+    field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+    field public static final int SOURCE_STYLUS = 16386; // 0x4002
+    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+    field public static final int SOURCE_UNKNOWN = 0; // 0x0
+  }
+
+  public final class LayoutInflaterCompat {
+    method @Deprecated public static androidx.core.view.LayoutInflaterFactory! getFactory(android.view.LayoutInflater!);
+    method @Deprecated public static void setFactory(android.view.LayoutInflater, androidx.core.view.LayoutInflaterFactory);
+    method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+  }
+
+  @Deprecated public interface LayoutInflaterFactory {
+    method @Deprecated public android.view.View! onCreateView(android.view.View!, String!, android.content.Context!, android.util.AttributeSet!);
+  }
+
+  public final class MarginLayoutParamsCompat {
+    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams!);
+    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams!);
+    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams!);
+    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams!);
+    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams!, int);
+    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams!, int);
+    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams!, int);
+    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams!, int);
+  }
+
+  public final class MenuCompat {
+    method public static void setGroupDividerEnabled(android.view.Menu!, boolean);
+    method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+  }
+
+  public final class MenuItemCompat {
+    method @Deprecated public static boolean collapseActionView(android.view.MenuItem!);
+    method @Deprecated public static boolean expandActionView(android.view.MenuItem!);
+    method public static androidx.core.view.ActionProvider! getActionProvider(android.view.MenuItem!);
+    method @Deprecated public static android.view.View! getActionView(android.view.MenuItem!);
+    method public static int getAlphabeticModifiers(android.view.MenuItem!);
+    method public static CharSequence! getContentDescription(android.view.MenuItem!);
+    method public static android.content.res.ColorStateList! getIconTintList(android.view.MenuItem!);
+    method public static android.graphics.PorterDuff.Mode! getIconTintMode(android.view.MenuItem!);
+    method public static int getNumericModifiers(android.view.MenuItem!);
+    method public static CharSequence! getTooltipText(android.view.MenuItem!);
+    method @Deprecated public static boolean isActionViewExpanded(android.view.MenuItem!);
+    method public static android.view.MenuItem! setActionProvider(android.view.MenuItem!, androidx.core.view.ActionProvider!);
+    method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, android.view.View!);
+    method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, int);
+    method public static void setAlphabeticShortcut(android.view.MenuItem!, char, int);
+    method public static void setContentDescription(android.view.MenuItem!, CharSequence!);
+    method public static void setIconTintList(android.view.MenuItem!, android.content.res.ColorStateList!);
+    method public static void setIconTintMode(android.view.MenuItem!, android.graphics.PorterDuff.Mode!);
+    method public static void setNumericShortcut(android.view.MenuItem!, char, int);
+    method @Deprecated public static android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem!, androidx.core.view.MenuItemCompat.OnActionExpandListener!);
+    method public static void setShortcut(android.view.MenuItem!, char, char, int, int);
+    method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+    method public static void setTooltipText(android.view.MenuItem!, CharSequence!);
+    field @Deprecated public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field @Deprecated public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field @Deprecated public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field @Deprecated public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field @Deprecated public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  @Deprecated public static interface MenuItemCompat.OnActionExpandListener {
+    method @Deprecated public boolean onMenuItemActionCollapse(android.view.MenuItem!);
+    method @Deprecated public boolean onMenuItemActionExpand(android.view.MenuItem!);
+  }
+
+  public final class MotionEventCompat {
+    method @Deprecated public static int findPointerIndex(android.view.MotionEvent!, int);
+    method @Deprecated public static int getActionIndex(android.view.MotionEvent!);
+    method @Deprecated public static int getActionMasked(android.view.MotionEvent!);
+    method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int);
+    method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int, int);
+    method @Deprecated public static int getButtonState(android.view.MotionEvent!);
+    method @Deprecated public static int getPointerCount(android.view.MotionEvent!);
+    method @Deprecated public static int getPointerId(android.view.MotionEvent!, int);
+    method @Deprecated public static int getSource(android.view.MotionEvent!);
+    method @Deprecated public static float getX(android.view.MotionEvent!, int);
+    method @Deprecated public static float getY(android.view.MotionEvent!, int);
+    method public static boolean isFromSource(android.view.MotionEvent!, int);
+    field @Deprecated public static final int ACTION_HOVER_ENTER = 9; // 0x9
+    field @Deprecated public static final int ACTION_HOVER_EXIT = 10; // 0xa
+    field @Deprecated public static final int ACTION_HOVER_MOVE = 7; // 0x7
+    field @Deprecated public static final int ACTION_MASK = 255; // 0xff
+    field @Deprecated public static final int ACTION_POINTER_DOWN = 5; // 0x5
+    field @Deprecated public static final int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+    field @Deprecated public static final int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+    field @Deprecated public static final int ACTION_POINTER_UP = 6; // 0x6
+    field @Deprecated public static final int ACTION_SCROLL = 8; // 0x8
+    field @Deprecated public static final int AXIS_BRAKE = 23; // 0x17
+    field @Deprecated public static final int AXIS_DISTANCE = 24; // 0x18
+    field @Deprecated public static final int AXIS_GAS = 22; // 0x16
+    field @Deprecated public static final int AXIS_GENERIC_1 = 32; // 0x20
+    field @Deprecated public static final int AXIS_GENERIC_10 = 41; // 0x29
+    field @Deprecated public static final int AXIS_GENERIC_11 = 42; // 0x2a
+    field @Deprecated public static final int AXIS_GENERIC_12 = 43; // 0x2b
+    field @Deprecated public static final int AXIS_GENERIC_13 = 44; // 0x2c
+    field @Deprecated public static final int AXIS_GENERIC_14 = 45; // 0x2d
+    field @Deprecated public static final int AXIS_GENERIC_15 = 46; // 0x2e
+    field @Deprecated public static final int AXIS_GENERIC_16 = 47; // 0x2f
+    field @Deprecated public static final int AXIS_GENERIC_2 = 33; // 0x21
+    field @Deprecated public static final int AXIS_GENERIC_3 = 34; // 0x22
+    field @Deprecated public static final int AXIS_GENERIC_4 = 35; // 0x23
+    field @Deprecated public static final int AXIS_GENERIC_5 = 36; // 0x24
+    field @Deprecated public static final int AXIS_GENERIC_6 = 37; // 0x25
+    field @Deprecated public static final int AXIS_GENERIC_7 = 38; // 0x26
+    field @Deprecated public static final int AXIS_GENERIC_8 = 39; // 0x27
+    field @Deprecated public static final int AXIS_GENERIC_9 = 40; // 0x28
+    field @Deprecated public static final int AXIS_HAT_X = 15; // 0xf
+    field @Deprecated public static final int AXIS_HAT_Y = 16; // 0x10
+    field @Deprecated public static final int AXIS_HSCROLL = 10; // 0xa
+    field @Deprecated public static final int AXIS_LTRIGGER = 17; // 0x11
+    field @Deprecated public static final int AXIS_ORIENTATION = 8; // 0x8
+    field @Deprecated public static final int AXIS_PRESSURE = 2; // 0x2
+    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+    field @Deprecated public static final int AXIS_RTRIGGER = 18; // 0x12
+    field @Deprecated public static final int AXIS_RUDDER = 20; // 0x14
+    field @Deprecated public static final int AXIS_RX = 12; // 0xc
+    field @Deprecated public static final int AXIS_RY = 13; // 0xd
+    field @Deprecated public static final int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
+    field @Deprecated public static final int AXIS_SIZE = 3; // 0x3
+    field @Deprecated public static final int AXIS_THROTTLE = 19; // 0x13
+    field @Deprecated public static final int AXIS_TILT = 25; // 0x19
+    field @Deprecated public static final int AXIS_TOOL_MAJOR = 6; // 0x6
+    field @Deprecated public static final int AXIS_TOOL_MINOR = 7; // 0x7
+    field @Deprecated public static final int AXIS_TOUCH_MAJOR = 4; // 0x4
+    field @Deprecated public static final int AXIS_TOUCH_MINOR = 5; // 0x5
+    field @Deprecated public static final int AXIS_VSCROLL = 9; // 0x9
+    field @Deprecated public static final int AXIS_WHEEL = 21; // 0x15
+    field @Deprecated public static final int AXIS_X = 0; // 0x0
+    field @Deprecated public static final int AXIS_Y = 1; // 0x1
+    field @Deprecated public static final int AXIS_Z = 11; // 0xb
+    field @Deprecated public static final int BUTTON_PRIMARY = 1; // 0x1
+  }
+
+  public interface NestedScrollingChild {
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+    method public boolean hasNestedScrollingParent();
+    method public boolean isNestedScrollingEnabled();
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public void stopNestedScroll();
+  }
+
+  public interface NestedScrollingChild2 extends androidx.core.view.NestedScrollingChild {
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public interface NestedScrollingChild3 extends androidx.core.view.NestedScrollingChild2 {
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]);
+  }
+
+  public class NestedScrollingChildHelper {
+    ctor public NestedScrollingChildHelper(android.view.View);
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]?);
+    method public boolean hasNestedScrollingParent();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isNestedScrollingEnabled();
+    method public void onDetachedFromWindow();
+    method public void onStopNestedScroll(android.view.View);
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll();
+    method public void stopNestedScroll(int);
+  }
+
+  public interface NestedScrollingParent {
+    method public int getNestedScrollAxes();
+    method public boolean onNestedFling(android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.view.View, float, float);
+    method public void onNestedPreScroll(android.view.View, int, int, int[]);
+    method public void onNestedScroll(android.view.View, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.view.View);
+  }
+
+  public interface NestedScrollingParent2 extends androidx.core.view.NestedScrollingParent {
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+  }
+
+  public interface NestedScrollingParent3 extends androidx.core.view.NestedScrollingParent2 {
+    method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]);
+  }
+
+  public class NestedScrollingParentHelper {
+    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+    method public int getNestedScrollAxes();
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View);
+    method public void onStopNestedScroll(android.view.View, int);
+  }
+
+  public interface OnApplyWindowInsetsListener {
+    method public androidx.core.view.WindowInsetsCompat! onApplyWindowInsets(android.view.View!, androidx.core.view.WindowInsetsCompat!);
+  }
+
+  public interface OnReceiveContentListener {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+  }
+
+  public interface OnReceiveContentViewBehavior {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+  }
+
+  public final class OneShotPreDrawListener implements android.view.View.OnAttachStateChangeListener android.view.ViewTreeObserver.OnPreDrawListener {
+    method public static androidx.core.view.OneShotPreDrawListener add(android.view.View, Runnable);
+    method public boolean onPreDraw();
+    method public void onViewAttachedToWindow(android.view.View!);
+    method public void onViewDetachedFromWindow(android.view.View!);
+    method public void removeListener();
+  }
+
+  public final class PointerIconCompat {
+    method public static androidx.core.view.PointerIconCompat! create(android.graphics.Bitmap!, float, float);
+    method public static androidx.core.view.PointerIconCompat! getSystemIcon(android.content.Context!, int);
+    method public static androidx.core.view.PointerIconCompat! load(android.content.res.Resources!, int);
+    field public static final int TYPE_ALIAS = 1010; // 0x3f2
+    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int TYPE_ARROW = 1000; // 0x3e8
+    field public static final int TYPE_CELL = 1006; // 0x3ee
+    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int TYPE_COPY = 1011; // 0x3f3
+    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+    field public static final int TYPE_GRAB = 1020; // 0x3fc
+    field public static final int TYPE_GRABBING = 1021; // 0x3fd
+    field public static final int TYPE_HAND = 1002; // 0x3ea
+    field public static final int TYPE_HELP = 1003; // 0x3eb
+    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_TEXT = 1008; // 0x3f0
+    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int TYPE_WAIT = 1004; // 0x3ec
+    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
+  public final class ScaleGestureDetectorCompat {
+    method @Deprecated public static boolean isQuickScaleEnabled(Object!);
+    method public static boolean isQuickScaleEnabled(android.view.ScaleGestureDetector!);
+    method @Deprecated public static void setQuickScaleEnabled(Object!, boolean);
+    method public static void setQuickScaleEnabled(android.view.ScaleGestureDetector!, boolean);
+  }
+
+  public interface ScrollingView {
+    method public int computeHorizontalScrollExtent();
+    method public int computeHorizontalScrollOffset();
+    method public int computeHorizontalScrollRange();
+    method public int computeVerticalScrollExtent();
+    method public int computeVerticalScrollOffset();
+    method public int computeVerticalScrollRange();
+  }
+
+  public interface TintableBackgroundView {
+    method public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  @Deprecated public final class VelocityTrackerCompat {
+    method @Deprecated public static float getXVelocity(android.view.VelocityTracker!, int);
+    method @Deprecated public static float getYVelocity(android.view.VelocityTracker!, int);
+  }
+
+  public class ViewCompat {
+    ctor @Deprecated protected ViewCompat();
+    method public static int addAccessibilityAction(android.view.View, CharSequence, androidx.core.view.accessibility.AccessibilityViewCommand);
+    method public static void addKeyboardNavigationClusters(android.view.View, java.util.Collection<android.view.View!>, int);
+    method public static void addOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+    method public static androidx.core.view.ViewPropertyAnimatorCompat animate(android.view.View);
+    method @Deprecated public static boolean canScrollHorizontally(android.view.View!, int);
+    method @Deprecated public static boolean canScrollVertically(android.view.View!, int);
+    method public static void cancelDragAndDrop(android.view.View);
+    method @Deprecated public static int combineMeasuredStates(int, int);
+    method public static androidx.core.view.WindowInsetsCompat computeSystemWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat, android.graphics.Rect);
+    method public static androidx.core.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+    method public static void dispatchFinishTemporaryDetach(android.view.View);
+    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?, int);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?);
+    method public static void dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, int, int[]);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, int);
+    method public static void dispatchStartTemporaryDetach(android.view.View);
+    method public static void enableAccessibleClickableSpanSupport(android.view.View!);
+    method public static int generateViewId();
+    method public static androidx.core.view.AccessibilityDelegateCompat? getAccessibilityDelegate(android.view.View);
+    method public static int getAccessibilityLiveRegion(android.view.View);
+    method public static androidx.core.view.accessibility.AccessibilityNodeProviderCompat! getAccessibilityNodeProvider(android.view.View);
+    method @UiThread public static CharSequence! getAccessibilityPaneTitle(android.view.View!);
+    method @Deprecated public static float getAlpha(android.view.View!);
+    method public static android.content.res.ColorStateList! getBackgroundTintList(android.view.View);
+    method public static android.graphics.PorterDuff.Mode! getBackgroundTintMode(android.view.View);
+    method public static android.graphics.Rect? getClipBounds(android.view.View);
+    method public static android.view.Display? getDisplay(android.view.View);
+    method public static float getElevation(android.view.View);
+    method public static boolean getFitsSystemWindows(android.view.View);
+    method public static int getImportantForAccessibility(android.view.View);
+    method public static int getImportantForAutofill(android.view.View);
+    method public static int getLabelFor(android.view.View);
+    method @Deprecated public static int getLayerType(android.view.View!);
+    method public static int getLayoutDirection(android.view.View);
+    method @Deprecated public static android.graphics.Matrix? getMatrix(android.view.View!);
+    method @Deprecated public static int getMeasuredHeightAndState(android.view.View!);
+    method @Deprecated public static int getMeasuredState(android.view.View!);
+    method @Deprecated public static int getMeasuredWidthAndState(android.view.View!);
+    method public static int getMinimumHeight(android.view.View);
+    method public static int getMinimumWidth(android.view.View);
+    method public static int getNextClusterForwardId(android.view.View);
+    method public static String![]? getOnReceiveContentMimeTypes(android.view.View);
+    method @Deprecated public static int getOverScrollMode(android.view.View!);
+    method @Px public static int getPaddingEnd(android.view.View);
+    method @Px public static int getPaddingStart(android.view.View);
+    method public static android.view.ViewParent! getParentForAccessibility(android.view.View);
+    method @Deprecated public static float getPivotX(android.view.View!);
+    method @Deprecated public static float getPivotY(android.view.View!);
+    method public static androidx.core.view.WindowInsetsCompat? getRootWindowInsets(android.view.View);
+    method @Deprecated public static float getRotation(android.view.View!);
+    method @Deprecated public static float getRotationX(android.view.View!);
+    method @Deprecated public static float getRotationY(android.view.View!);
+    method @Deprecated public static float getScaleX(android.view.View!);
+    method @Deprecated public static float getScaleY(android.view.View!);
+    method public static int getScrollIndicators(android.view.View);
+    method @UiThread public static final CharSequence? getStateDescription(android.view.View);
+    method public static java.util.List<android.graphics.Rect!> getSystemGestureExclusionRects(android.view.View);
+    method public static String? getTransitionName(android.view.View);
+    method @Deprecated public static float getTranslationX(android.view.View!);
+    method @Deprecated public static float getTranslationY(android.view.View!);
+    method public static float getTranslationZ(android.view.View);
+    method public static androidx.core.view.WindowInsetsControllerCompat? getWindowInsetsController(android.view.View);
+    method public static int getWindowSystemUiVisibility(android.view.View);
+    method @Deprecated public static float getX(android.view.View!);
+    method @Deprecated public static float getY(android.view.View!);
+    method public static float getZ(android.view.View);
+    method public static boolean hasAccessibilityDelegate(android.view.View);
+    method public static boolean hasExplicitFocusable(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View, int);
+    method public static boolean hasOnClickListeners(android.view.View);
+    method public static boolean hasOverlappingRendering(android.view.View);
+    method public static boolean hasTransientState(android.view.View);
+    method @UiThread public static boolean isAccessibilityHeading(android.view.View!);
+    method public static boolean isAttachedToWindow(android.view.View);
+    method public static boolean isFocusedByDefault(android.view.View);
+    method public static boolean isImportantForAccessibility(android.view.View);
+    method public static boolean isImportantForAutofill(android.view.View);
+    method public static boolean isInLayout(android.view.View);
+    method public static boolean isKeyboardNavigationCluster(android.view.View);
+    method public static boolean isLaidOut(android.view.View);
+    method public static boolean isLayoutDirectionResolved(android.view.View);
+    method public static boolean isNestedScrollingEnabled(android.view.View);
+    method @Deprecated public static boolean isOpaque(android.view.View!);
+    method public static boolean isPaddingRelative(android.view.View);
+    method @UiThread public static boolean isScreenReaderFocusable(android.view.View!);
+    method @Deprecated public static void jumpDrawablesToCurrentState(android.view.View!);
+    method public static android.view.View! keyboardNavigationClusterSearch(android.view.View, android.view.View!, int);
+    method public static void offsetLeftAndRight(android.view.View, int);
+    method public static void offsetTopAndBottom(android.view.View, int);
+    method public static androidx.core.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+    method @Deprecated public static void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method @Deprecated public static void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle!);
+    method public static androidx.core.view.ContentInfoCompat? performReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+    method public static void postInvalidateOnAnimation(android.view.View);
+    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+    method public static void postOnAnimation(android.view.View, Runnable!);
+    method public static void postOnAnimationDelayed(android.view.View, Runnable!, long);
+    method public static void removeAccessibilityAction(android.view.View, int);
+    method public static void removeOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+    method public static void replaceAccessibilityAction(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat, CharSequence?, androidx.core.view.accessibility.AccessibilityViewCommand?);
+    method public static void requestApplyInsets(android.view.View);
+    method public static <T extends android.view.View> T requireViewById(android.view.View, @IdRes int);
+    method @Deprecated public static int resolveSizeAndState(int, int, int);
+    method public static boolean restoreDefaultFocus(android.view.View);
+    method public static void saveAttributeDataForStyleable(android.view.View, android.content.Context, int[], android.util.AttributeSet?, android.content.res.TypedArray, int, int);
+    method public static void setAccessibilityDelegate(android.view.View, androidx.core.view.AccessibilityDelegateCompat!);
+    method @UiThread public static void setAccessibilityHeading(android.view.View!, boolean);
+    method public static void setAccessibilityLiveRegion(android.view.View, int);
+    method @UiThread public static void setAccessibilityPaneTitle(android.view.View!, CharSequence!);
+    method @Deprecated public static void setActivated(android.view.View!, boolean);
+    method @Deprecated public static void setAlpha(android.view.View!, @FloatRange(from=0.0, to=1.0) float);
+    method public static void setAutofillHints(android.view.View, java.lang.String!...);
+    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable?);
+    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList!);
+    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode!);
+    method @Deprecated public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup!, boolean);
+    method public static void setClipBounds(android.view.View, android.graphics.Rect!);
+    method public static void setElevation(android.view.View, float);
+    method @Deprecated public static void setFitsSystemWindows(android.view.View!, boolean);
+    method public static void setFocusedByDefault(android.view.View, boolean);
+    method public static void setHasTransientState(android.view.View, boolean);
+    method public static void setImportantForAccessibility(android.view.View, int);
+    method public static void setImportantForAutofill(android.view.View, int);
+    method public static void setKeyboardNavigationCluster(android.view.View, boolean);
+    method public static void setLabelFor(android.view.View, @IdRes int);
+    method public static void setLayerPaint(android.view.View, android.graphics.Paint!);
+    method @Deprecated public static void setLayerType(android.view.View!, int, android.graphics.Paint!);
+    method public static void setLayoutDirection(android.view.View, int);
+    method public static void setNestedScrollingEnabled(android.view.View, boolean);
+    method public static void setNextClusterForwardId(android.view.View, int);
+    method public static void setOnApplyWindowInsetsListener(android.view.View, androidx.core.view.OnApplyWindowInsetsListener?);
+    method public static void setOnReceiveContentListener(android.view.View, String![]?, androidx.core.view.OnReceiveContentListener?);
+    method @Deprecated public static void setOverScrollMode(android.view.View!, int);
+    method public static void setPaddingRelative(android.view.View, @Px int, @Px int, @Px int, @Px int);
+    method @Deprecated public static void setPivotX(android.view.View!, float);
+    method @Deprecated public static void setPivotY(android.view.View!, float);
+    method public static void setPointerIcon(android.view.View, androidx.core.view.PointerIconCompat!);
+    method @Deprecated public static void setRotation(android.view.View!, float);
+    method @Deprecated public static void setRotationX(android.view.View!, float);
+    method @Deprecated public static void setRotationY(android.view.View!, float);
+    method @Deprecated public static void setSaveFromParentEnabled(android.view.View!, boolean);
+    method @Deprecated public static void setScaleX(android.view.View!, float);
+    method @Deprecated public static void setScaleY(android.view.View!, float);
+    method @UiThread public static void setScreenReaderFocusable(android.view.View!, boolean);
+    method public static void setScrollIndicators(android.view.View, int);
+    method public static void setScrollIndicators(android.view.View, int, int);
+    method @UiThread public static void setStateDescription(android.view.View, CharSequence?);
+    method public static void setSystemGestureExclusionRects(android.view.View, java.util.List<android.graphics.Rect!>);
+    method public static void setTooltipText(android.view.View, CharSequence?);
+    method public static void setTransitionName(android.view.View, String!);
+    method @Deprecated public static void setTranslationX(android.view.View!, float);
+    method @Deprecated public static void setTranslationY(android.view.View!, float);
+    method public static void setTranslationZ(android.view.View, float);
+    method public static void setWindowInsetsAnimationCallback(android.view.View, androidx.core.view.WindowInsetsAnimationCompat.Callback?);
+    method @Deprecated public static void setX(android.view.View!, float);
+    method @Deprecated public static void setY(android.view.View!, float);
+    method public static void setZ(android.view.View, float);
+    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData!, android.view.View.DragShadowBuilder!, Object!, int);
+    method public static boolean startNestedScroll(android.view.View, int);
+    method public static boolean startNestedScroll(android.view.View, int, int);
+    method public static void stopNestedScroll(android.view.View);
+    method public static void stopNestedScroll(android.view.View, int);
+    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder!);
+    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+    field @Deprecated public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
+    field @Deprecated public static final int LAYER_TYPE_NONE = 0; // 0x0
+    field @Deprecated public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+    field @Deprecated public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+    field @Deprecated public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+    field @Deprecated public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
+    field @Deprecated public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field @Deprecated public static final int OVER_SCROLL_ALWAYS = 0; // 0x0
+    field @Deprecated public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+    field @Deprecated public static final int OVER_SCROLL_NEVER = 2; // 0x2
+    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+    field public static final int TYPE_NON_TOUCH = 1; // 0x1
+    field public static final int TYPE_TOUCH = 0; // 0x0
+  }
+
+  public static interface ViewCompat.OnUnhandledKeyEventListenerCompat {
+    method public boolean onUnhandledKeyEvent(android.view.View!, android.view.KeyEvent!);
+  }
+
+  public final class ViewConfigurationCompat {
+    method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static int getScaledHoverSlop(android.view.ViewConfiguration!);
+    method @Deprecated public static int getScaledPagingTouchSlop(android.view.ViewConfiguration!);
+    method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method @Deprecated public static boolean hasPermanentMenuKey(android.view.ViewConfiguration!);
+    method public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(android.view.ViewConfiguration!, android.content.Context);
+  }
+
+  public final class ViewGroupCompat {
+    method public static int getLayoutMode(android.view.ViewGroup);
+    method public static int getNestedScrollAxes(android.view.ViewGroup);
+    method public static boolean isTransitionGroup(android.view.ViewGroup);
+    method @Deprecated public static boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static void setLayoutMode(android.view.ViewGroup, int);
+    method @Deprecated public static void setMotionEventSplittingEnabled(android.view.ViewGroup!, boolean);
+    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+  }
+
+  public final class ViewParentCompat {
+    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent!, android.view.View!, android.view.View!, int);
+    method public static boolean onNestedFling(android.view.ViewParent!, android.view.View!, float, float, boolean);
+    method public static boolean onNestedPreFling(android.view.ViewParent!, android.view.View!, float, float);
+    method public static void onNestedPreScroll(android.view.ViewParent!, android.view.View!, int, int, int[]!);
+    method public static void onNestedPreScroll(android.view.ViewParent!, android.view.View!, int, int, int[]!, int);
+    method public static void onNestedScroll(android.view.ViewParent!, android.view.View!, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent!, android.view.View!, int, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent!, android.view.View!, int, int, int, int, int, int[]);
+    method public static void onNestedScrollAccepted(android.view.ViewParent!, android.view.View!, android.view.View!, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent!, android.view.View!, android.view.View!, int, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent!, android.view.View!, android.view.View!, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent!, android.view.View!, android.view.View!, int, int);
+    method public static void onStopNestedScroll(android.view.ViewParent!, android.view.View!);
+    method public static void onStopNestedScroll(android.view.ViewParent!, android.view.View!, int);
+    method @Deprecated public static boolean requestSendAccessibilityEvent(android.view.ViewParent!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+  }
+
+  public final class ViewPropertyAnimatorCompat {
+    method public androidx.core.view.ViewPropertyAnimatorCompat! alpha(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public android.view.animation.Interpolator! getInterpolator();
+    method public long getStartDelay();
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotation(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setDuration(long);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setInterpolator(android.view.animation.Interpolator!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setListener(androidx.core.view.ViewPropertyAnimatorListener!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setStartDelay(long);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setUpdateListener(androidx.core.view.ViewPropertyAnimatorUpdateListener!);
+    method public void start();
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationZ(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationZBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! withEndAction(Runnable!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! withLayer();
+    method public androidx.core.view.ViewPropertyAnimatorCompat! withStartAction(Runnable!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! x(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! xBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! y(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! yBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! z(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! zBy(float);
+  }
+
+  public interface ViewPropertyAnimatorListener {
+    method public void onAnimationCancel(android.view.View!);
+    method public void onAnimationEnd(android.view.View!);
+    method public void onAnimationStart(android.view.View!);
+  }
+
+  public class ViewPropertyAnimatorListenerAdapter implements androidx.core.view.ViewPropertyAnimatorListener {
+    ctor public ViewPropertyAnimatorListenerAdapter();
+    method public void onAnimationCancel(android.view.View!);
+    method public void onAnimationEnd(android.view.View!);
+    method public void onAnimationStart(android.view.View!);
+  }
+
+  public interface ViewPropertyAnimatorUpdateListener {
+    method public void onAnimationUpdate(android.view.View!);
+  }
+
+  public final class WindowCompat {
+    method public static androidx.core.view.WindowInsetsControllerCompat? getInsetsController(android.view.Window, android.view.View);
+    method public static <T extends android.view.View> T requireViewById(android.view.Window, @IdRes int);
+    method public static void setDecorFitsSystemWindows(android.view.Window, boolean);
+    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+  }
+
+  public final class WindowInsetsAnimationCompat {
+    ctor public WindowInsetsAnimationCompat(int, android.view.animation.Interpolator?, long);
+    method @FloatRange(from=0.0f, to=1.0f) public float getAlpha();
+    method public long getDurationMillis();
+    method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+    method public float getInterpolatedFraction();
+    method public android.view.animation.Interpolator? getInterpolator();
+    method public int getTypeMask();
+    method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float);
+    method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public static final class WindowInsetsAnimationCompat.Bounds {
+    ctor public WindowInsetsAnimationCompat.Bounds(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    ctor @RequiresApi(30) public WindowInsetsAnimationCompat.Bounds(android.view.WindowInsetsAnimation.Bounds);
+    method public androidx.core.graphics.Insets getLowerBound();
+    method public androidx.core.graphics.Insets getUpperBound();
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds inset(androidx.core.graphics.Insets);
+    method @RequiresApi(30) public android.view.WindowInsetsAnimation.Bounds toPlatformBounds();
+  }
+
+  public abstract static class WindowInsetsAnimationCompat.Callback {
+    ctor public WindowInsetsAnimationCompat.Callback(int);
+    method public final int getDispatchMode();
+    method public void onEnd(androidx.core.view.WindowInsetsAnimationCompat);
+    method public void onPrepare(androidx.core.view.WindowInsetsAnimationCompat);
+    method public abstract androidx.core.view.WindowInsetsCompat onProgress(androidx.core.view.WindowInsetsCompat, java.util.List<androidx.core.view.WindowInsetsAnimationCompat!>);
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds onStart(androidx.core.view.WindowInsetsAnimationCompat, androidx.core.view.WindowInsetsAnimationCompat.Bounds);
+    field public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1; // 0x1
+    field public static final int DISPATCH_MODE_STOP = 0; // 0x0
+  }
+
+  public interface WindowInsetsAnimationControlListenerCompat {
+    method public void onCancelled(androidx.core.view.WindowInsetsAnimationControllerCompat?);
+    method public void onFinished(androidx.core.view.WindowInsetsAnimationControllerCompat);
+    method public void onReady(androidx.core.view.WindowInsetsAnimationControllerCompat, int);
+  }
+
+  public final class WindowInsetsAnimationControllerCompat {
+    method public void finish(boolean);
+    method public float getCurrentAlpha();
+    method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+    method public androidx.core.graphics.Insets getCurrentInsets();
+    method public androidx.core.graphics.Insets getHiddenStateInsets();
+    method public androidx.core.graphics.Insets getShownStateInsets();
+    method public int getTypes();
+    method public boolean isCancelled();
+    method public boolean isFinished();
+    method public boolean isReady();
+    method public void setInsetsAndAlpha(androidx.core.graphics.Insets?, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public class WindowInsetsCompat {
+    ctor public WindowInsetsCompat(androidx.core.view.WindowInsetsCompat?);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeDisplayCutout();
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeStableInsets();
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeSystemWindowInsets();
+    method public androidx.core.view.DisplayCutoutCompat? getDisplayCutout();
+    method public androidx.core.graphics.Insets getInsets(int);
+    method public androidx.core.graphics.Insets getInsetsIgnoringVisibility(int);
+    method @Deprecated public androidx.core.graphics.Insets getMandatorySystemGestureInsets();
+    method @Deprecated public int getStableInsetBottom();
+    method @Deprecated public int getStableInsetLeft();
+    method @Deprecated public int getStableInsetRight();
+    method @Deprecated public int getStableInsetTop();
+    method @Deprecated public androidx.core.graphics.Insets getStableInsets();
+    method @Deprecated public androidx.core.graphics.Insets getSystemGestureInsets();
+    method @Deprecated public int getSystemWindowInsetBottom();
+    method @Deprecated public int getSystemWindowInsetLeft();
+    method @Deprecated public int getSystemWindowInsetRight();
+    method @Deprecated public int getSystemWindowInsetTop();
+    method @Deprecated public androidx.core.graphics.Insets getSystemWindowInsets();
+    method @Deprecated public androidx.core.graphics.Insets getTappableElementInsets();
+    method public boolean hasInsets();
+    method @Deprecated public boolean hasStableInsets();
+    method @Deprecated public boolean hasSystemWindowInsets();
+    method public androidx.core.view.WindowInsetsCompat inset(androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean isConsumed();
+    method public boolean isRound();
+    method public boolean isVisible(int);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+    method @RequiresApi(20) public android.view.WindowInsets? toWindowInsets();
+    method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets);
+    method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets, android.view.View?);
+    field public static final androidx.core.view.WindowInsetsCompat CONSUMED;
+  }
+
+  public static final class WindowInsetsCompat.Builder {
+    ctor public WindowInsetsCompat.Builder();
+    ctor public WindowInsetsCompat.Builder(androidx.core.view.WindowInsetsCompat);
+    method public androidx.core.view.WindowInsetsCompat build();
+    method public androidx.core.view.WindowInsetsCompat.Builder setDisplayCutout(androidx.core.view.DisplayCutoutCompat?);
+    method public androidx.core.view.WindowInsetsCompat.Builder setInsets(int, androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat.Builder setInsetsIgnoringVisibility(int, androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setMandatorySystemGestureInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setStableInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemGestureInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemWindowInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setTappableElementInsets(androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat.Builder setVisible(int, boolean);
+  }
+
+  public static final class WindowInsetsCompat.Type {
+    method public static int captionBar();
+    method public static int displayCutout();
+    method public static int ime();
+    method public static int mandatorySystemGestures();
+    method public static int navigationBars();
+    method public static int statusBars();
+    method public static int systemBars();
+    method public static int systemGestures();
+    method public static int tappableElement();
+  }
+
+  public final class WindowInsetsControllerCompat {
+    ctor public WindowInsetsControllerCompat(android.view.Window, android.view.View);
+    method public void addOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void controlWindowInsetsAnimation(int, long, android.view.animation.Interpolator?, android.os.CancellationSignal?, androidx.core.view.WindowInsetsAnimationControlListenerCompat);
+    method public int getSystemBarsBehavior();
+    method public void hide(int);
+    method public boolean isAppearanceLightNavigationBars();
+    method public boolean isAppearanceLightStatusBars();
+    method public void removeOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void setAppearanceLightNavigationBars(boolean);
+    method public void setAppearanceLightStatusBars(boolean);
+    method public void setSystemBarsBehavior(int);
+    method public void show(int);
+    method @RequiresApi(30) public static androidx.core.view.WindowInsetsControllerCompat toWindowInsetsControllerCompat(android.view.WindowInsetsController);
+    field public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
+    field public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0
+    field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
+  }
+
+  public static interface WindowInsetsControllerCompat.OnControllableInsetsChangedListener {
+    method public void onControllableInsetsChanged(androidx.core.view.WindowInsetsControllerCompat, int);
+  }
+
+}
+
+package androidx.core.view.accessibility {
+
+  public final class AccessibilityClickableSpanCompat extends android.text.style.ClickableSpan {
+    method public void onClick(android.view.View);
+  }
+
+  public final class AccessibilityEventCompat {
+    method @Deprecated public static void appendRecord(android.view.accessibility.AccessibilityEvent!, androidx.core.view.accessibility.AccessibilityRecordCompat!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! asRecord(android.view.accessibility.AccessibilityEvent!);
+    method public static int getAction(android.view.accessibility.AccessibilityEvent!);
+    method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent!);
+    method public static int getMovementGranularity(android.view.accessibility.AccessibilityEvent!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! getRecord(android.view.accessibility.AccessibilityEvent!, int);
+    method @Deprecated public static int getRecordCount(android.view.accessibility.AccessibilityEvent!);
+    method public static void setAction(android.view.accessibility.AccessibilityEvent!, int);
+    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent!, int);
+    method public static void setMovementGranularity(android.view.accessibility.AccessibilityEvent!, int);
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
+    field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
+    field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
+    field public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 64; // 0x40
+    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+    field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+    field @Deprecated public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field @Deprecated public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+    field @Deprecated public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+    field @Deprecated public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+    field @Deprecated public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+  }
+
+  public final class AccessibilityManagerCompat {
+    method @Deprecated public static boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener!);
+    method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!, int);
+    method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!);
+    method @Deprecated public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager!);
+    method @Deprecated public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener!);
+  }
+
+  @Deprecated public static interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    method @Deprecated public void onAccessibilityStateChanged(boolean);
+  }
+
+  @Deprecated public abstract static class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    ctor @Deprecated public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+  }
+
+  public static interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+    method public void onTouchExplorationStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfoCompat {
+    ctor @Deprecated public AccessibilityNodeInfoCompat(Object!);
+    method public void addAction(int);
+    method public void addAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+    method public void addChild(android.view.View!);
+    method public void addChild(android.view.View!, int);
+    method public boolean canOpenPopup();
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByText(String!);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByViewId(String!);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! findFocus(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! focusSearch(int);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!>! getActionList();
+    method public int getActions();
+    method @Deprecated public void getBoundsInParent(android.graphics.Rect!);
+    method public void getBoundsInScreen(android.graphics.Rect!);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getChild(int);
+    method public int getChildCount();
+    method public CharSequence! getClassName();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! getCollectionInfo();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! getCollectionItemInfo();
+    method public CharSequence! getContentDescription();
+    method public int getDrawingOrder();
+    method public CharSequence! getError();
+    method public android.os.Bundle! getExtras();
+    method public CharSequence? getHintText();
+    method @Deprecated public Object! getInfo();
+    method public int getInputType();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabelFor();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabeledBy();
+    method public int getLiveRegion();
+    method public int getMaxTextLength();
+    method public int getMovementGranularities();
+    method public CharSequence! getPackageName();
+    method public CharSequence? getPaneTitle();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getParent();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! getRangeInfo();
+    method public CharSequence? getRoleDescription();
+    method public CharSequence? getStateDescription();
+    method public CharSequence! getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public CharSequence? getTooltipText();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat? getTouchDelegateInfo();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalAfter();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalBefore();
+    method public String! getViewIdResourceName();
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getWindow();
+    method public int getWindowId();
+    method public boolean isAccessibilityFocused();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isContentInvalid();
+    method public boolean isContextClickable();
+    method public boolean isDismissable();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isHeading();
+    method public boolean isImportantForAccessibility();
+    method public boolean isLongClickable();
+    method public boolean isMultiLine();
+    method public boolean isPassword();
+    method public boolean isScreenReaderFocusable();
+    method public boolean isScrollable();
+    method public boolean isSelected();
+    method public boolean isShowingHintText();
+    method public boolean isTextEntryKey();
+    method public boolean isVisibleToUser();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!, int);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle!);
+    method public void recycle();
+    method public boolean refresh();
+    method public boolean removeAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+    method public boolean removeChild(android.view.View!);
+    method public boolean removeChild(android.view.View!, int);
+    method public void setAccessibilityFocused(boolean);
+    method @Deprecated public void setBoundsInParent(android.graphics.Rect!);
+    method public void setBoundsInScreen(android.graphics.Rect!);
+    method public void setCanOpenPopup(boolean);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(CharSequence!);
+    method public void setClickable(boolean);
+    method public void setCollectionInfo(Object!);
+    method public void setCollectionItemInfo(Object!);
+    method public void setContentDescription(CharSequence!);
+    method public void setContentInvalid(boolean);
+    method public void setContextClickable(boolean);
+    method public void setDismissable(boolean);
+    method public void setDrawingOrder(int);
+    method public void setEditable(boolean);
+    method public void setEnabled(boolean);
+    method public void setError(CharSequence!);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setHeading(boolean);
+    method public void setHintText(CharSequence?);
+    method public void setImportantForAccessibility(boolean);
+    method public void setInputType(int);
+    method public void setLabelFor(android.view.View!);
+    method public void setLabelFor(android.view.View!, int);
+    method public void setLabeledBy(android.view.View!);
+    method public void setLabeledBy(android.view.View!, int);
+    method public void setLiveRegion(int);
+    method public void setLongClickable(boolean);
+    method public void setMaxTextLength(int);
+    method public void setMovementGranularities(int);
+    method public void setMultiLine(boolean);
+    method public void setPackageName(CharSequence!);
+    method public void setPaneTitle(CharSequence?);
+    method public void setParent(android.view.View!);
+    method public void setParent(android.view.View!, int);
+    method public void setPassword(boolean);
+    method public void setRangeInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat!);
+    method public void setRoleDescription(CharSequence?);
+    method public void setScreenReaderFocusable(boolean);
+    method public void setScrollable(boolean);
+    method public void setSelected(boolean);
+    method public void setShowingHintText(boolean);
+    method public void setSource(android.view.View!);
+    method public void setSource(android.view.View!, int);
+    method public void setStateDescription(CharSequence?);
+    method public void setText(CharSequence!);
+    method public void setTextEntryKey(boolean);
+    method public void setTextSelection(int, int);
+    method public void setTooltipText(CharSequence?);
+    method public void setTouchDelegateInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat);
+    method public void setTraversalAfter(android.view.View!);
+    method public void setTraversalAfter(android.view.View!, int);
+    method public void setTraversalBefore(android.view.View!);
+    method public void setTraversalBefore(android.view.View!, int);
+    method public void setViewIdResourceName(String!);
+    method public void setVisibleToUser(boolean);
+    method public android.view.accessibility.AccessibilityNodeInfo! unwrap();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! wrap(android.view.accessibility.AccessibilityNodeInfo);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+    field public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+    field public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+    field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final String ACTION_ARGUMENT_MOVE_WINDOW_X = "ACTION_ARGUMENT_MOVE_WINDOW_X";
+    field public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y = "ACTION_ARGUMENT_MOVE_WINDOW_Y";
+    field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
+    field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+    field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+    field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+    field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 16; // 0x10
+    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+    field public static final int ACTION_COPY = 16384; // 0x4000
+    field public static final int ACTION_CUT = 65536; // 0x10000
+    field public static final int ACTION_DISMISS = 1048576; // 0x100000
+    field public static final int ACTION_EXPAND = 262144; // 0x40000
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+    field public static final int ACTION_PASTE = 32768; // 0x8000
+    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+  }
+
+  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, CharSequence!);
+    method public int getId();
+    method public CharSequence! getLabel();
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_ACCESSIBILITY_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_SELECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COLLAPSE;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CONTEXT_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COPY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CUT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_DISMISS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_EXPAND;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_HIDE_TOOLTIP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_IME_ENTER;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_LONG_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_MOVE_WINDOW;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_HTML_ELEMENT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_DOWN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_LEFT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_RIGHT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_UP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PASTE;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PRESS_AND_HOLD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_HTML_ELEMENT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_BACKWARD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_DOWN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_FORWARD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_LEFT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_RIGHT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_TO_POSITION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_UP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SELECT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_PROGRESS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_SELECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_TEXT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_ON_SCREEN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_TOOLTIP;
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+    method public int getColumnCount();
+    method public int getRowCount();
+    method public int getSelectionMode();
+    method public boolean isHierarchical();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean, int);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean);
+    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+    field public static final int SELECTION_MODE_NONE = 0; // 0x0
+    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    method @Deprecated public boolean isHeading();
+    method public boolean isSelected();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean, boolean);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean);
+  }
+
+  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    method public float getCurrent();
+    method public float getMax();
+    method public float getMin();
+    method public int getType();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! obtain(int, float, float, float);
+    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field public static final int RANGE_TYPE_INT = 0; // 0x0
+    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+  }
+
+  public static final class AccessibilityNodeInfoCompat.TouchDelegateInfoCompat {
+    ctor public AccessibilityNodeInfoCompat.TouchDelegateInfoCompat(java.util.Map<android.graphics.Region!,android.view.View!>);
+    method public android.graphics.Region? getRegionAt(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getRegionCount();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getTargetForRegion(android.graphics.Region);
+  }
+
+  public class AccessibilityNodeProviderCompat {
+    ctor public AccessibilityNodeProviderCompat();
+    ctor public AccessibilityNodeProviderCompat(Object!);
+    method public void addExtraDataToAccessibilityNodeInfo(int, androidx.core.view.accessibility.AccessibilityNodeInfoCompat, String, android.os.Bundle?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? createAccessibilityNodeInfo(int);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>? findAccessibilityNodeInfosByText(String!, int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? findFocus(int);
+    method public Object! getProvider();
+    method public boolean performAction(int, int, android.os.Bundle!);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+  }
+
+  public class AccessibilityRecordCompat {
+    ctor @Deprecated public AccessibilityRecordCompat(Object!);
+    method @Deprecated public boolean equals(Object?);
+    method @Deprecated public int getAddedCount();
+    method @Deprecated public CharSequence! getBeforeText();
+    method @Deprecated public CharSequence! getClassName();
+    method @Deprecated public CharSequence! getContentDescription();
+    method @Deprecated public int getCurrentItemIndex();
+    method @Deprecated public int getFromIndex();
+    method @Deprecated public Object! getImpl();
+    method @Deprecated public int getItemCount();
+    method @Deprecated public int getMaxScrollX();
+    method public static int getMaxScrollX(android.view.accessibility.AccessibilityRecord!);
+    method @Deprecated public int getMaxScrollY();
+    method public static int getMaxScrollY(android.view.accessibility.AccessibilityRecord!);
+    method @Deprecated public android.os.Parcelable! getParcelableData();
+    method @Deprecated public int getRemovedCount();
+    method @Deprecated public int getScrollX();
+    method @Deprecated public int getScrollY();
+    method @Deprecated public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getSource();
+    method @Deprecated public java.util.List<java.lang.CharSequence!>! getText();
+    method @Deprecated public int getToIndex();
+    method @Deprecated public int getWindowId();
+    method @Deprecated public int hashCode();
+    method @Deprecated public boolean isChecked();
+    method @Deprecated public boolean isEnabled();
+    method @Deprecated public boolean isFullScreen();
+    method @Deprecated public boolean isPassword();
+    method @Deprecated public boolean isScrollable();
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain(androidx.core.view.accessibility.AccessibilityRecordCompat!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain();
+    method @Deprecated public void recycle();
+    method @Deprecated public void setAddedCount(int);
+    method @Deprecated public void setBeforeText(CharSequence!);
+    method @Deprecated public void setChecked(boolean);
+    method @Deprecated public void setClassName(CharSequence!);
+    method @Deprecated public void setContentDescription(CharSequence!);
+    method @Deprecated public void setCurrentItemIndex(int);
+    method @Deprecated public void setEnabled(boolean);
+    method @Deprecated public void setFromIndex(int);
+    method @Deprecated public void setFullScreen(boolean);
+    method @Deprecated public void setItemCount(int);
+    method @Deprecated public void setMaxScrollX(int);
+    method public static void setMaxScrollX(android.view.accessibility.AccessibilityRecord!, int);
+    method @Deprecated public void setMaxScrollY(int);
+    method public static void setMaxScrollY(android.view.accessibility.AccessibilityRecord!, int);
+    method @Deprecated public void setParcelableData(android.os.Parcelable!);
+    method @Deprecated public void setPassword(boolean);
+    method @Deprecated public void setRemovedCount(int);
+    method @Deprecated public void setScrollX(int);
+    method @Deprecated public void setScrollY(int);
+    method @Deprecated public void setScrollable(boolean);
+    method @Deprecated public void setSource(android.view.View!);
+    method @Deprecated public void setSource(android.view.View!, int);
+    method public static void setSource(android.view.accessibility.AccessibilityRecord, android.view.View!, int);
+    method @Deprecated public void setToIndex(int);
+  }
+
+  public interface AccessibilityViewCommand {
+    method public boolean perform(android.view.View, androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments?);
+  }
+
+  public abstract static class AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.CommandArguments();
+  }
+
+  public static final class AccessibilityViewCommand.MoveAtGranularityArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveAtGranularityArguments();
+    method public boolean getExtendSelection();
+    method public int getGranularity();
+  }
+
+  public static final class AccessibilityViewCommand.MoveHtmlArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveHtmlArguments();
+    method public String! getHTMLElement();
+  }
+
+  public static final class AccessibilityViewCommand.MoveWindowArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveWindowArguments();
+    method public int getX();
+    method public int getY();
+  }
+
+  public static final class AccessibilityViewCommand.ScrollToPositionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.ScrollToPositionArguments();
+    method public int getColumn();
+    method public int getRow();
+  }
+
+  public static final class AccessibilityViewCommand.SetProgressArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetProgressArguments();
+    method public float getProgress();
+  }
+
+  public static final class AccessibilityViewCommand.SetSelectionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetSelectionArguments();
+    method public int getEnd();
+    method public int getStart();
+  }
+
+  public static final class AccessibilityViewCommand.SetTextArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetTextArguments();
+    method public CharSequence! getText();
+  }
+
+  public class AccessibilityWindowInfoCompat {
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getAnchor();
+    method public void getBoundsInScreen(android.graphics.Rect!);
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getChild(int);
+    method public int getChildCount();
+    method public int getId();
+    method public int getLayer();
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getParent();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getRoot();
+    method public CharSequence! getTitle();
+    method public int getType();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat! obtain();
+    method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat! obtain(androidx.core.view.accessibility.AccessibilityWindowInfoCompat!);
+    method public void recycle();
+    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
+}
+
+package androidx.core.view.animation {
+
+  public final class PathInterpolatorCompat {
+    method public static android.view.animation.Interpolator! create(android.graphics.Path!);
+    method public static android.view.animation.Interpolator! create(float, float);
+    method public static android.view.animation.Interpolator! create(float, float, float, float);
+  }
+
+}
+
+package androidx.core.view.inputmethod {
+
+  public final class EditorInfoCompat {
+    ctor @Deprecated public EditorInfoCompat();
+    method public static String![] getContentMimeTypes(android.view.inputmethod.EditorInfo!);
+    method public static CharSequence? getInitialSelectedText(android.view.inputmethod.EditorInfo, int);
+    method public static CharSequence? getInitialTextAfterCursor(android.view.inputmethod.EditorInfo, int, int);
+    method public static CharSequence? getInitialTextBeforeCursor(android.view.inputmethod.EditorInfo, int, int);
+    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, String![]?);
+    method public static void setInitialSurroundingSubText(android.view.inputmethod.EditorInfo, CharSequence, int);
+    method public static void setInitialSurroundingText(android.view.inputmethod.EditorInfo, CharSequence);
+    field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+    field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+  }
+
+  public final class InputConnectionCompat {
+    ctor @Deprecated public InputConnectionCompat();
+    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle?);
+    method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+    field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
+  }
+
+  public static interface InputConnectionCompat.OnCommitContentListener {
+    method public boolean onCommitContent(androidx.core.view.inputmethod.InputContentInfoCompat!, int, android.os.Bundle!);
+  }
+
+  public final class InputContentInfoCompat {
+    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri?);
+    method public android.net.Uri getContentUri();
+    method public android.content.ClipDescription getDescription();
+    method public android.net.Uri? getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
+    method public Object? unwrap();
+    method public static androidx.core.view.inputmethod.InputContentInfoCompat? wrap(Object?);
+  }
+
+}
+
+package androidx.core.widget {
+
+  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+    ctor public AutoScrollHelper(android.view.View);
+    method public abstract boolean canTargetScrollHorizontally(int);
+    method public abstract boolean canTargetScrollVertically(int);
+    method public boolean isEnabled();
+    method public boolean isExclusive();
+    method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+    method public abstract void scrollTargetBy(int, int);
+    method public androidx.core.widget.AutoScrollHelper setActivationDelay(int);
+    method public androidx.core.widget.AutoScrollHelper setEdgeType(int);
+    method public androidx.core.widget.AutoScrollHelper! setEnabled(boolean);
+    method public androidx.core.widget.AutoScrollHelper! setExclusive(boolean);
+    method public androidx.core.widget.AutoScrollHelper setMaximumEdges(float, float);
+    method public androidx.core.widget.AutoScrollHelper setMaximumVelocity(float, float);
+    method public androidx.core.widget.AutoScrollHelper setMinimumVelocity(float, float);
+    method public androidx.core.widget.AutoScrollHelper setRampDownDuration(int);
+    method public androidx.core.widget.AutoScrollHelper setRampUpDuration(int);
+    method public androidx.core.widget.AutoScrollHelper setRelativeEdges(float, float);
+    method public androidx.core.widget.AutoScrollHelper setRelativeVelocity(float, float);
+    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+    field public static final float NO_MAX = 3.4028235E38f;
+    field public static final float NO_MIN = 0.0f;
+    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+  }
+
+  public final class CompoundButtonCompat {
+    method public static android.graphics.drawable.Drawable? getButtonDrawable(android.widget.CompoundButton);
+    method public static android.content.res.ColorStateList? getButtonTintList(android.widget.CompoundButton);
+    method public static android.graphics.PorterDuff.Mode? getButtonTintMode(android.widget.CompoundButton);
+    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList?);
+    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode?);
+  }
+
+  public class ContentLoadingProgressBar extends android.widget.ProgressBar {
+    ctor public ContentLoadingProgressBar(android.content.Context);
+    ctor public ContentLoadingProgressBar(android.content.Context, android.util.AttributeSet?);
+    method public void hide();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void show();
+  }
+
+  public final class EdgeEffectCompat {
+    ctor @Deprecated public EdgeEffectCompat(android.content.Context!);
+    method @Deprecated public boolean draw(android.graphics.Canvas!);
+    method @Deprecated public void finish();
+    method @Deprecated public boolean isFinished();
+    method @Deprecated public boolean onAbsorb(int);
+    method @Deprecated public boolean onPull(float);
+    method @Deprecated public boolean onPull(float, float);
+    method public static void onPull(android.widget.EdgeEffect, float, float);
+    method @Deprecated public boolean onRelease();
+    method @Deprecated public void setSize(int, int);
+  }
+
+  public class ImageViewCompat {
+    method public static android.content.res.ColorStateList? getImageTintList(android.widget.ImageView);
+    method public static android.graphics.PorterDuff.Mode? getImageTintMode(android.widget.ImageView);
+    method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList?);
+    method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode?);
+  }
+
+  public final class ListPopupWindowCompat {
+    method @Deprecated public static android.view.View.OnTouchListener! createDragToOpenListener(Object!, android.view.View!);
+    method public static android.view.View.OnTouchListener? createDragToOpenListener(android.widget.ListPopupWindow, android.view.View);
+  }
+
+  public class ListViewAutoScrollHelper extends androidx.core.widget.AutoScrollHelper {
+    ctor public ListViewAutoScrollHelper(android.widget.ListView);
+    method public boolean canTargetScrollHorizontally(int);
+    method public boolean canTargetScrollVertically(int);
+    method public void scrollTargetBy(int, int);
+  }
+
+  public final class ListViewCompat {
+    method public static boolean canScrollList(android.widget.ListView, int);
+    method public static void scrollListBy(android.widget.ListView, int);
+  }
+
+  public class NestedScrollView extends android.widget.FrameLayout implements androidx.core.view.NestedScrollingChild3 androidx.core.view.NestedScrollingParent3 androidx.core.view.ScrollingView {
+    ctor public NestedScrollView(android.content.Context);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?, int);
+    method public boolean arrowScroll(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollExtent();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollOffset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollRange();
+    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollExtent();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollOffset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollRange();
+    method public boolean dispatchNestedPreScroll(int, int, int[]!, int[]!, int);
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]!, int);
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fling(int);
+    method public boolean fullScroll(int);
+    method public int getMaxScrollAmount();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isFillViewport();
+    method public boolean isSmoothScrollingEnabled();
+    method public void onAttachedToWindow();
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+    method public boolean pageScroll(int);
+    method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(androidx.core.widget.NestedScrollView.OnScrollChangeListener?);
+    method public void setSmoothScrollingEnabled(boolean);
+    method public final void smoothScrollBy(int, int);
+    method public final void smoothScrollBy(int, int, int);
+    method public final void smoothScrollTo(int, int);
+    method public final void smoothScrollTo(int, int, int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public static interface NestedScrollView.OnScrollChangeListener {
+    method public void onScrollChange(androidx.core.widget.NestedScrollView!, int, int, int, int);
+  }
+
+  public final class PopupMenuCompat {
+    method public static android.view.View.OnTouchListener? getDragToOpenListener(Object);
+  }
+
+  public final class PopupWindowCompat {
+    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+    method public static int getWindowLayoutType(android.widget.PopupWindow);
+    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+  }
+
+  @Deprecated public final class ScrollerCompat {
+    method @Deprecated public void abortAnimation();
+    method @Deprecated public boolean computeScrollOffset();
+    method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!);
+    method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!, android.view.animation.Interpolator!);
+    method @Deprecated public void fling(int, int, int, int, int, int, int, int);
+    method @Deprecated public void fling(int, int, int, int, int, int, int, int, int, int);
+    method @Deprecated public float getCurrVelocity();
+    method @Deprecated public int getCurrX();
+    method @Deprecated public int getCurrY();
+    method @Deprecated public int getFinalX();
+    method @Deprecated public int getFinalY();
+    method @Deprecated public boolean isFinished();
+    method @Deprecated public boolean isOverScrolled();
+    method @Deprecated public void notifyHorizontalEdgeReached(int, int, int);
+    method @Deprecated public void notifyVerticalEdgeReached(int, int, int);
+    method @Deprecated public boolean springBack(int, int, int, int, int, int);
+    method @Deprecated public void startScroll(int, int, int, int);
+    method @Deprecated public void startScroll(int, int, int, int, int);
+  }
+
+  public final class TextViewCompat {
+    method public static int getAutoSizeMaxTextSize(android.widget.TextView);
+    method public static int getAutoSizeMinTextSize(android.widget.TextView);
+    method public static int getAutoSizeStepGranularity(android.widget.TextView);
+    method public static int[] getAutoSizeTextAvailableSizes(android.widget.TextView);
+    method public static int getAutoSizeTextType(android.widget.TextView);
+    method public static android.content.res.ColorStateList? getCompoundDrawableTintList(android.widget.TextView);
+    method public static android.graphics.PorterDuff.Mode? getCompoundDrawableTintMode(android.widget.TextView);
+    method public static android.graphics.drawable.Drawable![] getCompoundDrawablesRelative(android.widget.TextView);
+    method public static int getFirstBaselineToTopHeight(android.widget.TextView);
+    method public static int getLastBaselineToBottomHeight(android.widget.TextView);
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
+    method public static androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParams(android.widget.TextView);
+    method public static void setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeWithDefaults(android.widget.TextView, int);
+    method public static void setCompoundDrawableTintList(android.widget.TextView, android.content.res.ColorStateList?);
+    method public static void setCompoundDrawableTintMode(android.widget.TextView, android.graphics.PorterDuff.Mode?);
+    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, @DrawableRes int, @DrawableRes int, @DrawableRes int, @DrawableRes int);
+    method public static void setCustomSelectionActionModeCallback(android.widget.TextView, android.view.ActionMode.Callback);
+    method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
+    method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
+    method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
+  }
+
+  public interface TintableCompoundButton {
+    method public android.content.res.ColorStateList? getSupportButtonTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public interface TintableCompoundDrawablesView {
+    method public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+}
+
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index 8c72f00..dc43509 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -842,8 +842,8 @@
   }
 
   public final class ShareCompat {
-    method public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
-    method public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
     method public static android.content.ComponentName? getCallingActivity(android.app.Activity);
     method public static String? getCallingPackage(android.app.Activity);
     field public static final String EXTRA_CALLING_ACTIVITY = "androidx.core.app.EXTRA_CALLING_ACTIVITY";
@@ -1037,6 +1037,8 @@
 
   public final class PackageInfoCompat {
     method public static long getLongVersionCode(android.content.pm.PackageInfo);
+    method public static java.util.List<android.content.pm.Signature!> getSignatures(android.content.pm.PackageManager, String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static boolean hasSignatures(android.content.pm.PackageManager, String, @Size(min=1) java.util.Map<byte[]!,java.lang.Integer!>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
   }
 
   public final class PermissionInfoCompat {
@@ -1522,7 +1524,7 @@
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O_MR1) public static boolean isAtLeastOMR1();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.P) public static boolean isAtLeastP();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
-    method @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
     method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
   }
 
@@ -1910,6 +1912,31 @@
     method public void onActionProviderVisibilityChanged(boolean);
   }
 
+  public final class ContentInfoCompat {
+    method public android.content.ClipData getClip();
+    method public android.os.Bundle? getExtras();
+    method public int getFlags();
+    method public android.net.Uri? getLinkUri();
+    method public int getSource();
+    method public android.util.Pair<androidx.core.view.ContentInfoCompat!,androidx.core.view.ContentInfoCompat!> partition(androidx.core.util.Predicate<android.content.ClipData.Item!>);
+    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+  }
+
+  public static final class ContentInfoCompat.Builder {
+    ctor public ContentInfoCompat.Builder(androidx.core.view.ContentInfoCompat);
+    ctor public ContentInfoCompat.Builder(android.content.ClipData, int);
+    method public androidx.core.view.ContentInfoCompat build();
+    method public androidx.core.view.ContentInfoCompat.Builder setClip(android.content.ClipData);
+    method public androidx.core.view.ContentInfoCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.view.ContentInfoCompat.Builder setFlags(int);
+    method public androidx.core.view.ContentInfoCompat.Builder setLinkUri(android.net.Uri?);
+    method public androidx.core.view.ContentInfoCompat.Builder setSource(int);
+  }
+
   public final class DisplayCompat {
     method public static androidx.core.view.DisplayCompat.ModeCompat![] getSupportedModes(android.content.Context, android.view.Display);
   }
@@ -2206,6 +2233,14 @@
     method public androidx.core.view.WindowInsetsCompat! onApplyWindowInsets(android.view.View!, androidx.core.view.WindowInsetsCompat!);
   }
 
+  public interface OnReceiveContentListener {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+  }
+
+  public interface OnReceiveContentViewBehavior {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+  }
+
   public final class OneShotPreDrawListener implements android.view.View.OnAttachStateChangeListener android.view.ViewTreeObserver.OnPreDrawListener {
     method public static androidx.core.view.OneShotPreDrawListener add(android.view.View, Runnable);
     method public boolean onPreDraw();
@@ -2317,6 +2352,7 @@
     method public static int getMinimumHeight(android.view.View);
     method public static int getMinimumWidth(android.view.View);
     method public static int getNextClusterForwardId(android.view.View);
+    method public static String![]? getOnReceiveContentMimeTypes(android.view.View);
     method @Deprecated public static int getOverScrollMode(android.view.View!);
     method @Px public static int getPaddingEnd(android.view.View);
     method @Px public static int getPaddingStart(android.view.View);
@@ -2370,6 +2406,7 @@
     method public static void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
     method @Deprecated public static void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
     method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle!);
+    method public static androidx.core.view.ContentInfoCompat? performReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
     method public static void postInvalidateOnAnimation(android.view.View);
     method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
     method public static void postOnAnimation(android.view.View, Runnable!);
@@ -2408,6 +2445,7 @@
     method public static void setNestedScrollingEnabled(android.view.View, boolean);
     method public static void setNextClusterForwardId(android.view.View, int);
     method public static void setOnApplyWindowInsetsListener(android.view.View, androidx.core.view.OnApplyWindowInsetsListener?);
+    method public static void setOnReceiveContentListener(android.view.View, String![]?, androidx.core.view.OnReceiveContentListener?);
     method @Deprecated public static void setOverScrollMode(android.view.View!, int);
     method public static void setPaddingRelative(android.view.View, @Px int, @Px int, @Px int, @Px int);
     method @Deprecated public static void setPivotX(android.view.View!, float);
@@ -2429,6 +2467,7 @@
     method @Deprecated public static void setTranslationX(android.view.View!, float);
     method @Deprecated public static void setTranslationY(android.view.View!, float);
     method public static void setTranslationZ(android.view.View, float);
+    method public static void setWindowInsetsAnimationCallback(android.view.View, androidx.core.view.WindowInsetsAnimationCompat.Callback?);
     method @Deprecated public static void setX(android.view.View!, float);
     method @Deprecated public static void setY(android.view.View!, float);
     method public static void setZ(android.view.View, float);
@@ -2581,6 +2620,58 @@
     field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
   }
 
+  public final class WindowInsetsAnimationCompat {
+    ctor public WindowInsetsAnimationCompat(int, android.view.animation.Interpolator?, long);
+    method @FloatRange(from=0.0f, to=1.0f) public float getAlpha();
+    method public long getDurationMillis();
+    method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+    method public float getInterpolatedFraction();
+    method public android.view.animation.Interpolator? getInterpolator();
+    method public int getTypeMask();
+    method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float);
+    method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public static final class WindowInsetsAnimationCompat.Bounds {
+    ctor public WindowInsetsAnimationCompat.Bounds(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    ctor @RequiresApi(30) public WindowInsetsAnimationCompat.Bounds(android.view.WindowInsetsAnimation.Bounds);
+    method public androidx.core.graphics.Insets getLowerBound();
+    method public androidx.core.graphics.Insets getUpperBound();
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds inset(androidx.core.graphics.Insets);
+    method @RequiresApi(30) public android.view.WindowInsetsAnimation.Bounds toPlatformBounds();
+  }
+
+  public abstract static class WindowInsetsAnimationCompat.Callback {
+    ctor public WindowInsetsAnimationCompat.Callback(int);
+    method public final int getDispatchMode();
+    method public void onEnd(androidx.core.view.WindowInsetsAnimationCompat);
+    method public void onPrepare(androidx.core.view.WindowInsetsAnimationCompat);
+    method public abstract androidx.core.view.WindowInsetsCompat onProgress(androidx.core.view.WindowInsetsCompat, java.util.List<androidx.core.view.WindowInsetsAnimationCompat!>);
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds onStart(androidx.core.view.WindowInsetsAnimationCompat, androidx.core.view.WindowInsetsAnimationCompat.Bounds);
+    field public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1; // 0x1
+    field public static final int DISPATCH_MODE_STOP = 0; // 0x0
+  }
+
+  public interface WindowInsetsAnimationControlListenerCompat {
+    method public void onCancelled(androidx.core.view.WindowInsetsAnimationControllerCompat?);
+    method public void onFinished(androidx.core.view.WindowInsetsAnimationControllerCompat);
+    method public void onReady(androidx.core.view.WindowInsetsAnimationControllerCompat, int);
+  }
+
+  public final class WindowInsetsAnimationControllerCompat {
+    method public void finish(boolean);
+    method public float getCurrentAlpha();
+    method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+    method public androidx.core.graphics.Insets getCurrentInsets();
+    method public androidx.core.graphics.Insets getHiddenStateInsets();
+    method public androidx.core.graphics.Insets getShownStateInsets();
+    method public int getTypes();
+    method public boolean isCancelled();
+    method public boolean isFinished();
+    method public boolean isReady();
+    method public void setInsetsAndAlpha(androidx.core.graphics.Insets?, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+  }
+
   public class WindowInsetsCompat {
     ctor public WindowInsetsCompat(androidx.core.view.WindowInsetsCompat?);
     method @Deprecated public androidx.core.view.WindowInsetsCompat consumeDisplayCutout();
@@ -2647,10 +2738,13 @@
 
   public final class WindowInsetsControllerCompat {
     ctor public WindowInsetsControllerCompat(android.view.Window, android.view.View);
+    method public void addOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void controlWindowInsetsAnimation(int, long, android.view.animation.Interpolator?, android.os.CancellationSignal?, androidx.core.view.WindowInsetsAnimationControlListenerCompat);
     method public int getSystemBarsBehavior();
     method public void hide(int);
     method public boolean isAppearanceLightNavigationBars();
     method public boolean isAppearanceLightStatusBars();
+    method public void removeOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
     method public void setAppearanceLightNavigationBars(boolean);
     method public void setAppearanceLightStatusBars(boolean);
     method public void setSystemBarsBehavior(int);
@@ -2661,6 +2755,10 @@
     field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
   }
 
+  public static interface WindowInsetsControllerCompat.OnControllableInsetsChangedListener {
+    method public void onControllableInsetsChanged(androidx.core.view.WindowInsetsControllerCompat, int);
+  }
+
 }
 
 package androidx.core.view.accessibility {
@@ -3333,15 +3431,6 @@
     method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
   }
 
-  public abstract class RichContentReceiverCompat<T extends android.view.View> {
-    ctor public RichContentReceiverCompat();
-    method public abstract java.util.Set<java.lang.String!> getSupportedMimeTypes();
-    method public abstract boolean onReceive(T, android.content.ClipData, int, int);
-    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
-    field public static final int SOURCE_CLIPBOARD = 0; // 0x0
-    field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
-  }
-
   @Deprecated public final class ScrollerCompat {
     method @Deprecated public void abortAnimation();
     method @Deprecated public boolean computeScrollOffset();
@@ -3396,12 +3485,6 @@
     field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
   }
 
-  public abstract class TextViewRichContentReceiverCompat extends androidx.core.widget.RichContentReceiverCompat<android.widget.TextView> {
-    ctor public TextViewRichContentReceiverCompat();
-    method public java.util.Set<java.lang.String!> getSupportedMimeTypes();
-    method public boolean onReceive(android.widget.TextView, android.content.ClipData, int, int);
-  }
-
   public interface TintableCompoundButton {
     method public android.content.res.ColorStateList? getSupportButtonTintList();
     method public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
diff --git a/core/core/api/res-1.5.0-beta01.txt b/core/core/api/res-1.5.0-beta01.txt
new file mode 100644
index 0000000..a609e0a
--- /dev/null
+++ b/core/core/api/res-1.5.0-beta01.txt
@@ -0,0 +1,17 @@
+attr alpha
+attr font
+attr fontProviderAuthority
+attr fontProviderCerts
+attr fontProviderFetchStrategy
+attr fontProviderFetchTimeout
+attr fontProviderPackage
+attr fontProviderQuery
+attr fontStyle
+attr fontVariationSettings
+attr fontWeight
+attr ttcIndex
+style TextAppearance_Compat_Notification
+style TextAppearance_Compat_Notification_Info
+style TextAppearance_Compat_Notification_Line2
+style TextAppearance_Compat_Notification_Time
+style TextAppearance_Compat_Notification_Title
diff --git a/core/core/api/restricted_1.5.0-beta01.txt b/core/core/api/restricted_1.5.0-beta01.txt
new file mode 100644
index 0000000..6e89c98
--- /dev/null
+++ b/core/core/api/restricted_1.5.0-beta01.txt
@@ -0,0 +1,3971 @@
+// Signature format: 4.0
+package android.support.v4.os {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ResultReceiver implements android.os.Parcelable {
+    ctor public ResultReceiver(android.os.Handler!);
+    method public int describeContents();
+    method protected void onReceiveResult(int, android.os.Bundle!);
+    method public void send(int, android.os.Bundle!);
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.os.ResultReceiver!>! CREATOR;
+  }
+
+}
+
+package androidx.core.accessibilityservice {
+
+  public final class AccessibilityServiceInfoCompat {
+    method public static String capabilityToString(int);
+    method public static String feedbackTypeToString(int);
+    method public static String? flagToString(int);
+    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static String? loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+  }
+
+}
+
+package androidx.core.app {
+
+  public class ActivityCompat extends androidx.core.content.ContextCompat {
+    ctor protected ActivityCompat();
+    method public static void finishAffinity(android.app.Activity);
+    method public static void finishAfterTransition(android.app.Activity);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.app.ActivityCompat.PermissionCompatDelegate! getPermissionCompatDelegate();
+    method public static android.net.Uri? getReferrer(android.app.Activity);
+    method @Deprecated public static boolean invalidateOptionsMenu(android.app.Activity!);
+    method public static void postponeEnterTransition(android.app.Activity);
+    method public static void recreate(android.app.Activity);
+    method public static androidx.core.view.DragAndDropPermissionsCompat? requestDragAndDropPermissions(android.app.Activity!, android.view.DragEvent!);
+    method public static void requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+    method public static <T extends android.view.View> T requireViewById(android.app.Activity, @IdRes int);
+    method public static void setEnterSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+    method public static void setExitSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+    method public static void setLocusContext(android.app.Activity, androidx.core.content.LocusIdCompat?, android.os.Bundle?);
+    method public static void setPermissionCompatDelegate(androidx.core.app.ActivityCompat.PermissionCompatDelegate?);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, String);
+    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle?);
+    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public static void startPostponedEnterTransition(android.app.Activity);
+  }
+
+  public static interface ActivityCompat.OnRequestPermissionsResultCallback {
+    method public void onRequestPermissionsResult(int, String![], int[]);
+  }
+
+  public static interface ActivityCompat.PermissionCompatDelegate {
+    method public boolean onActivityResult(android.app.Activity, @IntRange(from=0) int, int, android.content.Intent?);
+    method public boolean requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface ActivityCompat.RequestPermissionsRequestCodeValidator {
+    method public void validateRequestPermissionsRequestCode(int);
+  }
+
+  public final class ActivityManagerCompat {
+    method public static boolean isLowRamDevice(android.app.ActivityManager);
+  }
+
+  public class ActivityOptionsCompat {
+    ctor protected ActivityOptionsCompat();
+    method public android.graphics.Rect? getLaunchBounds();
+    method public static androidx.core.app.ActivityOptionsCompat makeBasic();
+    method public static androidx.core.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, String);
+    method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, androidx.core.util.Pair<android.view.View!,java.lang.String!>!...);
+    method public static androidx.core.app.ActivityOptionsCompat makeTaskLaunchBehind();
+    method public static androidx.core.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+    method public void requestUsageTimeReport(android.app.PendingIntent);
+    method public androidx.core.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect?);
+    method public android.os.Bundle? toBundle();
+    method public void update(androidx.core.app.ActivityOptionsCompat);
+    field public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+    field public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+  }
+
+  public final class AlarmManagerCompat {
+    method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+    method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+  }
+
+  @RequiresApi(28) public class AppComponentFactory extends android.app.AppComponentFactory {
+    ctor public AppComponentFactory();
+    method public final android.app.Activity instantiateActivity(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Activity instantiateActivityCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.app.Application instantiateApplication(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Application instantiateApplicationCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.content.ContentProvider instantiateProvider(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.content.ContentProvider instantiateProviderCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.content.BroadcastReceiver instantiateReceiver(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.content.BroadcastReceiver instantiateReceiverCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.app.Service instantiateService(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Service instantiateServiceCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+  }
+
+  public class AppLaunchChecker {
+    ctor @Deprecated public AppLaunchChecker();
+    method public static boolean hasStartedFromLauncher(android.content.Context);
+    method public static void onActivityCreate(android.app.Activity);
+  }
+
+  public final class AppOpsManagerCompat {
+    method public static int noteOp(android.content.Context, String, int, String);
+    method public static int noteOpNoThrow(android.content.Context, String, int, String);
+    method public static int noteProxyOp(android.content.Context, String, String);
+    method public static int noteProxyOpNoThrow(android.content.Context, String, String);
+    method public static String? permissionToOp(String);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
+    field public static final int MODE_ERRORED = 2; // 0x2
+    field public static final int MODE_IGNORED = 1; // 0x1
+  }
+
+  public final class BundleCompat {
+    method public static android.os.IBinder? getBinder(android.os.Bundle, String?);
+    method public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ComponentActivity extends android.app.Activity implements androidx.core.view.KeyEventDispatcher.Component androidx.lifecycle.LifecycleOwner {
+    ctor public ComponentActivity();
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void putExtraData(androidx.core.app.ComponentActivity.ExtraData!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean superDispatchKeyEvent(android.view.KeyEvent!);
+  }
+
+  @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ComponentActivity.ExtraData {
+    ctor @Deprecated public ComponentActivity.ExtraData();
+  }
+
+  @RequiresApi(api=28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CoreComponentFactory extends android.app.AppComponentFactory {
+    ctor public CoreComponentFactory();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface CoreComponentFactory.CompatWrapped {
+    method public Object! getWrapper();
+  }
+
+  public class DialogCompat {
+    method public static android.view.View requireViewById(android.app.Dialog, int);
+  }
+
+  public class FrameMetricsAggregator {
+    ctor public FrameMetricsAggregator();
+    ctor public FrameMetricsAggregator(@androidx.core.app.FrameMetricsAggregator.MetricType int);
+    method public void add(android.app.Activity);
+    method public android.util.SparseIntArray![]? getMetrics();
+    method public android.util.SparseIntArray![]? remove(android.app.Activity);
+    method public android.util.SparseIntArray![]? reset();
+    method public android.util.SparseIntArray![]? stop();
+    field public static final int ANIMATION_DURATION = 256; // 0x100
+    field public static final int ANIMATION_INDEX = 8; // 0x8
+    field public static final int COMMAND_DURATION = 32; // 0x20
+    field public static final int COMMAND_INDEX = 5; // 0x5
+    field public static final int DELAY_DURATION = 128; // 0x80
+    field public static final int DELAY_INDEX = 7; // 0x7
+    field public static final int DRAW_DURATION = 8; // 0x8
+    field public static final int DRAW_INDEX = 3; // 0x3
+    field public static final int EVERY_DURATION = 511; // 0x1ff
+    field public static final int INPUT_DURATION = 2; // 0x2
+    field public static final int INPUT_INDEX = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 4; // 0x4
+    field public static final int LAYOUT_MEASURE_INDEX = 2; // 0x2
+    field public static final int SWAP_DURATION = 64; // 0x40
+    field public static final int SWAP_INDEX = 6; // 0x6
+    field public static final int SYNC_DURATION = 16; // 0x10
+    field public static final int SYNC_INDEX = 4; // 0x4
+    field public static final int TOTAL_DURATION = 1; // 0x1
+    field public static final int TOTAL_INDEX = 0; // 0x0
+  }
+
+  @IntDef(flag=true, value={androidx.core.app.FrameMetricsAggregator.TOTAL_DURATION, androidx.core.app.FrameMetricsAggregator.INPUT_DURATION, androidx.core.app.FrameMetricsAggregator.LAYOUT_MEASURE_DURATION, androidx.core.app.FrameMetricsAggregator.DRAW_DURATION, androidx.core.app.FrameMetricsAggregator.SYNC_DURATION, androidx.core.app.FrameMetricsAggregator.COMMAND_DURATION, androidx.core.app.FrameMetricsAggregator.SWAP_DURATION, androidx.core.app.FrameMetricsAggregator.DELAY_DURATION, androidx.core.app.FrameMetricsAggregator.ANIMATION_DURATION, androidx.core.app.FrameMetricsAggregator.EVERY_DURATION}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface FrameMetricsAggregator.MetricType {
+  }
+
+  public abstract class JobIntentService extends android.app.Service {
+    ctor public JobIntentService();
+    method public static void enqueueWork(android.content.Context, Class<?>, int, android.content.Intent);
+    method public static void enqueueWork(android.content.Context, android.content.ComponentName, int, android.content.Intent);
+    method public boolean isStopped();
+    method public android.os.IBinder! onBind(android.content.Intent);
+    method protected abstract void onHandleWork(android.content.Intent);
+    method public boolean onStopCurrentWork();
+    method public void setInterruptIfStopped(boolean);
+  }
+
+  public final class NavUtils {
+    method public static android.content.Intent? getParentActivityIntent(android.app.Activity);
+    method public static android.content.Intent? getParentActivityIntent(android.content.Context, Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static android.content.Intent? getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static String? getParentActivityName(android.app.Activity);
+    method public static String? getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void navigateUpFromSameTask(android.app.Activity);
+    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+    field public static final String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface NotificationBuilderWithBuilderAccessor {
+    method public android.app.Notification.Builder! getBuilder();
+  }
+
+  public class NotificationChannelCompat {
+    method public boolean canBubble();
+    method public boolean canBypassDnd();
+    method public boolean canShowBadge();
+    method public android.media.AudioAttributes? getAudioAttributes();
+    method public String? getConversationId();
+    method public String? getDescription();
+    method public String? getGroup();
+    method public String getId();
+    method public int getImportance();
+    method public int getLightColor();
+    method @androidx.core.app.NotificationCompat.NotificationVisibility public int getLockscreenVisibility();
+    method public CharSequence? getName();
+    method public String? getParentChannelId();
+    method public android.net.Uri? getSound();
+    method public long[]? getVibrationPattern();
+    method public boolean isImportantConversation();
+    method public boolean shouldShowLights();
+    method public boolean shouldVibrate();
+    method public androidx.core.app.NotificationChannelCompat.Builder toBuilder();
+    field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
+  }
+
+  public static class NotificationChannelCompat.Builder {
+    ctor public NotificationChannelCompat.Builder(String, int);
+    method public androidx.core.app.NotificationChannelCompat build();
+    method public androidx.core.app.NotificationChannelCompat.Builder setConversationId(String, String);
+    method public androidx.core.app.NotificationChannelCompat.Builder setDescription(String?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setGroup(String?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setImportance(int);
+    method public androidx.core.app.NotificationChannelCompat.Builder setLightColor(int);
+    method public androidx.core.app.NotificationChannelCompat.Builder setLightsEnabled(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setName(CharSequence?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setShowBadge(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setSound(android.net.Uri?, android.media.AudioAttributes?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setVibrationEnabled(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setVibrationPattern(long[]?);
+  }
+
+  public class NotificationChannelGroupCompat {
+    method public java.util.List<androidx.core.app.NotificationChannelCompat!> getChannels();
+    method public String? getDescription();
+    method public String getId();
+    method public CharSequence? getName();
+    method public boolean isBlocked();
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder toBuilder();
+  }
+
+  public static class NotificationChannelGroupCompat.Builder {
+    ctor public NotificationChannelGroupCompat.Builder(String);
+    method public androidx.core.app.NotificationChannelGroupCompat build();
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder setDescription(String?);
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder setName(CharSequence?);
+  }
+
+  public class NotificationCompat {
+    ctor @Deprecated public NotificationCompat();
+    method public static androidx.core.app.NotificationCompat.Action? getAction(android.app.Notification, int);
+    method public static int getActionCount(android.app.Notification);
+    method public static boolean getAllowSystemGeneratedContextualActions(android.app.Notification);
+    method public static boolean getAutoCancel(android.app.Notification);
+    method public static int getBadgeIconType(android.app.Notification);
+    method public static androidx.core.app.NotificationCompat.BubbleMetadata? getBubbleMetadata(android.app.Notification);
+    method public static String? getCategory(android.app.Notification);
+    method public static String? getChannelId(android.app.Notification);
+    method public static int getColor(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentInfo(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentText(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentTitle(android.app.Notification);
+    method public static android.os.Bundle? getExtras(android.app.Notification);
+    method public static String? getGroup(android.app.Notification);
+    method @androidx.core.app.NotificationCompat.GroupAlertBehavior public static int getGroupAlertBehavior(android.app.Notification);
+    method @RequiresApi(21) public static java.util.List<androidx.core.app.NotificationCompat.Action!> getInvisibleActions(android.app.Notification);
+    method public static boolean getLocalOnly(android.app.Notification);
+    method public static androidx.core.content.LocusIdCompat? getLocusId(android.app.Notification);
+    method public static boolean getOngoing(android.app.Notification);
+    method public static boolean getOnlyAlertOnce(android.app.Notification);
+    method public static java.util.List<androidx.core.app.Person!> getPeople(android.app.Notification);
+    method public static android.app.Notification? getPublicVersion(android.app.Notification);
+    method public static CharSequence? getSettingsText(android.app.Notification);
+    method public static String? getShortcutId(android.app.Notification);
+    method @RequiresApi(19) public static boolean getShowWhen(android.app.Notification);
+    method public static String? getSortKey(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getSubText(android.app.Notification);
+    method public static long getTimeoutAfter(android.app.Notification);
+    method @RequiresApi(19) public static boolean getUsesChronometer(android.app.Notification);
+    method @androidx.core.app.NotificationCompat.NotificationVisibility public static int getVisibility(android.app.Notification);
+    method public static boolean isGroupSummary(android.app.Notification);
+    field public static final int BADGE_ICON_LARGE = 2; // 0x2
+    field public static final int BADGE_ICON_NONE = 0; // 0x0
+    field public static final int BADGE_ICON_SMALL = 1; // 0x1
+    field public static final String CATEGORY_ALARM = "alarm";
+    field public static final String CATEGORY_CALL = "call";
+    field public static final String CATEGORY_EMAIL = "email";
+    field public static final String CATEGORY_ERROR = "err";
+    field public static final String CATEGORY_EVENT = "event";
+    field public static final String CATEGORY_LOCATION_SHARING = "location_sharing";
+    field public static final String CATEGORY_MESSAGE = "msg";
+    field public static final String CATEGORY_MISSED_CALL = "missed_call";
+    field public static final String CATEGORY_NAVIGATION = "navigation";
+    field public static final String CATEGORY_PROGRESS = "progress";
+    field public static final String CATEGORY_PROMO = "promo";
+    field public static final String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final String CATEGORY_REMINDER = "reminder";
+    field public static final String CATEGORY_SERVICE = "service";
+    field public static final String CATEGORY_SOCIAL = "social";
+    field public static final String CATEGORY_STATUS = "status";
+    field public static final String CATEGORY_STOPWATCH = "stopwatch";
+    field public static final String CATEGORY_SYSTEM = "sys";
+    field public static final String CATEGORY_TRANSPORT = "transport";
+    field public static final String CATEGORY_WORKOUT = "workout";
+    field @ColorInt public static final int COLOR_DEFAULT = 0; // 0x0
+    field public static final int DEFAULT_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_LIGHTS = 4; // 0x4
+    field public static final int DEFAULT_SOUND = 1; // 0x1
+    field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+    field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
+    field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
+    field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
+    field public static final String EXTRA_COLORIZED = "android.colorized";
+    field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+    field public static final String EXTRA_COMPAT_TEMPLATE = "androidx.core.app.extra.COMPAT_TEMPLATE";
+    field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final String EXTRA_HIDDEN_CONVERSATION_TITLE = "android.hiddenConversationTitle";
+    field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
+    field public static final String EXTRA_INFO_TEXT = "android.infoText";
+    field public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
+    field public static final String EXTRA_LARGE_ICON = "android.largeIcon";
+    field public static final String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+    field public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final String EXTRA_MESSAGES = "android.messages";
+    field public static final String EXTRA_MESSAGING_STYLE_USER = "android.messagingStyleUser";
+    field public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+    field public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
+    field @Deprecated public static final String EXTRA_PEOPLE = "android.people";
+    field public static final String EXTRA_PEOPLE_LIST = "android.people.list";
+    field public static final String EXTRA_PICTURE = "android.picture";
+    field public static final String EXTRA_PROGRESS = "android.progress";
+    field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    field public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    field public static final String EXTRA_SHOW_WHEN = "android.showWhen";
+    field public static final String EXTRA_SMALL_ICON = "android.icon";
+    field public static final String EXTRA_SUB_TEXT = "android.subText";
+    field public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
+    field public static final String EXTRA_TEMPLATE = "android.template";
+    field public static final String EXTRA_TEXT = "android.text";
+    field public static final String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final String EXTRA_TITLE = "android.title";
+    field public static final String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+    field public static final int FLAG_BUBBLE = 4096; // 0x1000
+    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+    field @Deprecated public static final int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final int FLAG_INSISTENT = 4; // 0x4
+    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+    field public static final int FLAG_NO_CLEAR = 32; // 0x20
+    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final int GROUP_ALERT_ALL = 0; // 0x0
+    field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
+    field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
+    field public static final String GROUP_KEY_SILENT = "silent";
+    field public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES";
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+  }
+
+  public static class NotificationCompat.Action {
+    ctor public NotificationCompat.Action(int, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+    method public android.app.PendingIntent? getActionIntent();
+    method public boolean getAllowGeneratedReplies();
+    method public androidx.core.app.RemoteInput![]? getDataOnlyRemoteInputs();
+    method public android.os.Bundle getExtras();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
+    method public androidx.core.app.RemoteInput![]? getRemoteInputs();
+    method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
+    method public boolean getShowsUserInterface();
+    method public CharSequence? getTitle();
+    method public boolean isContextual();
+    field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
+    field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
+    field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
+    field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
+    field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
+    field public static final int SEMANTIC_ACTION_MUTE = 6; // 0x6
+    field public static final int SEMANTIC_ACTION_NONE = 0; // 0x0
+    field public static final int SEMANTIC_ACTION_REPLY = 1; // 0x1
+    field public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9; // 0x9
+    field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
+    field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
+    field public android.app.PendingIntent! actionIntent;
+    field @Deprecated public int icon;
+    field public CharSequence! title;
+  }
+
+  public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action.Builder(int, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.Action.Builder addRemoteInput(androidx.core.app.RemoteInput?);
+    method public androidx.core.app.NotificationCompat.Action build();
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Extender);
+    method @RequiresApi(19) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.app.NotificationCompat.Action.Builder fromAndroidAction(android.app.Notification.Action);
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setContextual(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setSemanticAction(@androidx.core.app.NotificationCompat.Action.SemanticAction int);
+    method public androidx.core.app.NotificationCompat.Action.Builder setShowsUserInterface(boolean);
+  }
+
+  public static interface NotificationCompat.Action.Extender {
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_NONE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_REPLY, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_UNREAD, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_DELETE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_ARCHIVE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_UNMUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_UP, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_DOWN, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_CALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.Action.SemanticAction {
+  }
+
+  public static final class NotificationCompat.Action.WearableExtender implements androidx.core.app.NotificationCompat.Action.Extender {
+    ctor public NotificationCompat.Action.WearableExtender();
+    ctor public NotificationCompat.Action.WearableExtender(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender clone();
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+    method @Deprecated public CharSequence? getCancelLabel();
+    method @Deprecated public CharSequence? getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
+    method public boolean getHintLaunchesActivity();
+    method @Deprecated public CharSequence? getInProgressLabel();
+    method public boolean isAvailableOffline();
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setCancelLabel(CharSequence?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setConfirmLabel(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setInProgressLabel(CharSequence?);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.BADGE_ICON_NONE, androidx.core.app.NotificationCompat.BADGE_ICON_SMALL, androidx.core.app.NotificationCompat.BADGE_ICON_LARGE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.BadgeIconType {
+  }
+
+  public static class NotificationCompat.BigPictureStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigPictureStyle();
+    ctor public NotificationCompat.BigPictureStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle setSummaryText(CharSequence?);
+  }
+
+  public static class NotificationCompat.BigTextStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigTextStyle();
+    ctor public NotificationCompat.BigTextStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle bigText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle setSummaryText(CharSequence?);
+  }
+
+  public static final class NotificationCompat.BubbleMetadata {
+    method public static androidx.core.app.NotificationCompat.BubbleMetadata? fromPlatform(android.app.Notification.BubbleMetadata?);
+    method public boolean getAutoExpandBubble();
+    method public android.app.PendingIntent? getDeleteIntent();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getDesiredHeight();
+    method @DimenRes public int getDesiredHeightResId();
+    method public androidx.core.graphics.drawable.IconCompat? getIcon();
+    method public android.app.PendingIntent? getIntent();
+    method public String? getShortcutId();
+    method public boolean isNotificationSuppressed();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setFlags(int);
+    method public static android.app.Notification.BubbleMetadata? toPlatform(androidx.core.app.NotificationCompat.BubbleMetadata?);
+  }
+
+  public static final class NotificationCompat.BubbleMetadata.Builder {
+    ctor @Deprecated public NotificationCompat.BubbleMetadata.Builder();
+    ctor @RequiresApi(30) public NotificationCompat.BubbleMetadata.Builder(String);
+    ctor public NotificationCompat.BubbleMetadata.Builder(android.app.PendingIntent, androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata build();
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setAutoExpandBubble(boolean);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeight(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeightResId(@DimenRes int);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIcon(androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setSuppressNotification(boolean);
+  }
+
+  public static class NotificationCompat.Builder {
+    ctor @RequiresApi(19) public NotificationCompat.Builder(android.content.Context, android.app.Notification);
+    ctor public NotificationCompat.Builder(android.content.Context, String);
+    ctor @Deprecated public NotificationCompat.Builder(android.content.Context);
+    method public androidx.core.app.NotificationCompat.Builder addAction(int, CharSequence?, android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder addAction(androidx.core.app.NotificationCompat.Action?);
+    method public androidx.core.app.NotificationCompat.Builder addExtras(android.os.Bundle?);
+    method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(int, CharSequence?, android.app.PendingIntent?);
+    method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(androidx.core.app.NotificationCompat.Action?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder addPerson(String?);
+    method public androidx.core.app.NotificationCompat.Builder addPerson(androidx.core.app.Person?);
+    method public android.app.Notification build();
+    method public androidx.core.app.NotificationCompat.Builder clearActions();
+    method public androidx.core.app.NotificationCompat.Builder clearInvisibleActions();
+    method public androidx.core.app.NotificationCompat.Builder clearPeople();
+    method public android.widget.RemoteViews? createBigContentView();
+    method public android.widget.RemoteViews? createContentView();
+    method public android.widget.RemoteViews? createHeadsUpContentView();
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Extender);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! getBigContentView();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.core.app.NotificationCompat.BubbleMetadata? getBubbleMetadata();
+    method @ColorInt @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int getColor();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! getContentView();
+    method public android.os.Bundle getExtras();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! getHeadsUpContentView();
+    method @Deprecated public android.app.Notification getNotification();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int getPriority();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public long getWhenIfShowing();
+    method protected static CharSequence? limitCharSequenceLength(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setAllowSystemGeneratedContextualActions(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setAutoCancel(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setBadgeIconType(@androidx.core.app.NotificationCompat.BadgeIconType int);
+    method public androidx.core.app.NotificationCompat.Builder setBubbleMetadata(androidx.core.app.NotificationCompat.BubbleMetadata?);
+    method public androidx.core.app.NotificationCompat.Builder setCategory(String?);
+    method public androidx.core.app.NotificationCompat.Builder setChannelId(String);
+    method @RequiresApi(24) public androidx.core.app.NotificationCompat.Builder setChronometerCountDown(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setColor(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.Builder setColorized(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setContent(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setContentInfo(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder setContentText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setDefaults(int);
+    method public androidx.core.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent?, boolean);
+    method public androidx.core.app.NotificationCompat.Builder setGroup(String?);
+    method public androidx.core.app.NotificationCompat.Builder setGroupAlertBehavior(@androidx.core.app.NotificationCompat.GroupAlertBehavior int);
+    method public androidx.core.app.NotificationCompat.Builder setGroupSummary(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.Builder setLights(@ColorInt int, int, int);
+    method public androidx.core.app.NotificationCompat.Builder setLocalOnly(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder setNotificationSilent();
+    method public androidx.core.app.NotificationCompat.Builder setNumber(int);
+    method public androidx.core.app.NotificationCompat.Builder setOngoing(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setPriority(int);
+    method public androidx.core.app.NotificationCompat.Builder setProgress(int, int, boolean);
+    method public androidx.core.app.NotificationCompat.Builder setPublicVersion(android.app.Notification?);
+    method public androidx.core.app.NotificationCompat.Builder setRemoteInputHistory(CharSequence![]?);
+    method public androidx.core.app.NotificationCompat.Builder setSettingsText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setShortcutId(String?);
+    method public androidx.core.app.NotificationCompat.Builder setShortcutInfo(androidx.core.content.pm.ShortcutInfoCompat?);
+    method public androidx.core.app.NotificationCompat.Builder setShowWhen(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setSilent(boolean);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.Builder setSmallIcon(androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int);
+    method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int, int);
+    method public androidx.core.app.NotificationCompat.Builder setSortKey(String?);
+    method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?);
+    method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?, @androidx.core.app.NotificationCompat.StreamType int);
+    method public androidx.core.app.NotificationCompat.Builder setStyle(androidx.core.app.NotificationCompat.Style?);
+    method public androidx.core.app.NotificationCompat.Builder setSubText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?, android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setTimeoutAfter(long);
+    method public androidx.core.app.NotificationCompat.Builder setUsesChronometer(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setVibrate(long[]?);
+    method public androidx.core.app.NotificationCompat.Builder setVisibility(@androidx.core.app.NotificationCompat.NotificationVisibility int);
+    method public androidx.core.app.NotificationCompat.Builder setWhen(long);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public java.util.ArrayList<androidx.core.app.NotificationCompat.Action!>! mActions;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.Context! mContext;
+    field @Deprecated public java.util.ArrayList<java.lang.String!>! mPeople;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public java.util.ArrayList<androidx.core.app.Person!> mPersonList;
+  }
+
+  public static final class NotificationCompat.CarExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.CarExtender();
+    ctor public NotificationCompat.CarExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method @ColorInt public int getColor();
+    method public android.graphics.Bitmap? getLargeIcon();
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation? getUnreadConversation();
+    method public androidx.core.app.NotificationCompat.CarExtender setColor(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender setUnreadConversation(androidx.core.app.NotificationCompat.CarExtender.UnreadConversation?);
+  }
+
+  @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation {
+    method @Deprecated public long getLatestTimestamp();
+    method @Deprecated public String![]? getMessages();
+    method @Deprecated public String? getParticipant();
+    method @Deprecated public String![]? getParticipants();
+    method @Deprecated public android.app.PendingIntent? getReadPendingIntent();
+    method @Deprecated public androidx.core.app.RemoteInput? getRemoteInput();
+    method @Deprecated public android.app.PendingIntent? getReplyPendingIntent();
+  }
+
+  @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+    ctor @Deprecated public NotificationCompat.CarExtender.UnreadConversation.Builder(String);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(String?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation build();
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent?, androidx.core.app.RemoteInput?);
+  }
+
+  public static class NotificationCompat.DecoratedCustomViewStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static interface NotificationCompat.Extender {
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.GROUP_ALERT_ALL, androidx.core.app.NotificationCompat.GROUP_ALERT_SUMMARY, androidx.core.app.NotificationCompat.GROUP_ALERT_CHILDREN}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.GroupAlertBehavior {
+  }
+
+  public static class NotificationCompat.InboxStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.InboxStyle();
+    ctor public NotificationCompat.InboxStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.InboxStyle addLine(CharSequence?);
+    method public androidx.core.app.NotificationCompat.InboxStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.InboxStyle setSummaryText(CharSequence?);
+  }
+
+  public static class NotificationCompat.MessagingStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor @Deprecated public NotificationCompat.MessagingStyle(CharSequence);
+    ctor public NotificationCompat.MessagingStyle(androidx.core.app.Person);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addHistoricMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+    method @Deprecated public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, CharSequence?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, androidx.core.app.Person?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+    method public static androidx.core.app.NotificationCompat.MessagingStyle? extractMessagingStyleFromNotification(android.app.Notification);
+    method public CharSequence? getConversationTitle();
+    method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getHistoricMessages();
+    method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getMessages();
+    method public androidx.core.app.Person getUser();
+    method @Deprecated public CharSequence? getUserDisplayName();
+    method public boolean isGroupConversation();
+    method public androidx.core.app.NotificationCompat.MessagingStyle setConversationTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle setGroupConversation(boolean);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class NotificationCompat.MessagingStyle.Message {
+    ctor public NotificationCompat.MessagingStyle.Message(CharSequence?, long, androidx.core.app.Person?);
+    ctor @Deprecated public NotificationCompat.MessagingStyle.Message(CharSequence?, long, CharSequence?);
+    method public String? getDataMimeType();
+    method public android.net.Uri? getDataUri();
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.Person? getPerson();
+    method @Deprecated public CharSequence? getSender();
+    method public CharSequence getText();
+    method public long getTimestamp();
+    method public androidx.core.app.NotificationCompat.MessagingStyle.Message setData(String?, android.net.Uri?);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC, androidx.core.app.NotificationCompat.VISIBILITY_PRIVATE, androidx.core.app.NotificationCompat.VISIBILITY_SECRET}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.NotificationVisibility {
+  }
+
+  @IntDef({android.media.AudioManager.STREAM_VOICE_CALL, android.media.AudioManager.STREAM_SYSTEM, android.media.AudioManager.STREAM_RING, android.media.AudioManager.STREAM_MUSIC, android.media.AudioManager.STREAM_ALARM, android.media.AudioManager.STREAM_NOTIFICATION, android.media.AudioManager.STREAM_DTMF, android.media.AudioManager.STREAM_ACCESSIBILITY}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.StreamType {
+  }
+
+  public abstract static class NotificationCompat.Style {
+    ctor public NotificationCompat.Style();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void addCompatExtras(android.os.Bundle);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void apply(androidx.core.app.NotificationBuilderWithBuilderAccessor!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews applyStandardTemplate(boolean, int, boolean);
+    method public android.app.Notification? build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void buildIntoRemoteViews(android.widget.RemoteViews!, android.widget.RemoteViews!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected void clearCompatExtraKeys(android.os.Bundle);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.Bitmap! createColoredBitmap(int, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean displayCustomViewInline();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.app.NotificationCompat.Style? extractStyleFromNotification(android.app.Notification);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected String? getClassName();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! makeBigContentView(androidx.core.app.NotificationBuilderWithBuilderAccessor!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! makeContentView(androidx.core.app.NotificationBuilderWithBuilderAccessor!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! makeHeadsUpContentView(androidx.core.app.NotificationBuilderWithBuilderAccessor!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected void restoreFromCompatExtras(android.os.Bundle);
+    method public void setBuilder(androidx.core.app.NotificationCompat.Builder?);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected androidx.core.app.NotificationCompat.Builder! mBuilder;
+  }
+
+  public static final class NotificationCompat.WearableExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.WearableExtender();
+    ctor public NotificationCompat.WearableExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.WearableExtender addAction(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.WearableExtender addActions(java.util.List<androidx.core.app.NotificationCompat.Action!>);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification!>);
+    method public androidx.core.app.NotificationCompat.WearableExtender clearActions();
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender clearPages();
+    method public androidx.core.app.NotificationCompat.WearableExtender clone();
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method public java.util.List<androidx.core.app.NotificationCompat.Action!> getActions();
+    method @Deprecated public android.graphics.Bitmap? getBackground();
+    method public String? getBridgeTag();
+    method public int getContentAction();
+    method @Deprecated public int getContentIcon();
+    method @Deprecated public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method @Deprecated public int getCustomContentHeight();
+    method @Deprecated public int getCustomSizePreset();
+    method public String? getDismissalId();
+    method @Deprecated public android.app.PendingIntent? getDisplayIntent();
+    method @Deprecated public int getGravity();
+    method @Deprecated public boolean getHintAmbientBigPicture();
+    method @Deprecated public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
+    method @Deprecated public boolean getHintHideIcon();
+    method @Deprecated public int getHintScreenTimeout();
+    method @Deprecated public boolean getHintShowBackgroundOnly();
+    method @Deprecated public java.util.List<android.app.Notification!> getPages();
+    method public boolean getStartScrollBottom();
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.WearableExtender setBridgeTag(String?);
+    method public androidx.core.app.NotificationCompat.WearableExtender setContentAction(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIcon(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+    method public androidx.core.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+    method public androidx.core.app.NotificationCompat.WearableExtender setDismissalId(String?);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent?);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setGravity(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public androidx.core.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public androidx.core.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+    field @Deprecated public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field @Deprecated public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field @Deprecated public static final int SIZE_DEFAULT = 0; // 0x0
+    field @Deprecated public static final int SIZE_FULL_SCREEN = 5; // 0x5
+    field @Deprecated public static final int SIZE_LARGE = 4; // 0x4
+    field @Deprecated public static final int SIZE_MEDIUM = 3; // 0x3
+    field @Deprecated public static final int SIZE_SMALL = 2; // 0x2
+    field @Deprecated public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public final class NotificationCompatExtras {
+    field public static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+    field public static final String EXTRA_GROUP_KEY = "android.support.groupKey";
+    field public static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+    field public static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+    field public static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+    field public static final String EXTRA_SORT_KEY = "android.support.sortKey";
+  }
+
+  public abstract class NotificationCompatSideChannelService extends android.app.Service {
+    ctor public NotificationCompatSideChannelService();
+    method public abstract void cancel(String!, int, String!);
+    method public abstract void cancelAll(String!);
+    method public abstract void notify(String!, int, String!, android.app.Notification!);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+  }
+
+  public final class NotificationManagerCompat {
+    method public boolean areNotificationsEnabled();
+    method public void cancel(int);
+    method public void cancel(String?, int);
+    method public void cancelAll();
+    method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void createNotificationChannel(androidx.core.app.NotificationChannelCompat);
+    method public void createNotificationChannelGroup(android.app.NotificationChannelGroup);
+    method public void createNotificationChannelGroup(androidx.core.app.NotificationChannelGroupCompat);
+    method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup!>);
+    method public void createNotificationChannelGroupsCompat(java.util.List<androidx.core.app.NotificationChannelGroupCompat!>);
+    method public void createNotificationChannels(java.util.List<android.app.NotificationChannel!>);
+    method public void createNotificationChannelsCompat(java.util.List<androidx.core.app.NotificationChannelCompat!>);
+    method public void deleteNotificationChannel(String);
+    method public void deleteNotificationChannelGroup(String);
+    method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
+    method public static androidx.core.app.NotificationManagerCompat from(android.content.Context);
+    method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
+    method public int getImportance();
+    method public android.app.NotificationChannel? getNotificationChannel(String);
+    method public android.app.NotificationChannel? getNotificationChannel(String, String);
+    method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String);
+    method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String, String);
+    method public android.app.NotificationChannelGroup? getNotificationChannelGroup(String);
+    method public androidx.core.app.NotificationChannelGroupCompat? getNotificationChannelGroupCompat(String);
+    method public java.util.List<android.app.NotificationChannelGroup!> getNotificationChannelGroups();
+    method public java.util.List<androidx.core.app.NotificationChannelGroupCompat!> getNotificationChannelGroupsCompat();
+    method public java.util.List<android.app.NotificationChannel!> getNotificationChannels();
+    method public java.util.List<androidx.core.app.NotificationChannelCompat!> getNotificationChannelsCompat();
+    method public void notify(int, android.app.Notification);
+    method public void notify(String?, int, android.app.Notification);
+    field public static final String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+    field public static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
+    field public static final int IMPORTANCE_NONE = 0; // 0x0
+    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+  }
+
+  public class Person {
+    method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.app.Person fromAndroidPerson(android.app.Person);
+    method public static androidx.core.app.Person fromBundle(android.os.Bundle);
+    method @RequiresApi(22) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.app.Person fromPersistableBundle(android.os.PersistableBundle);
+    method public androidx.core.graphics.drawable.IconCompat? getIcon();
+    method public String? getKey();
+    method public CharSequence? getName();
+    method public String? getUri();
+    method public boolean isBot();
+    method public boolean isImportant();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public String resolveToLegacyUri();
+    method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.app.Person toAndroidPerson();
+    method public androidx.core.app.Person.Builder toBuilder();
+    method public android.os.Bundle toBundle();
+    method @RequiresApi(22) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.os.PersistableBundle toPersistableBundle();
+  }
+
+  public static class Person.Builder {
+    ctor public Person.Builder();
+    method public androidx.core.app.Person build();
+    method public androidx.core.app.Person.Builder setBot(boolean);
+    method public androidx.core.app.Person.Builder setIcon(androidx.core.graphics.drawable.IconCompat?);
+    method public androidx.core.app.Person.Builder setImportant(boolean);
+    method public androidx.core.app.Person.Builder setKey(String?);
+    method public androidx.core.app.Person.Builder setName(CharSequence?);
+    method public androidx.core.app.Person.Builder setUri(String?);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(jetifyAs="android.support.v4.app.RemoteActionCompat") public final class RemoteActionCompat implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public RemoteActionCompat(androidx.core.graphics.drawable.IconCompat, CharSequence, CharSequence, android.app.PendingIntent);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public RemoteActionCompat();
+    ctor public RemoteActionCompat(androidx.core.app.RemoteActionCompat);
+    method @RequiresApi(26) public static androidx.core.app.RemoteActionCompat createFromRemoteAction(android.app.RemoteAction);
+    method public android.app.PendingIntent getActionIntent();
+    method public CharSequence getContentDescription();
+    method public androidx.core.graphics.drawable.IconCompat getIcon();
+    method public CharSequence getTitle();
+    method public boolean isEnabled();
+    method public void setEnabled(boolean);
+    method public void setShouldShowIcon(boolean);
+    method public boolean shouldShowIcon();
+    method @RequiresApi(26) public android.app.RemoteAction toRemoteAction();
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(4) public android.app.PendingIntent! mActionIntent;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(3) public CharSequence! mContentDescription;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(5) public boolean mEnabled;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(1) public androidx.core.graphics.drawable.IconCompat! mIcon;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(6) public boolean mShouldShowIcon;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(2) public CharSequence! mTitle;
+  }
+
+  public final class RemoteInput {
+    method public static void addDataResultToIntent(androidx.core.app.RemoteInput!, android.content.Intent!, java.util.Map<java.lang.String!,android.net.Uri!>!);
+    method public static void addResultsToIntent(androidx.core.app.RemoteInput![]!, android.content.Intent!, android.os.Bundle!);
+    method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String!>! getAllowedDataTypes();
+    method public CharSequence![]! getChoices();
+    method public static java.util.Map<java.lang.String!,android.net.Uri!>! getDataResultsFromIntent(android.content.Intent!, String!);
+    method @androidx.core.app.RemoteInput.EditChoicesBeforeSending public int getEditChoicesBeforeSending();
+    method public android.os.Bundle! getExtras();
+    method public CharSequence! getLabel();
+    method public String! getResultKey();
+    method public static android.os.Bundle! getResultsFromIntent(android.content.Intent!);
+    method @androidx.core.app.RemoteInput.Source public static int getResultsSource(android.content.Intent);
+    method public boolean isDataOnly();
+    method public static void setResultsSource(android.content.Intent, @androidx.core.app.RemoteInput.Source int);
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0; // 0x0
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1; // 0x1
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2; // 0x2
+    field public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    field public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+    field public static final int SOURCE_CHOICE = 1; // 0x1
+    field public static final int SOURCE_FREE_FORM_INPUT = 0; // 0x0
+  }
+
+  public static final class RemoteInput.Builder {
+    ctor public RemoteInput.Builder(String);
+    method public androidx.core.app.RemoteInput.Builder addExtras(android.os.Bundle);
+    method public androidx.core.app.RemoteInput build();
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.RemoteInput.Builder setAllowDataType(String, boolean);
+    method public androidx.core.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+    method public androidx.core.app.RemoteInput.Builder setChoices(CharSequence![]?);
+    method public androidx.core.app.RemoteInput.Builder setEditChoicesBeforeSending(@androidx.core.app.RemoteInput.EditChoicesBeforeSending int);
+    method public androidx.core.app.RemoteInput.Builder setLabel(CharSequence?);
+  }
+
+  @IntDef({androidx.core.app.RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO, androidx.core.app.RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED, androidx.core.app.RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RemoteInput.EditChoicesBeforeSending {
+  }
+
+  @IntDef({androidx.core.app.RemoteInput.SOURCE_FREE_FORM_INPUT, androidx.core.app.RemoteInput.SOURCE_CHOICE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RemoteInput.Source {
+  }
+
+  public final class ServiceCompat {
+    method public static void stopForeground(android.app.Service, @androidx.core.app.ServiceCompat.StopForegroundFlags int);
+    field public static final int START_STICKY = 1; // 0x1
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+  }
+
+  @IntDef(flag=true, value={androidx.core.app.ServiceCompat.STOP_FOREGROUND_REMOVE, androidx.core.app.ServiceCompat.STOP_FOREGROUND_DETACH}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ServiceCompat.StopForegroundFlags {
+  }
+
+  public final class ShareCompat {
+    method @Deprecated public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
+    method public static android.content.ComponentName? getCallingActivity(android.app.Activity);
+    method public static String? getCallingPackage(android.app.Activity);
+    field public static final String EXTRA_CALLING_ACTIVITY = "androidx.core.app.EXTRA_CALLING_ACTIVITY";
+    field public static final String EXTRA_CALLING_ACTIVITY_INTEROP = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+    field public static final String EXTRA_CALLING_PACKAGE = "androidx.core.app.EXTRA_CALLING_PACKAGE";
+    field public static final String EXTRA_CALLING_PACKAGE_INTEROP = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+  }
+
+  public static class ShareCompat.IntentBuilder {
+    ctor public ShareCompat.IntentBuilder(android.content.Context);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+    method public android.content.Intent createChooserIntent();
+    method @Deprecated public static androidx.core.app.ShareCompat.IntentBuilder from(android.app.Activity);
+    method public android.content.Intent getIntent();
+    method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(CharSequence?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(@StringRes int);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailBcc(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailCc(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailTo(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setHtmlText(String?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setStream(android.net.Uri?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setSubject(String?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setText(CharSequence?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setType(String?);
+    method public void startChooser();
+  }
+
+  public static class ShareCompat.IntentReader {
+    ctor public ShareCompat.IntentReader(android.app.Activity);
+    ctor public ShareCompat.IntentReader(android.content.Context, android.content.Intent);
+    method @Deprecated public static androidx.core.app.ShareCompat.IntentReader from(android.app.Activity);
+    method public android.content.ComponentName? getCallingActivity();
+    method public android.graphics.drawable.Drawable? getCallingActivityIcon();
+    method public android.graphics.drawable.Drawable? getCallingApplicationIcon();
+    method public CharSequence? getCallingApplicationLabel();
+    method public String? getCallingPackage();
+    method public String![]? getEmailBcc();
+    method public String![]? getEmailCc();
+    method public String![]? getEmailTo();
+    method public String? getHtmlText();
+    method public android.net.Uri? getStream();
+    method public android.net.Uri? getStream(int);
+    method public int getStreamCount();
+    method public String? getSubject();
+    method public CharSequence? getText();
+    method public String? getType();
+    method public boolean isMultipleShare();
+    method public boolean isShareIntent();
+    method public boolean isSingleShare();
+  }
+
+  public abstract class SharedElementCallback {
+    ctor public SharedElementCallback();
+    method public android.os.Parcelable! onCaptureSharedElementSnapshot(android.view.View!, android.graphics.Matrix!, android.graphics.RectF!);
+    method public android.view.View! onCreateSnapshotView(android.content.Context!, android.os.Parcelable!);
+    method public void onMapSharedElements(java.util.List<java.lang.String!>!, java.util.Map<java.lang.String!,android.view.View!>!);
+    method public void onRejectSharedElements(java.util.List<android.view.View!>!);
+    method public void onSharedElementEnd(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+    method public void onSharedElementStart(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+    method public void onSharedElementsArrived(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, androidx.core.app.SharedElementCallback.OnSharedElementsReadyListener!);
+  }
+
+  public static interface SharedElementCallback.OnSharedElementsReadyListener {
+    method public void onSharedElementsReady();
+  }
+
+  public final class TaskStackBuilder implements java.lang.Iterable<android.content.Intent> {
+    method public androidx.core.app.TaskStackBuilder addNextIntent(android.content.Intent);
+    method public androidx.core.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+    method public androidx.core.app.TaskStackBuilder addParentStack(android.app.Activity);
+    method public androidx.core.app.TaskStackBuilder addParentStack(Class<?>);
+    method public androidx.core.app.TaskStackBuilder! addParentStack(android.content.ComponentName!);
+    method public static androidx.core.app.TaskStackBuilder create(android.content.Context);
+    method public android.content.Intent? editIntentAt(int);
+    method @Deprecated public static androidx.core.app.TaskStackBuilder! from(android.content.Context!);
+    method @Deprecated public android.content.Intent! getIntent(int);
+    method public int getIntentCount();
+    method public android.content.Intent![] getIntents();
+    method public android.app.PendingIntent? getPendingIntent(int, int);
+    method public android.app.PendingIntent? getPendingIntent(int, int, android.os.Bundle?);
+    method @Deprecated public java.util.Iterator<android.content.Intent!>! iterator();
+    method public void startActivities();
+    method public void startActivities(android.os.Bundle?);
+  }
+
+  public static interface TaskStackBuilder.SupportParentable {
+    method public android.content.Intent? getSupportParentActivityIntent();
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentProviderCompat {
+    method public static android.content.Context requireContext(android.content.ContentProvider);
+  }
+
+  public final class ContentResolverCompat {
+    method public static android.database.Cursor! query(android.content.ContentResolver!, android.net.Uri!, String![]!, String!, String![]!, String!, androidx.core.os.CancellationSignal!);
+  }
+
+  public class ContextCompat {
+    ctor protected ContextCompat();
+    method public static int checkSelfPermission(android.content.Context, String);
+    method public static android.content.Context? createDeviceProtectedStorageContext(android.content.Context);
+    method public static java.io.File! getCodeCacheDir(android.content.Context);
+    method @ColorInt public static int getColor(android.content.Context, @ColorRes int);
+    method public static android.content.res.ColorStateList? getColorStateList(android.content.Context, @ColorRes int);
+    method public static java.io.File? getDataDir(android.content.Context);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+    method public static java.io.File![] getExternalCacheDirs(android.content.Context);
+    method public static java.io.File![] getExternalFilesDirs(android.content.Context, String?);
+    method public static java.util.concurrent.Executor! getMainExecutor(android.content.Context!);
+    method public static java.io.File? getNoBackupFilesDir(android.content.Context);
+    method public static java.io.File![] getObbDirs(android.content.Context);
+    method public static <T> T? getSystemService(android.content.Context, Class<T!>);
+    method public static String? getSystemServiceName(android.content.Context, Class<?>);
+    method public static boolean isDeviceProtectedStorage(android.content.Context);
+    method public static boolean startActivities(android.content.Context, android.content.Intent![]);
+    method public static boolean startActivities(android.content.Context, android.content.Intent![], android.os.Bundle?);
+    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle?);
+    method public static void startForegroundService(android.content.Context, android.content.Intent);
+  }
+
+  public class FileProvider extends android.content.ContentProvider {
+    ctor public FileProvider();
+    method public int delete(android.net.Uri, String?, String![]?);
+    method public String! getType(android.net.Uri);
+    method public static android.net.Uri! getUriForFile(android.content.Context, String, java.io.File);
+    method public static android.net.Uri getUriForFile(android.content.Context, String, java.io.File, String);
+    method public android.net.Uri! insert(android.net.Uri, android.content.ContentValues!);
+    method public boolean onCreate();
+    method public android.database.Cursor! query(android.net.Uri, String![]?, String?, String![]?, String?);
+    method public int update(android.net.Uri, android.content.ContentValues!, String?, String![]?);
+  }
+
+  public final class IntentCompat {
+    method public static android.content.Intent makeMainSelectorActivity(String, String);
+    field public static final String ACTION_CREATE_REMINDER = "android.intent.action.CREATE_REMINDER";
+    field public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    field public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+    field public static final String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
+    field public static final String EXTRA_TIME = "android.intent.extra.TIME";
+  }
+
+  public final class LocusIdCompat {
+    ctor public LocusIdCompat(String);
+    method public String getId();
+    method @RequiresApi(29) public android.content.LocusId toLocusId();
+    method @RequiresApi(29) public static androidx.core.content.LocusIdCompat toLocusIdCompat(android.content.LocusId);
+  }
+
+  public final class MimeTypeFilter {
+    method public static boolean matches(String?, String);
+    method public static String? matches(String?, String![]);
+    method public static String? matches(String![]?, String);
+    method public static String![] matchesMany(String![]?, String);
+  }
+
+  public final class PermissionChecker {
+    method @androidx.core.content.PermissionChecker.PermissionResult public static int checkCallingOrSelfPermission(android.content.Context, String);
+    method @androidx.core.content.PermissionChecker.PermissionResult public static int checkCallingPermission(android.content.Context, String, String?);
+    method @androidx.core.content.PermissionChecker.PermissionResult public static int checkPermission(android.content.Context, String, int, int, String?);
+    method @androidx.core.content.PermissionChecker.PermissionResult public static int checkSelfPermission(android.content.Context, String);
+    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+    field public static final int PERMISSION_GRANTED = 0; // 0x0
+  }
+
+  @IntDef({androidx.core.content.PermissionChecker.PERMISSION_GRANTED, androidx.core.content.PermissionChecker.PERMISSION_DENIED, androidx.core.content.PermissionChecker.PERMISSION_DENIED_APP_OP}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PermissionChecker.PermissionResult {
+  }
+
+  @Deprecated public final class SharedPreferencesCompat {
+  }
+
+  @Deprecated public static final class SharedPreferencesCompat.EditorCompat {
+    method @Deprecated public void apply(android.content.SharedPreferences.Editor);
+    method @Deprecated public static androidx.core.content.SharedPreferencesCompat.EditorCompat! getInstance();
+  }
+
+}
+
+package androidx.core.content.pm {
+
+  @Deprecated public final class ActivityInfoCompat {
+    field @Deprecated public static final int CONFIG_UI_MODE = 512; // 0x200
+  }
+
+  public final class PackageInfoCompat {
+    method public static long getLongVersionCode(android.content.pm.PackageInfo);
+    method public static java.util.List<android.content.pm.Signature!> getSignatures(android.content.pm.PackageManager, String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static boolean hasSignatures(android.content.pm.PackageManager, String, @Size(min=1) java.util.Map<byte[]!,java.lang.Integer!>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+  }
+
+  public final class PermissionInfoCompat {
+    method public static int getProtection(android.content.pm.PermissionInfo);
+    method public static int getProtectionFlags(android.content.pm.PermissionInfo);
+  }
+
+  public class ShortcutInfoCompat {
+    method public android.content.ComponentName? getActivity();
+    method public java.util.Set<java.lang.String!>? getCategories();
+    method public CharSequence? getDisabledMessage();
+    method public int getDisabledReason();
+    method public android.os.PersistableBundle? getExtras();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.core.graphics.drawable.IconCompat! getIcon();
+    method public String getId();
+    method public android.content.Intent getIntent();
+    method public android.content.Intent![] getIntents();
+    method public long getLastChangedTimestamp();
+    method public androidx.core.content.LocusIdCompat? getLocusId();
+    method public CharSequence? getLongLabel();
+    method public String getPackage();
+    method public int getRank();
+    method public CharSequence getShortLabel();
+    method public android.os.UserHandle? getUserHandle();
+    method public boolean hasKeyFieldsOnly();
+    method public boolean isCached();
+    method public boolean isDeclaredInManifest();
+    method public boolean isDynamic();
+    method public boolean isEnabled();
+    method public boolean isImmutable();
+    method public boolean isPinned();
+    method @RequiresApi(25) public android.content.pm.ShortcutInfo! toShortcutInfo();
+  }
+
+  public static class ShortcutInfoCompat.Builder {
+    ctor public ShortcutInfoCompat.Builder(android.content.Context, String);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public ShortcutInfoCompat.Builder(androidx.core.content.pm.ShortcutInfoCompat);
+    ctor @RequiresApi(25) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public ShortcutInfoCompat.Builder(android.content.Context, android.content.pm.ShortcutInfo);
+    method public androidx.core.content.pm.ShortcutInfoCompat build();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setAlwaysBadged();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setCategories(java.util.Set<java.lang.String!>);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(CharSequence);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExtras(android.os.PersistableBundle);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIcon(androidx.core.graphics.drawable.IconCompat!);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsConversation();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
+    method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived(boolean);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPerson(androidx.core.app.Person);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPersons(androidx.core.app.Person![]);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setRank(int);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setShortLabel(CharSequence);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class ShortcutInfoCompatSaver<T> {
+    ctor public ShortcutInfoCompatSaver();
+    method @AnyThread public abstract T! addShortcuts(java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>!);
+    method @WorkerThread public java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>! getShortcuts() throws java.lang.Exception;
+    method @AnyThread public abstract T! removeAllShortcuts();
+    method @AnyThread public abstract T! removeShortcuts(java.util.List<java.lang.String!>!);
+  }
+
+  public class ShortcutManagerCompat {
+    method public static boolean addDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static android.content.Intent createShortcutResultIntent(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+    method public static void disableShortcuts(android.content.Context, java.util.List<java.lang.String!>, CharSequence?);
+    method public static void enableShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getDynamicShortcuts(android.content.Context);
+    method public static int getIconMaxHeight(android.content.Context);
+    method public static int getIconMaxWidth(android.content.Context);
+    method public static int getMaxShortcutCountPerActivity(android.content.Context);
+    method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getShortcuts(android.content.Context, @androidx.core.content.pm.ShortcutManagerCompat.ShortcutMatchFlags int);
+    method public static boolean isRateLimitingActive(android.content.Context);
+    method public static boolean isRequestPinShortcutSupported(android.content.Context);
+    method public static boolean pushDynamicShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+    method public static void removeAllDynamicShortcuts(android.content.Context);
+    method public static void removeDynamicShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+    method public static void removeLongLivedShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+    method public static void reportShortcutUsed(android.content.Context, String);
+    method public static boolean requestPinShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat, android.content.IntentSender?);
+    method public static boolean setDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static boolean updateShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    field public static final String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID";
+    field public static final int FLAG_MATCH_CACHED = 8; // 0x8
+    field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
+    field public static final int FLAG_MATCH_MANIFEST = 1; // 0x1
+    field public static final int FLAG_MATCH_PINNED = 4; // 0x4
+  }
+
+  @IntDef(flag=true, value={androidx.core.content.pm.ShortcutManagerCompat.FLAG_MATCH_MANIFEST, androidx.core.content.pm.ShortcutManagerCompat.FLAG_MATCH_DYNAMIC, androidx.core.content.pm.ShortcutManagerCompat.FLAG_MATCH_PINNED, androidx.core.content.pm.ShortcutManagerCompat.FLAG_MATCH_CACHED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ShortcutManagerCompat.ShortcutMatchFlags {
+  }
+
+}
+
+package androidx.core.content.res {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ColorStateListInflaterCompat {
+    method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static android.content.res.ColorStateList createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static android.content.res.ColorStateList? inflate(android.content.res.Resources, @XmlRes int, android.content.res.Resources.Theme?);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ComplexColorCompat {
+    method @ColorInt public int getColor();
+    method public android.graphics.Shader? getShader();
+    method public static androidx.core.content.res.ComplexColorCompat? inflate(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?);
+    method public boolean isGradient();
+    method public boolean isStateful();
+    method public boolean onStateChanged(int[]!);
+    method public void setColor(@ColorInt int);
+    method public boolean willDraw();
+  }
+
+  public final class ConfigurationHelper {
+    method public static int getDensityDpi(android.content.res.Resources);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FontResourcesParserCompat {
+    method public static androidx.core.content.res.FontResourcesParserCompat.FamilyResourceEntry? parse(org.xmlpull.v1.XmlPullParser!, android.content.res.Resources!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static java.util.List<java.util.List<byte[]!>!>! readCerts(android.content.res.Resources!, @ArrayRes int);
+    field public static final int FETCH_STRATEGY_ASYNC = 1; // 0x1
+    field public static final int FETCH_STRATEGY_BLOCKING = 0; // 0x0
+    field public static final int INFINITE_TIMEOUT_VALUE = -1; // 0xffffffff
+  }
+
+  public static interface FontResourcesParserCompat.FamilyResourceEntry {
+  }
+
+  @IntDef({androidx.core.content.res.FontResourcesParserCompat.FETCH_STRATEGY_BLOCKING, androidx.core.content.res.FontResourcesParserCompat.FETCH_STRATEGY_ASYNC}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface FontResourcesParserCompat.FetchStrategy {
+  }
+
+  public static final class FontResourcesParserCompat.FontFamilyFilesResourceEntry implements androidx.core.content.res.FontResourcesParserCompat.FamilyResourceEntry {
+    ctor public FontResourcesParserCompat.FontFamilyFilesResourceEntry(androidx.core.content.res.FontResourcesParserCompat.FontFileResourceEntry![]);
+    method public androidx.core.content.res.FontResourcesParserCompat.FontFileResourceEntry![] getEntries();
+  }
+
+  public static final class FontResourcesParserCompat.FontFileResourceEntry {
+    ctor public FontResourcesParserCompat.FontFileResourceEntry(String, int, boolean, String?, int, int);
+    method public String getFileName();
+    method public int getResourceId();
+    method public int getTtcIndex();
+    method public String? getVariationSettings();
+    method public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static final class FontResourcesParserCompat.ProviderResourceEntry implements androidx.core.content.res.FontResourcesParserCompat.FamilyResourceEntry {
+    ctor public FontResourcesParserCompat.ProviderResourceEntry(androidx.core.provider.FontRequest, @androidx.core.content.res.FontResourcesParserCompat.FetchStrategy int, int);
+    method @androidx.core.content.res.FontResourcesParserCompat.FetchStrategy public int getFetchStrategy();
+    method public androidx.core.provider.FontRequest getRequest();
+    method public int getTimeout();
+  }
+
+  public final class ResourcesCompat {
+    method public static android.graphics.Typeface? getCachedFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+    method @ColorInt public static int getColor(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.content.res.ColorStateList? getColorStateList(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable? getDrawableForDensity(android.content.res.Resources, @DrawableRes int, int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static float getFloat(android.content.res.Resources, @DimenRes int);
+    method public static android.graphics.Typeface? getFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+    method public static void getFont(android.content.Context, @FontRes int, androidx.core.content.res.ResourcesCompat.FontCallback, android.os.Handler?) throws android.content.res.Resources.NotFoundException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface! getFont(android.content.Context, @FontRes int, android.util.TypedValue!, int, androidx.core.content.res.ResourcesCompat.FontCallback?) throws android.content.res.Resources.NotFoundException;
+    field @AnyRes public static final int ID_NULL = 0; // 0x0
+  }
+
+  public abstract static class ResourcesCompat.FontCallback {
+    ctor public ResourcesCompat.FontCallback();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final void callbackFailAsync(@androidx.core.provider.FontsContractCompat.FontRequestCallback.FontRequestFailReason int, android.os.Handler?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final void callbackSuccessAsync(android.graphics.Typeface!, android.os.Handler?);
+    method public abstract void onFontRetrievalFailed(@androidx.core.provider.FontsContractCompat.FontRequestCallback.FontRequestFailReason int);
+    method public abstract void onFontRetrieved(android.graphics.Typeface);
+  }
+
+  public static final class ResourcesCompat.ThemeCompat {
+    method public static void rebase(android.content.res.Resources.Theme);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TypedArrayUtils {
+    method public static int getAttr(android.content.Context, int, int);
+    method public static boolean getBoolean(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int, boolean);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int);
+    method public static int getInt(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int, int);
+    method public static boolean getNamedBoolean(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int, boolean);
+    method @ColorInt public static int getNamedColor(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int, @ColorInt int);
+    method public static android.content.res.ColorStateList? getNamedColorStateList(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme?, String, @StyleableRes int);
+    method public static androidx.core.content.res.ComplexColorCompat! getNamedComplexColor(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme?, String, @StyleableRes int, @ColorInt int);
+    method public static float getNamedFloat(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int, float);
+    method public static int getNamedInt(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int, int);
+    method @AnyRes public static int getNamedResourceId(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int, @AnyRes int);
+    method public static String? getNamedString(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int);
+    method @AnyRes public static int getResourceId(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int, @AnyRes int);
+    method public static String? getString(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int);
+    method public static CharSequence? getText(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int);
+    method public static CharSequence![]? getTextArray(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int);
+    method public static boolean hasAttribute(org.xmlpull.v1.XmlPullParser, String);
+    method public static android.content.res.TypedArray obtainAttributes(android.content.res.Resources, android.content.res.Resources.Theme?, android.util.AttributeSet, int[]);
+    method public static android.util.TypedValue? peekNamedValue(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, int);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorWindowCompat {
+    method public static android.database.CursorWindow create(String?, long);
+  }
+
+  @Deprecated public final class DatabaseUtilsCompat {
+    method @Deprecated public static String![]! appendSelectionArgs(String![]!, String![]!);
+    method @Deprecated public static String! concatenateWhere(String!, String!);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteCursorCompat {
+    method public static void setFillWindowForwardOnly(android.database.sqlite.SQLiteCursor, boolean);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapCompat {
+    method public static int getAllocationByteCount(android.graphics.Bitmap);
+    method public static boolean hasMipMap(android.graphics.Bitmap);
+    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+  }
+
+  public class BlendModeColorFilterCompat {
+    method public static android.graphics.ColorFilter? createBlendModeColorFilterCompat(int, androidx.core.graphics.BlendModeCompat);
+  }
+
+  public enum BlendModeCompat {
+    enum_constant public static final androidx.core.graphics.BlendModeCompat CLEAR;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_BURN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_DODGE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DARKEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat DIFFERENCE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_ATOP;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_IN;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OUT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OVER;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat EXCLUSION;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HARD_LIGHT;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HUE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat LIGHTEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat LUMINOSITY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat MODULATE;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat MULTIPLY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat OVERLAY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat PLUS;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SATURATION;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SCREEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SOFT_LIGHT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_ATOP;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_IN;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OUT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OVER;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat XOR;
+  }
+
+  public final class ColorUtils {
+    method @ColorInt public static int HSLToColor(float[]);
+    method @ColorInt public static int LABToColor(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double);
+    method public static void LABToXYZ(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double, double[]);
+    method public static void RGBToHSL(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, float[]);
+    method public static void RGBToLAB(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+    method public static void RGBToXYZ(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+    method @ColorInt public static int XYZToColor(@FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_X) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Y) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Z) double);
+    method public static void XYZToLAB(@FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_X) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Y) double, @FloatRange(from=0.0f, to=androidx.core.graphics.ColorUtils.XYZ_WHITE_REFERENCE_Z) double, double[]);
+    method @ColorInt public static int blendARGB(@ColorInt int, @ColorInt int, @FloatRange(from=0.0, to=1.0) float);
+    method public static void blendHSL(float[], float[], @FloatRange(from=0.0, to=1.0) float, float[]);
+    method public static void blendLAB(double[], double[], @FloatRange(from=0.0, to=1.0) double, double[]);
+    method public static double calculateContrast(@ColorInt int, @ColorInt int);
+    method @FloatRange(from=0.0, to=1.0) public static double calculateLuminance(@ColorInt int);
+    method public static int calculateMinimumAlpha(@ColorInt int, @ColorInt int, float);
+    method public static void colorToHSL(@ColorInt int, float[]);
+    method public static void colorToLAB(@ColorInt int, double[]);
+    method public static void colorToXYZ(@ColorInt int, double[]);
+    method public static int compositeColors(@ColorInt int, @ColorInt int);
+    method @RequiresApi(26) public static android.graphics.Color compositeColors(android.graphics.Color, android.graphics.Color);
+    method public static double distanceEuclidean(double[], double[]);
+    method @ColorInt public static int setAlphaComponent(@ColorInt int, @IntRange(from=0, to=255) int);
+  }
+
+  public final class Insets {
+    method public static androidx.core.graphics.Insets add(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets max(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets min(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets of(int, int, int, int);
+    method public static androidx.core.graphics.Insets of(android.graphics.Rect);
+    method public static androidx.core.graphics.Insets subtract(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method @RequiresApi(api=29) public static androidx.core.graphics.Insets toCompatInsets(android.graphics.Insets);
+    method @RequiresApi(api=29) public android.graphics.Insets toPlatformInsets();
+    method @Deprecated @RequiresApi(api=29) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.graphics.Insets wrap(android.graphics.Insets);
+    field public static final androidx.core.graphics.Insets NONE;
+    field public final int bottom;
+    field public final int left;
+    field public final int right;
+    field public final int top;
+  }
+
+  public final class PaintCompat {
+    method public static boolean hasGlyph(android.graphics.Paint, String);
+    method public static boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat?);
+  }
+
+  public final class PathSegment {
+    ctor public PathSegment(android.graphics.PointF, float, android.graphics.PointF, float);
+    method public android.graphics.PointF getEnd();
+    method public float getEndFraction();
+    method public android.graphics.PointF getStart();
+    method public float getStartFraction();
+  }
+
+  public final class PathUtils {
+    method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path);
+    method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path, @FloatRange(from=0) float);
+  }
+
+  public class TypefaceCompat {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @VisibleForTesting public static void clearCache();
+    method public static android.graphics.Typeface create(android.content.Context, android.graphics.Typeface?, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? createFromFontInfo(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![], int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? createFromResourcesFamilyXml(android.content.Context, androidx.core.content.res.FontResourcesParserCompat.FamilyResourceEntry, android.content.res.Resources, int, int, androidx.core.content.res.ResourcesCompat.FontCallback?, android.os.Handler?, boolean);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? createFromResourcesFontFile(android.content.Context, android.content.res.Resources, int, String!, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? findFromCache(android.content.res.Resources, int, int);
+  }
+
+  @RequiresApi(26) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TypefaceCompatApi26Impl {
+    ctor public TypefaceCompatApi26Impl();
+    method protected android.graphics.Typeface? createFromFamiliesWithDefault(Object!);
+    method public android.graphics.Typeface? createFromFontFamilyFilesResourceEntry(android.content.Context!, androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry!, android.content.res.Resources!, int);
+    method public android.graphics.Typeface? createFromFontInfo(android.content.Context!, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![], int);
+    method public android.graphics.Typeface? createFromResourcesFontFile(android.content.Context!, android.content.res.Resources!, int, String!, int);
+    method protected java.lang.reflect.Method! obtainAbortCreationMethod(Class<?>!) throws java.lang.NoSuchMethodException;
+    method protected java.lang.reflect.Method! obtainAddFontFromAssetManagerMethod(Class<?>!) throws java.lang.NoSuchMethodException;
+    method protected java.lang.reflect.Method! obtainAddFontFromBufferMethod(Class<?>!) throws java.lang.NoSuchMethodException;
+    method protected java.lang.reflect.Method! obtainCreateFromFamiliesWithDefaultMethod(Class<?>!) throws java.lang.NoSuchMethodException;
+    method protected Class<?>! obtainFontFamily() throws java.lang.ClassNotFoundException;
+    method protected java.lang.reflect.Constructor<?>! obtainFontFamilyCtor(Class<?>!) throws java.lang.NoSuchMethodException;
+    method protected java.lang.reflect.Method! obtainFreezeMethod(Class<?>!) throws java.lang.NoSuchMethodException;
+    field protected final java.lang.reflect.Method! mAbortCreation;
+    field protected final java.lang.reflect.Method! mAddFontFromAssetManager;
+    field protected final java.lang.reflect.Method! mAddFontFromBuffer;
+    field protected final java.lang.reflect.Method! mCreateFromFamiliesWithDefault;
+    field protected final Class<?>! mFontFamily;
+    field protected final java.lang.reflect.Constructor<?>! mFontFamilyCtor;
+    field protected final java.lang.reflect.Method! mFreeze;
+  }
+
+  @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TypefaceCompatApi28Impl extends androidx.core.graphics.TypefaceCompatApi26Impl {
+    ctor public TypefaceCompatApi28Impl();
+  }
+
+  @RequiresApi(29) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class TypefaceCompatApi29Impl {
+    ctor public TypefaceCompatApi29Impl();
+    method public android.graphics.Typeface? createFromFontFamilyFilesResourceEntry(android.content.Context!, androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry!, android.content.res.Resources!, int);
+    method public android.graphics.Typeface? createFromFontInfo(android.content.Context!, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![], int);
+    method protected android.graphics.Typeface! createFromInputStream(android.content.Context!, java.io.InputStream!);
+    method public android.graphics.Typeface? createFromResourcesFontFile(android.content.Context!, android.content.res.Resources!, int, String!, int);
+    method protected androidx.core.provider.FontsContractCompat.FontInfo! findBestInfo(androidx.core.provider.FontsContractCompat.FontInfo![]!, int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TypefaceCompatUtil {
+    method public static void closeQuietly(java.io.Closeable!);
+    method @RequiresApi(19) public static java.nio.ByteBuffer? copyToDirectBuffer(android.content.Context!, android.content.res.Resources!, int);
+    method public static boolean copyToFile(java.io.File!, java.io.InputStream!);
+    method public static boolean copyToFile(java.io.File!, android.content.res.Resources!, int);
+    method public static java.io.File? getTempFile(android.content.Context!);
+    method @RequiresApi(19) public static java.nio.ByteBuffer? mmap(android.content.Context!, android.os.CancellationSignal!, android.net.Uri!);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class DrawableCompat {
+    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+    method public static void clearColorFilter(android.graphics.drawable.Drawable);
+    method public static int getAlpha(android.graphics.drawable.Drawable);
+    method public static android.graphics.ColorFilter! getColorFilter(android.graphics.drawable.Drawable);
+    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+    method @Deprecated public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+    method public static void setTint(android.graphics.drawable.Drawable, @ColorInt int);
+    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList?);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static <T extends android.graphics.drawable.Drawable> T! unwrap(android.graphics.drawable.Drawable);
+    method public static android.graphics.drawable.Drawable! wrap(android.graphics.drawable.Drawable);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(allowSerialization=true, ignoreParcelables=true, isCustom=true, jetifyAs="android.support.v4.graphics.drawable.IconCompat") public class IconCompat extends androidx.versionedparcelable.CustomVersionedParcelable {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void addToShortcutIntent(android.content.Intent, android.graphics.drawable.Drawable?, android.content.Context);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void checkResource(android.content.Context);
+    method public static androidx.core.graphics.drawable.IconCompat? createFromBundle(android.os.Bundle);
+    method @RequiresApi(23) public static androidx.core.graphics.drawable.IconCompat? createFromIcon(android.content.Context, android.graphics.drawable.Icon);
+    method @RequiresApi(23) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.graphics.drawable.IconCompat? createFromIcon(android.graphics.drawable.Icon);
+    method @RequiresApi(23) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.graphics.drawable.IconCompat? createFromIconOrNullIfZeroResId(android.graphics.drawable.Icon);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithAdaptiveBitmap(android.graphics.Bitmap!);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(String);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(android.net.Uri);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithBitmap(android.graphics.Bitmap!);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithContentUri(String!);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithContentUri(android.net.Uri!);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithData(byte[]!, int, int);
+    method public static androidx.core.graphics.drawable.IconCompat! createWithResource(android.content.Context!, @DrawableRes int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.graphics.drawable.IconCompat! createWithResource(android.content.res.Resources!, String!, @DrawableRes int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.Bitmap? getBitmap();
+    method @IdRes public int getResId();
+    method public String getResPackage();
+    method public int getType();
+    method public android.net.Uri getUri();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.io.InputStream? getUriInputStream(android.content.Context);
+    method public android.graphics.drawable.Drawable? loadDrawable(android.content.Context);
+    method public androidx.core.graphics.drawable.IconCompat! setTint(@ColorInt int);
+    method public androidx.core.graphics.drawable.IconCompat! setTintList(android.content.res.ColorStateList!);
+    method public androidx.core.graphics.drawable.IconCompat! setTintMode(android.graphics.PorterDuff.Mode!);
+    method public android.os.Bundle toBundle();
+    method @Deprecated @RequiresApi(23) public android.graphics.drawable.Icon toIcon();
+    method @RequiresApi(23) public android.graphics.drawable.Icon toIcon(android.content.Context?);
+    field public static final int TYPE_ADAPTIVE_BITMAP = 5; // 0x5
+    field public static final int TYPE_BITMAP = 1; // 0x1
+    field public static final int TYPE_DATA = 3; // 0x3
+    field public static final int TYPE_RESOURCE = 2; // 0x2
+    field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+    field public static final int TYPE_URI = 4; // 0x4
+    field public static final int TYPE_URI_ADAPTIVE_BITMAP = 6; // 0x6
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.ParcelField(value=1, defaultValue="androidx.core.graphics.drawable.IconCompat.TYPE_UNKNOWN") public int mType;
+  }
+
+  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.Bitmap? getBitmap();
+    method public float getCornerRadius();
+    method public int getGravity();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public boolean hasAntiAlias();
+    method public boolean hasMipMap();
+    method public boolean isCircular();
+    method public void setAlpha(int);
+    method public void setAntiAlias(boolean);
+    method public void setCircular(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setCornerRadius(float);
+    method public void setDither(boolean);
+    method public void setGravity(int);
+    method public void setMipMap(boolean);
+    method public void setTargetDensity(android.graphics.Canvas);
+    method public void setTargetDensity(android.util.DisplayMetrics);
+    method public void setTargetDensity(int);
+  }
+
+  public final class RoundedBitmapDrawableFactory {
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap?);
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, String);
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface TintAwareDrawable {
+    method public void setTint(@ColorInt int);
+    method public void setTintList(android.content.res.ColorStateList!);
+    method public void setTintMode(android.graphics.PorterDuff.Mode!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface WrappedDrawable {
+    method public android.graphics.drawable.Drawable! getWrappedDrawable();
+    method public void setWrappedDrawable(android.graphics.drawable.Drawable!);
+  }
+
+}
+
+package androidx.core.hardware.display {
+
+  public final class DisplayManagerCompat {
+    method public android.view.Display? getDisplay(int);
+    method public android.view.Display![] getDisplays();
+    method public android.view.Display![] getDisplays(String?);
+    method public static androidx.core.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+    field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+  }
+
+}
+
+package androidx.core.hardware.fingerprint {
+
+  @Deprecated public class FingerprintManagerCompat {
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public void authenticate(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject?, int, androidx.core.os.CancellationSignal?, androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler?);
+    method @Deprecated public static androidx.core.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean hasEnrolledFingerprints();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean isHardwareDetected();
+  }
+
+  @Deprecated public abstract static class FingerprintManagerCompat.AuthenticationCallback {
+    ctor @Deprecated public FingerprintManagerCompat.AuthenticationCallback();
+    method @Deprecated public void onAuthenticationError(int, CharSequence!);
+    method @Deprecated public void onAuthenticationFailed();
+    method @Deprecated public void onAuthenticationHelp(int, CharSequence!);
+    method @Deprecated public void onAuthenticationSucceeded(androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult!);
+  }
+
+  @Deprecated public static final class FingerprintManagerCompat.AuthenticationResult {
+    ctor @Deprecated public FingerprintManagerCompat.AuthenticationResult(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject!);
+    method @Deprecated public androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject! getCryptoObject();
+  }
+
+  @Deprecated public static class FingerprintManagerCompat.CryptoObject {
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+    method @Deprecated public javax.crypto.Cipher? getCipher();
+    method @Deprecated public javax.crypto.Mac? getMac();
+    method @Deprecated public java.security.Signature? getSignature();
+  }
+
+}
+
+package androidx.core.internal.view {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface SupportMenu extends android.view.Menu {
+    method public void setGroupDividerEnabled(boolean);
+    field public static final int CATEGORY_MASK = -65536; // 0xffff0000
+    field public static final int CATEGORY_SHIFT = 16; // 0x10
+    field public static final int FLAG_KEEP_OPEN_ON_SUBMENU_OPENED = 4; // 0x4
+    field public static final int SUPPORTED_MODIFIERS_MASK = 69647; // 0x1100f
+    field public static final int USER_MASK = 65535; // 0xffff
+    field public static final int USER_SHIFT = 0; // 0x0
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface SupportMenuItem extends android.view.MenuItem {
+    method public int getAlphabeticModifiers();
+    method public CharSequence! getContentDescription();
+    method public android.content.res.ColorStateList! getIconTintList();
+    method public android.graphics.PorterDuff.Mode! getIconTintMode();
+    method public int getNumericModifiers();
+    method public androidx.core.view.ActionProvider! getSupportActionProvider();
+    method public CharSequence! getTooltipText();
+    method public boolean requiresActionButton();
+    method public boolean requiresOverflow();
+    method public android.view.MenuItem! setAlphabeticShortcut(char, int);
+    method public androidx.core.internal.view.SupportMenuItem! setContentDescription(CharSequence!);
+    method public android.view.MenuItem! setIconTintList(android.content.res.ColorStateList!);
+    method public android.view.MenuItem! setIconTintMode(android.graphics.PorterDuff.Mode!);
+    method public android.view.MenuItem! setNumericShortcut(char, int);
+    method public android.view.MenuItem! setShortcut(char, char, int, int);
+    method public androidx.core.internal.view.SupportMenuItem! setSupportActionProvider(androidx.core.view.ActionProvider!);
+    method public androidx.core.internal.view.SupportMenuItem! setTooltipText(CharSequence!);
+    field public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface SupportSubMenu extends androidx.core.internal.view.SupportMenu android.view.SubMenu {
+  }
+
+}
+
+package androidx.core.location {
+
+  public abstract class GnssStatusCompat {
+    method @FloatRange(from=0, to=360) public abstract float getAzimuthDegrees(@IntRange(from=0) int);
+    method @FloatRange(from=0, to=63) public abstract float getBasebandCn0DbHz(@IntRange(from=0) int);
+    method @FloatRange(from=0) public abstract float getCarrierFrequencyHz(@IntRange(from=0) int);
+    method @FloatRange(from=0, to=63) public abstract float getCn0DbHz(@IntRange(from=0) int);
+    method public abstract int getConstellationType(@IntRange(from=0) int);
+    method @FloatRange(from=0xffffffa6, to=90) public abstract float getElevationDegrees(@IntRange(from=0) int);
+    method @IntRange(from=0) public abstract int getSatelliteCount();
+    method @IntRange(from=1, to=200) public abstract int getSvid(@IntRange(from=0) int);
+    method public abstract boolean hasAlmanacData(@IntRange(from=0) int);
+    method public abstract boolean hasBasebandCn0DbHz(@IntRange(from=0) int);
+    method public abstract boolean hasCarrierFrequencyHz(@IntRange(from=0) int);
+    method public abstract boolean hasEphemerisData(@IntRange(from=0) int);
+    method public abstract boolean usedInFix(@IntRange(from=0) int);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) public static androidx.core.location.GnssStatusCompat wrap(android.location.GnssStatus);
+    method public static androidx.core.location.GnssStatusCompat wrap(android.location.GpsStatus);
+    field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
+    field public static final int CONSTELLATION_GALILEO = 6; // 0x6
+    field public static final int CONSTELLATION_GLONASS = 3; // 0x3
+    field public static final int CONSTELLATION_GPS = 1; // 0x1
+    field public static final int CONSTELLATION_IRNSS = 7; // 0x7
+    field public static final int CONSTELLATION_QZSS = 4; // 0x4
+    field public static final int CONSTELLATION_SBAS = 2; // 0x2
+    field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
+  }
+
+  public abstract static class GnssStatusCompat.Callback {
+    ctor public GnssStatusCompat.Callback();
+    method public void onFirstFix(@IntRange(from=0) int);
+    method public void onSatelliteStatusChanged(androidx.core.location.GnssStatusCompat);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public final class LocationManagerCompat {
+    method public static boolean isLocationEnabled(android.location.LocationManager);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback, android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, java.util.concurrent.Executor, androidx.core.location.GnssStatusCompat.Callback);
+    method public static void unregisterGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback);
+  }
+
+}
+
+package androidx.core.math {
+
+  public class MathUtils {
+    method public static float clamp(float, float, float);
+    method public static double clamp(double, double, double);
+    method public static int clamp(int, int, int);
+    method public static long clamp(long, long, long);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class ConnectivityManagerCompat {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static android.net.NetworkInfo? getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+    method @androidx.core.net.ConnectivityManagerCompat.RestrictBackgroundStatus public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+  }
+
+  @IntDef({androidx.core.net.ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_DISABLED, androidx.core.net.ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_WHITELISTED, androidx.core.net.ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_ENABLED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ConnectivityManagerCompat.RestrictBackgroundStatus {
+  }
+
+  public final class MailTo {
+    method public String? getBcc();
+    method public String? getBody();
+    method public String? getCc();
+    method public java.util.Map<java.lang.String!,java.lang.String!>? getHeaders();
+    method public String? getSubject();
+    method public String? getTo();
+    method public static boolean isMailTo(String?);
+    method public static boolean isMailTo(android.net.Uri?);
+    method public static androidx.core.net.MailTo parse(String) throws androidx.core.net.ParseException;
+    method public static androidx.core.net.MailTo parse(android.net.Uri) throws androidx.core.net.ParseException;
+    field public static final String MAILTO_SCHEME = "mailto:";
+  }
+
+  public class ParseException extends java.lang.RuntimeException {
+    field public final String response;
+  }
+
+  public final class TrafficStatsCompat {
+    method @Deprecated public static void clearThreadStatsTag();
+    method @Deprecated public static int getThreadStatsTag();
+    method @Deprecated public static void incrementOperationCount(int);
+    method @Deprecated public static void incrementOperationCount(int, int);
+    method @Deprecated public static void setThreadStatsTag(int);
+    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method @Deprecated public static void tagSocket(java.net.Socket!) throws java.net.SocketException;
+    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method @Deprecated public static void untagSocket(java.net.Socket!) throws java.net.SocketException;
+  }
+
+  public final class UriCompat {
+    method public static String toSafeString(android.net.Uri);
+  }
+
+}
+
+package androidx.core.os {
+
+  public class BuildCompat {
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N) public static boolean isAtLeastN();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N_MR1) public static boolean isAtLeastNMR1();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O) public static boolean isAtLeastO();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O_MR1) public static boolean isAtLeastOMR1();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.P) public static boolean isAtLeastP();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
+    method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
+  }
+
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
+    method public void cancel();
+    method public Object? getCancellationSignalObject();
+    method public boolean isCanceled();
+    method public void setOnCancelListener(androidx.core.os.CancellationSignal.OnCancelListener?);
+    method public void throwIfCanceled();
+  }
+
+  public static interface CancellationSignal.OnCancelListener {
+    method public void onCancel();
+  }
+
+  public final class ConfigurationCompat {
+    method public static androidx.core.os.LocaleListCompat getLocales(android.content.res.Configuration);
+  }
+
+  public final class EnvironmentCompat {
+    method public static String getStorageState(java.io.File);
+    field public static final String MEDIA_UNKNOWN = "unknown";
+  }
+
+  public final class HandlerCompat {
+    method public static android.os.Handler createAsync(android.os.Looper);
+    method public static android.os.Handler createAsync(android.os.Looper, android.os.Handler.Callback);
+    method public static boolean postDelayed(android.os.Handler, Runnable, Object?, long);
+  }
+
+  public class HandlerExecutor implements java.util.concurrent.Executor {
+    ctor public HandlerExecutor(android.os.Handler);
+    method public void execute(Runnable);
+  }
+
+  public final class LocaleListCompat {
+    method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
+    method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
+    method public java.util.Locale! get(int);
+    method @Size(min=1) public static androidx.core.os.LocaleListCompat getAdjustedDefault();
+    method @Size(min=1) public static androidx.core.os.LocaleListCompat getDefault();
+    method public static androidx.core.os.LocaleListCompat getEmptyLocaleList();
+    method public java.util.Locale? getFirstMatch(String![]);
+    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale!);
+    method public boolean isEmpty();
+    method @IntRange(from=0) public int size();
+    method public String toLanguageTags();
+    method public Object? unwrap();
+    method @Deprecated @RequiresApi(24) public static androidx.core.os.LocaleListCompat! wrap(Object!);
+    method @RequiresApi(24) public static androidx.core.os.LocaleListCompat wrap(android.os.LocaleList);
+  }
+
+  public final class MessageCompat {
+    method public static boolean isAsynchronous(android.os.Message);
+    method public static void setAsynchronous(android.os.Message, boolean);
+  }
+
+  public class OperationCanceledException extends java.lang.RuntimeException {
+    ctor public OperationCanceledException();
+    ctor public OperationCanceledException(String?);
+  }
+
+  public final class ParcelCompat {
+    method public static boolean readBoolean(android.os.Parcel);
+    method public static void writeBoolean(android.os.Parcel, boolean);
+  }
+
+  @Deprecated public final class ParcelableCompat {
+    method @Deprecated public static <T> android.os.Parcelable.Creator<T!>! newCreator(androidx.core.os.ParcelableCompatCreatorCallbacks<T!>!);
+  }
+
+  @Deprecated public interface ParcelableCompatCreatorCallbacks<T> {
+    method @Deprecated public T! createFromParcel(android.os.Parcel!, ClassLoader!);
+    method @Deprecated public T![]! newArray(int);
+  }
+
+  public final class ProcessCompat {
+    method public static boolean isApplicationUid(int);
+  }
+
+  @Deprecated public final class TraceCompat {
+    method @Deprecated public static void beginAsyncSection(String, int);
+    method @Deprecated public static void beginSection(String);
+    method @Deprecated public static void endAsyncSection(String, int);
+    method @Deprecated public static void endSection();
+    method @Deprecated public static boolean isEnabled();
+    method @Deprecated public static void setCounter(String, int);
+  }
+
+  public class UserManagerCompat {
+    method public static boolean isUserUnlocked(android.content.Context);
+  }
+
+}
+
+package androidx.core.provider {
+
+  public final class FontRequest {
+    ctor public FontRequest(String, String, String, java.util.List<java.util.List<byte[]!>!>);
+    ctor public FontRequest(String, String, String, @ArrayRes int);
+    method public java.util.List<java.util.List<byte[]!>!>? getCertificates();
+    method @ArrayRes public int getCertificatesArrayResId();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public String! getIdentifier();
+    method public String getProviderAuthority();
+    method public String getProviderPackage();
+    method public String getQuery();
+  }
+
+  public class FontsContractCompat {
+    method public static android.graphics.Typeface? buildTypeface(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![]);
+    method public static androidx.core.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface! getFontSync(android.content.Context!, androidx.core.provider.FontRequest!, androidx.core.content.res.ResourcesCompat.FontCallback?, android.os.Handler?, boolean, int, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @VisibleForTesting public static android.content.pm.ProviderInfo? getProvider(android.content.pm.PackageManager, androidx.core.provider.FontRequest, android.content.res.Resources?) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @RequiresApi(19) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static java.util.Map<android.net.Uri!,java.nio.ByteBuffer!>! prepareFontData(android.content.Context!, androidx.core.provider.FontsContractCompat.FontInfo![]!, android.os.CancellationSignal!);
+    method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void resetCache();
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final String PARCEL_FONT_RESULTS = "font_results";
+  }
+
+  public static final class FontsContractCompat.Columns implements android.provider.BaseColumns {
+    ctor public FontsContractCompat.Columns();
+    field public static final String FILE_ID = "file_id";
+    field public static final String ITALIC = "font_italic";
+    field public static final String RESULT_CODE = "result_code";
+    field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3
+    field public static final int RESULT_CODE_OK = 0; // 0x0
+    field public static final String TTC_INDEX = "font_ttc_index";
+    field public static final String VARIATION_SETTINGS = "font_variation_settings";
+    field public static final String WEIGHT = "font_weight";
+  }
+
+  public static class FontsContractCompat.FontFamilyResult {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public FontsContractCompat.FontFamilyResult(int, androidx.core.provider.FontsContractCompat.FontInfo![]?);
+    method public androidx.core.provider.FontsContractCompat.FontInfo![]! getFonts();
+    method public int getStatusCode();
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
+    field public static final int STATUS_WRONG_CERTIFICATES = 1; // 0x1
+  }
+
+  public static class FontsContractCompat.FontInfo {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public FontsContractCompat.FontInfo(android.net.Uri, @IntRange(from=0) int, @IntRange(from=1, to=1000) int, boolean, int);
+    method public int getResultCode();
+    method @IntRange(from=0) public int getTtcIndex();
+    method public android.net.Uri getUri();
+    method @IntRange(from=1, to=1000) public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static class FontsContractCompat.FontRequestCallback {
+    ctor public FontsContractCompat.FontRequestCallback();
+    method public void onTypefaceRequestFailed(@androidx.core.provider.FontsContractCompat.FontRequestCallback.FontRequestFailReason int);
+    method public void onTypefaceRetrieved(android.graphics.Typeface!);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+    field public static final int FAIL_REASON_SECURITY_VIOLATION = -4; // 0xfffffffc
+    field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final int RESULT_OK = 0; // 0x0
+  }
+
+  @IntDef({androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_FONT_UNAVAILABLE, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_MALFORMED_QUERY, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_WRONG_CERTIFICATES, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_SECURITY_VIOLATION, androidx.core.provider.FontsContractCompat.FontRequestCallback.RESULT_OK}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface FontsContractCompat.FontRequestCallback.FontRequestFailReason {
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class SelfDestructiveThread {
+    ctor public SelfDestructiveThread(String!, int, int);
+    method @VisibleForTesting public int getGeneration();
+    method @VisibleForTesting public boolean isRunning();
+    method public <T> void postAndReply(java.util.concurrent.Callable<T!>!, androidx.core.provider.SelfDestructiveThread.ReplyCallback<T!>!);
+    method public <T> T! postAndWait(java.util.concurrent.Callable<T!>!, int) throws java.lang.InterruptedException;
+  }
+
+  public static interface SelfDestructiveThread.ReplyCallback<T> {
+    method public void onReply(T!);
+  }
+
+}
+
+package androidx.core.telephony.mbms {
+
+  public final class MbmsHelper {
+    method public static CharSequence? getBestNameForService(android.content.Context, android.telephony.mbms.ServiceInfo);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class BidiFormatter {
+    method public static androidx.core.text.BidiFormatter! getInstance();
+    method public static androidx.core.text.BidiFormatter! getInstance(boolean);
+    method public static androidx.core.text.BidiFormatter! getInstance(java.util.Locale!);
+    method public boolean getStereoReset();
+    method public boolean isRtl(String!);
+    method public boolean isRtl(CharSequence!);
+    method public boolean isRtlContext();
+    method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+    method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+    method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!);
+    method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!);
+    method public String! unicodeWrap(String!, boolean);
+    method public CharSequence! unicodeWrap(CharSequence!, boolean);
+    method public String! unicodeWrap(String!);
+    method public CharSequence! unicodeWrap(CharSequence!);
+  }
+
+  public static final class BidiFormatter.Builder {
+    ctor public BidiFormatter.Builder();
+    ctor public BidiFormatter.Builder(boolean);
+    ctor public BidiFormatter.Builder(java.util.Locale!);
+    method public androidx.core.text.BidiFormatter! build();
+    method public androidx.core.text.BidiFormatter.Builder! setTextDirectionHeuristic(androidx.core.text.TextDirectionHeuristicCompat!);
+    method public androidx.core.text.BidiFormatter.Builder! stereoReset(boolean);
+  }
+
+  public final class HtmlCompat {
+    method public static android.text.Spanned fromHtml(String, int);
+    method public static android.text.Spanned fromHtml(String, int, android.text.Html.ImageGetter?, android.text.Html.TagHandler?);
+    method public static String toHtml(android.text.Spanned, int);
+    field public static final int FROM_HTML_MODE_COMPACT = 63; // 0x3f
+    field public static final int FROM_HTML_MODE_LEGACY = 0; // 0x0
+    field public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 256; // 0x100
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 32; // 0x20
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 16; // 0x10
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 2; // 0x2
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 8; // 0x8
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 4; // 0x4
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 1; // 0x1
+    field public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0; // 0x0
+    field public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 1; // 0x1
+  }
+
+  public final class ICUCompat {
+    method public static String? maximizeAndGetScript(java.util.Locale!);
+  }
+
+  public class PrecomputedTextCompat implements android.text.Spannable {
+    method public char charAt(int);
+    method public static androidx.core.text.PrecomputedTextCompat! create(CharSequence, androidx.core.text.PrecomputedTextCompat.Params);
+    method @IntRange(from=0) public int getParagraphCount();
+    method @IntRange(from=0) public int getParagraphEnd(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getParagraphStart(@IntRange(from=0) int);
+    method public androidx.core.text.PrecomputedTextCompat.Params getParams();
+    method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.text.PrecomputedText? getPrecomputedText();
+    method public int getSpanEnd(Object!);
+    method public int getSpanFlags(Object!);
+    method public int getSpanStart(Object!);
+    method public <T> T![]! getSpans(int, int, Class<T!>!);
+    method @UiThread public static java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>! getTextFuture(CharSequence, androidx.core.text.PrecomputedTextCompat.Params, java.util.concurrent.Executor?);
+    method public int length();
+    method public int nextSpanTransition(int, int, Class!);
+    method public void removeSpan(Object!);
+    method public void setSpan(Object!, int, int, int);
+    method public CharSequence! subSequence(int, int);
+  }
+
+  public static final class PrecomputedTextCompat.Params {
+    ctor @RequiresApi(28) public PrecomputedTextCompat.Params(android.text.PrecomputedText.Params);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean equalsWithoutTextDirection(androidx.core.text.PrecomputedTextCompat.Params);
+    method @RequiresApi(23) public int getBreakStrategy();
+    method @RequiresApi(23) public int getHyphenationFrequency();
+    method @RequiresApi(18) public android.text.TextDirectionHeuristic? getTextDirection();
+    method public android.text.TextPaint getTextPaint();
+  }
+
+  public static class PrecomputedTextCompat.Params.Builder {
+    ctor public PrecomputedTextCompat.Params.Builder(android.text.TextPaint);
+    method public androidx.core.text.PrecomputedTextCompat.Params build();
+    method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setBreakStrategy(int);
+    method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setHyphenationFrequency(int);
+    method @RequiresApi(18) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setTextDirection(android.text.TextDirectionHeuristic);
+  }
+
+  public interface TextDirectionHeuristicCompat {
+    method public boolean isRtl(char[]!, int, int);
+    method public boolean isRtl(CharSequence!, int, int);
+  }
+
+  public final class TextDirectionHeuristicsCompat {
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! ANYRTL_LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_RTL;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! LOCALE;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! RTL;
+  }
+
+  public final class TextUtilsCompat {
+    method public static int getLayoutDirectionFromLocale(java.util.Locale?);
+    method public static String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.text.util {
+
+  public final class LinkifyCompat {
+    method public static boolean addLinks(android.text.Spannable, @androidx.core.text.util.LinkifyCompat.LinkifyMask int);
+    method public static boolean addLinks(android.widget.TextView, @androidx.core.text.util.LinkifyCompat.LinkifyMask int);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+  }
+
+  @IntDef(flag=true, value={android.text.util.Linkify.WEB_URLS, android.text.util.Linkify.EMAIL_ADDRESSES, android.text.util.Linkify.PHONE_NUMBERS, android.text.util.Linkify.MAP_ADDRESSES, android.text.util.Linkify.ALL}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface LinkifyCompat.LinkifyMask {
+  }
+
+}
+
+package androidx.core.util {
+
+  public class AtomicFile {
+    ctor public AtomicFile(java.io.File);
+    method public void delete();
+    method public void failWrite(java.io.FileOutputStream?);
+    method public void finishWrite(java.io.FileOutputStream?);
+    method public java.io.File getBaseFile();
+    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+    method public byte[] readFully() throws java.io.IOException;
+    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+  }
+
+  public interface Consumer<T> {
+    method public void accept(T!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DebugUtils {
+    method public static void buildShortClassTag(Object!, StringBuilder!);
+  }
+
+  @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class LogWriter extends java.io.Writer {
+    ctor @Deprecated public LogWriter(String!);
+    method @Deprecated public void close();
+    method @Deprecated public void flush();
+    method @Deprecated public void write(char[]!, int, int);
+  }
+
+  public class ObjectsCompat {
+    method public static boolean equals(Object?, Object?);
+    method public static int hash(java.lang.Object!...);
+    method public static int hashCode(Object?);
+    method public static String? toString(Object?, String?);
+  }
+
+  public class Pair<F, S> {
+    ctor public Pair(F!, S!);
+    method public static <A, B> androidx.core.util.Pair<A!,B!> create(A!, B!);
+    field public final F! first;
+    field public final S! second;
+  }
+
+  public final class PatternsCompat {
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final java.util.regex.Pattern AUTOLINK_EMAIL_ADDRESS;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final java.util.regex.Pattern AUTOLINK_WEB_URL;
+    field public static final java.util.regex.Pattern DOMAIN_NAME;
+    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field public static final java.util.regex.Pattern WEB_URL;
+  }
+
+  public final class Pools {
+  }
+
+  public static interface Pools.Pool<T> {
+    method public T? acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SimplePool<T> implements androidx.core.util.Pools.Pool<T> {
+    ctor public Pools.SimplePool(int);
+    method public T! acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SynchronizedPool<T> extends androidx.core.util.Pools.SimplePool<T> {
+    ctor public Pools.SynchronizedPool(int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class Preconditions {
+    method public static void checkArgument(boolean);
+    method public static void checkArgument(boolean, Object);
+    method public static int checkArgumentInRange(int, int, int, String);
+    method @IntRange(from=0) public static int checkArgumentNonnegative(int, String?);
+    method @IntRange(from=0) public static int checkArgumentNonnegative(int);
+    method public static int checkFlagsArgument(int, int);
+    method public static <T> T checkNotNull(T?);
+    method public static <T> T checkNotNull(T?, Object);
+    method public static void checkState(boolean, String?);
+    method public static void checkState(boolean);
+    method public static <T extends java.lang.CharSequence> T checkStringNotEmpty(T?);
+    method public static <T extends java.lang.CharSequence> T checkStringNotEmpty(T?, Object);
+    method public static <T extends java.lang.CharSequence> T checkStringNotEmpty(T?, String, java.lang.Object!...);
+  }
+
+  public interface Predicate<T> {
+    method public boolean test(T!);
+  }
+
+  public interface Supplier<T> {
+    method public T! get();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class TimeUtils {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void formatDuration(long, StringBuilder!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void formatDuration(long, java.io.PrintWriter!, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void formatDuration(long, java.io.PrintWriter!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void formatDuration(long, long, java.io.PrintWriter!);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final int HUNDRED_DAY_FIELD_LEN = 19; // 0x13
+  }
+
+}
+
+package androidx.core.view {
+
+  public class AccessibilityDelegateCompat {
+    ctor public AccessibilityDelegateCompat();
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public AccessibilityDelegateCompat(android.view.View.AccessibilityDelegate!);
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat! getAccessibilityNodeProvider(android.view.View!);
+    method public void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public void onInitializeAccessibilityNodeInfo(android.view.View!, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method public void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public boolean performAccessibilityAction(android.view.View!, int, android.os.Bundle!);
+    method public void sendAccessibilityEvent(android.view.View!, int);
+    method public void sendAccessibilityEventUnchecked(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+  }
+
+  public abstract class ActionProvider {
+    ctor public ActionProvider(android.content.Context!);
+    method public android.content.Context! getContext();
+    method public boolean hasSubMenu();
+    method public boolean isVisible();
+    method public abstract android.view.View! onCreateActionView();
+    method public android.view.View! onCreateActionView(android.view.MenuItem!);
+    method public boolean onPerformDefaultAction();
+    method public void onPrepareSubMenu(android.view.SubMenu!);
+    method public boolean overridesItemVisibility();
+    method public void refreshVisibility();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void reset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSubUiVisibilityListener(androidx.core.view.ActionProvider.SubUiVisibilityListener!);
+    method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void subUiVisibilityChanged(boolean);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface ActionProvider.SubUiVisibilityListener {
+    method public void onSubUiVisibilityChanged(boolean);
+  }
+
+  public static interface ActionProvider.VisibilityListener {
+    method public void onActionProviderVisibilityChanged(boolean);
+  }
+
+  public final class ContentInfoCompat {
+    method public android.content.ClipData getClip();
+    method public android.os.Bundle? getExtras();
+    method @androidx.core.view.ContentInfoCompat.Flags public int getFlags();
+    method public android.net.Uri? getLinkUri();
+    method @androidx.core.view.ContentInfoCompat.Source public int getSource();
+    method public android.util.Pair<androidx.core.view.ContentInfoCompat!,androidx.core.view.ContentInfoCompat!> partition(androidx.core.util.Predicate<android.content.ClipData.Item!>);
+    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+  }
+
+  public static final class ContentInfoCompat.Builder {
+    ctor public ContentInfoCompat.Builder(androidx.core.view.ContentInfoCompat);
+    ctor public ContentInfoCompat.Builder(android.content.ClipData, @androidx.core.view.ContentInfoCompat.Source int);
+    method public androidx.core.view.ContentInfoCompat build();
+    method public androidx.core.view.ContentInfoCompat.Builder setClip(android.content.ClipData);
+    method public androidx.core.view.ContentInfoCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.view.ContentInfoCompat.Builder setFlags(@androidx.core.view.ContentInfoCompat.Flags int);
+    method public androidx.core.view.ContentInfoCompat.Builder setLinkUri(android.net.Uri?);
+    method public androidx.core.view.ContentInfoCompat.Builder setSource(@androidx.core.view.ContentInfoCompat.Source int);
+  }
+
+  @IntDef(flag=true, value={androidx.core.view.ContentInfoCompat.FLAG_CONVERT_TO_PLAIN_TEXT}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ContentInfoCompat.Flags {
+  }
+
+  @IntDef({androidx.core.view.ContentInfoCompat.SOURCE_APP, androidx.core.view.ContentInfoCompat.SOURCE_CLIPBOARD, androidx.core.view.ContentInfoCompat.SOURCE_INPUT_METHOD, androidx.core.view.ContentInfoCompat.SOURCE_DRAG_AND_DROP}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ContentInfoCompat.Source {
+  }
+
+  public final class DisplayCompat {
+    method public static androidx.core.view.DisplayCompat.ModeCompat![] getSupportedModes(android.content.Context, android.view.Display);
+  }
+
+  public static final class DisplayCompat.ModeCompat {
+    method public int getPhysicalHeight();
+    method public int getPhysicalWidth();
+    method public boolean isNative();
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public android.view.Display.Mode? toMode();
+  }
+
+  public final class DisplayCutoutCompat {
+    ctor public DisplayCutoutCompat(android.graphics.Rect!, java.util.List<android.graphics.Rect!>!);
+    ctor public DisplayCutoutCompat(androidx.core.graphics.Insets, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, androidx.core.graphics.Insets);
+    method public java.util.List<android.graphics.Rect!> getBoundingRects();
+    method public int getSafeInsetBottom();
+    method public int getSafeInsetLeft();
+    method public int getSafeInsetRight();
+    method public int getSafeInsetTop();
+    method public androidx.core.graphics.Insets getWaterfallInsets();
+  }
+
+  public final class DragAndDropPermissionsCompat {
+    method public void release();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.view.DragAndDropPermissionsCompat? request(android.app.Activity!, android.view.DragEvent!);
+  }
+
+  public class DragStartHelper {
+    ctor public DragStartHelper(android.view.View!, androidx.core.view.DragStartHelper.OnDragStartListener!);
+    method public void attach();
+    method public void detach();
+    method public void getTouchPosition(android.graphics.Point!);
+    method public boolean onLongClick(android.view.View!);
+    method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+  }
+
+  public static interface DragStartHelper.OnDragStartListener {
+    method public boolean onDragStart(android.view.View!, androidx.core.view.DragStartHelper!);
+  }
+
+  public final class GestureDetectorCompat {
+    ctor public GestureDetectorCompat(android.content.Context!, android.view.GestureDetector.OnGestureListener!);
+    ctor public GestureDetectorCompat(android.content.Context!, android.view.GestureDetector.OnGestureListener!, android.os.Handler!);
+    method public boolean isLongpressEnabled();
+    method public boolean onTouchEvent(android.view.MotionEvent!);
+    method public void setIsLongpressEnabled(boolean);
+    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener!);
+  }
+
+  public final class GravityCompat {
+    method public static void apply(int, int, int, android.graphics.Rect!, android.graphics.Rect!, int);
+    method public static void apply(int, int, int, android.graphics.Rect!, int, int, android.graphics.Rect!, int);
+    method public static void applyDisplay(int, android.graphics.Rect!, android.graphics.Rect!, int);
+    method public static int getAbsoluteGravity(int, int);
+    field public static final int END = 8388613; // 0x800005
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+    field public static final int START = 8388611; // 0x800003
+  }
+
+  public final class InputDeviceCompat {
+    field public static final int SOURCE_ANY = -256; // 0xffffff00
+    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+    field public static final int SOURCE_DPAD = 513; // 0x201
+    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+    field public static final int SOURCE_KEYBOARD = 257; // 0x101
+    field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+    field public static final int SOURCE_STYLUS = 16386; // 0x4002
+    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+    field public static final int SOURCE_UNKNOWN = 0; // 0x0
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class KeyEventDispatcher {
+    method public static boolean dispatchBeforeHierarchy(android.view.View, android.view.KeyEvent);
+    method public static boolean dispatchKeyEvent(androidx.core.view.KeyEventDispatcher.Component, android.view.View?, android.view.Window.Callback?, android.view.KeyEvent);
+  }
+
+  public static interface KeyEventDispatcher.Component {
+    method public boolean superDispatchKeyEvent(android.view.KeyEvent!);
+  }
+
+  public final class LayoutInflaterCompat {
+    method @Deprecated public static androidx.core.view.LayoutInflaterFactory! getFactory(android.view.LayoutInflater!);
+    method @Deprecated public static void setFactory(android.view.LayoutInflater, androidx.core.view.LayoutInflaterFactory);
+    method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+  }
+
+  @Deprecated public interface LayoutInflaterFactory {
+    method @Deprecated public android.view.View! onCreateView(android.view.View!, String!, android.content.Context!, android.util.AttributeSet!);
+  }
+
+  public final class MarginLayoutParamsCompat {
+    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams!);
+    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams!);
+    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams!);
+    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams!);
+    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams!, int);
+    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams!, int);
+    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams!, int);
+    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams!, int);
+  }
+
+  public final class MenuCompat {
+    method public static void setGroupDividerEnabled(android.view.Menu!, boolean);
+    method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+  }
+
+  public final class MenuItemCompat {
+    method @Deprecated public static boolean collapseActionView(android.view.MenuItem!);
+    method @Deprecated public static boolean expandActionView(android.view.MenuItem!);
+    method public static androidx.core.view.ActionProvider! getActionProvider(android.view.MenuItem!);
+    method @Deprecated public static android.view.View! getActionView(android.view.MenuItem!);
+    method public static int getAlphabeticModifiers(android.view.MenuItem!);
+    method public static CharSequence! getContentDescription(android.view.MenuItem!);
+    method public static android.content.res.ColorStateList! getIconTintList(android.view.MenuItem!);
+    method public static android.graphics.PorterDuff.Mode! getIconTintMode(android.view.MenuItem!);
+    method public static int getNumericModifiers(android.view.MenuItem!);
+    method public static CharSequence! getTooltipText(android.view.MenuItem!);
+    method @Deprecated public static boolean isActionViewExpanded(android.view.MenuItem!);
+    method public static android.view.MenuItem! setActionProvider(android.view.MenuItem!, androidx.core.view.ActionProvider!);
+    method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, android.view.View!);
+    method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, int);
+    method public static void setAlphabeticShortcut(android.view.MenuItem!, char, int);
+    method public static void setContentDescription(android.view.MenuItem!, CharSequence!);
+    method public static void setIconTintList(android.view.MenuItem!, android.content.res.ColorStateList!);
+    method public static void setIconTintMode(android.view.MenuItem!, android.graphics.PorterDuff.Mode!);
+    method public static void setNumericShortcut(android.view.MenuItem!, char, int);
+    method @Deprecated public static android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem!, androidx.core.view.MenuItemCompat.OnActionExpandListener!);
+    method public static void setShortcut(android.view.MenuItem!, char, char, int, int);
+    method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+    method public static void setTooltipText(android.view.MenuItem!, CharSequence!);
+    field @Deprecated public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field @Deprecated public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field @Deprecated public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field @Deprecated public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field @Deprecated public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  @Deprecated public static interface MenuItemCompat.OnActionExpandListener {
+    method @Deprecated public boolean onMenuItemActionCollapse(android.view.MenuItem!);
+    method @Deprecated public boolean onMenuItemActionExpand(android.view.MenuItem!);
+  }
+
+  public final class MotionEventCompat {
+    method @Deprecated public static int findPointerIndex(android.view.MotionEvent!, int);
+    method @Deprecated public static int getActionIndex(android.view.MotionEvent!);
+    method @Deprecated public static int getActionMasked(android.view.MotionEvent!);
+    method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int);
+    method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int, int);
+    method @Deprecated public static int getButtonState(android.view.MotionEvent!);
+    method @Deprecated public static int getPointerCount(android.view.MotionEvent!);
+    method @Deprecated public static int getPointerId(android.view.MotionEvent!, int);
+    method @Deprecated public static int getSource(android.view.MotionEvent!);
+    method @Deprecated public static float getX(android.view.MotionEvent!, int);
+    method @Deprecated public static float getY(android.view.MotionEvent!, int);
+    method public static boolean isFromSource(android.view.MotionEvent!, int);
+    field @Deprecated public static final int ACTION_HOVER_ENTER = 9; // 0x9
+    field @Deprecated public static final int ACTION_HOVER_EXIT = 10; // 0xa
+    field @Deprecated public static final int ACTION_HOVER_MOVE = 7; // 0x7
+    field @Deprecated public static final int ACTION_MASK = 255; // 0xff
+    field @Deprecated public static final int ACTION_POINTER_DOWN = 5; // 0x5
+    field @Deprecated public static final int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+    field @Deprecated public static final int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+    field @Deprecated public static final int ACTION_POINTER_UP = 6; // 0x6
+    field @Deprecated public static final int ACTION_SCROLL = 8; // 0x8
+    field @Deprecated public static final int AXIS_BRAKE = 23; // 0x17
+    field @Deprecated public static final int AXIS_DISTANCE = 24; // 0x18
+    field @Deprecated public static final int AXIS_GAS = 22; // 0x16
+    field @Deprecated public static final int AXIS_GENERIC_1 = 32; // 0x20
+    field @Deprecated public static final int AXIS_GENERIC_10 = 41; // 0x29
+    field @Deprecated public static final int AXIS_GENERIC_11 = 42; // 0x2a
+    field @Deprecated public static final int AXIS_GENERIC_12 = 43; // 0x2b
+    field @Deprecated public static final int AXIS_GENERIC_13 = 44; // 0x2c
+    field @Deprecated public static final int AXIS_GENERIC_14 = 45; // 0x2d
+    field @Deprecated public static final int AXIS_GENERIC_15 = 46; // 0x2e
+    field @Deprecated public static final int AXIS_GENERIC_16 = 47; // 0x2f
+    field @Deprecated public static final int AXIS_GENERIC_2 = 33; // 0x21
+    field @Deprecated public static final int AXIS_GENERIC_3 = 34; // 0x22
+    field @Deprecated public static final int AXIS_GENERIC_4 = 35; // 0x23
+    field @Deprecated public static final int AXIS_GENERIC_5 = 36; // 0x24
+    field @Deprecated public static final int AXIS_GENERIC_6 = 37; // 0x25
+    field @Deprecated public static final int AXIS_GENERIC_7 = 38; // 0x26
+    field @Deprecated public static final int AXIS_GENERIC_8 = 39; // 0x27
+    field @Deprecated public static final int AXIS_GENERIC_9 = 40; // 0x28
+    field @Deprecated public static final int AXIS_HAT_X = 15; // 0xf
+    field @Deprecated public static final int AXIS_HAT_Y = 16; // 0x10
+    field @Deprecated public static final int AXIS_HSCROLL = 10; // 0xa
+    field @Deprecated public static final int AXIS_LTRIGGER = 17; // 0x11
+    field @Deprecated public static final int AXIS_ORIENTATION = 8; // 0x8
+    field @Deprecated public static final int AXIS_PRESSURE = 2; // 0x2
+    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+    field @Deprecated public static final int AXIS_RTRIGGER = 18; // 0x12
+    field @Deprecated public static final int AXIS_RUDDER = 20; // 0x14
+    field @Deprecated public static final int AXIS_RX = 12; // 0xc
+    field @Deprecated public static final int AXIS_RY = 13; // 0xd
+    field @Deprecated public static final int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
+    field @Deprecated public static final int AXIS_SIZE = 3; // 0x3
+    field @Deprecated public static final int AXIS_THROTTLE = 19; // 0x13
+    field @Deprecated public static final int AXIS_TILT = 25; // 0x19
+    field @Deprecated public static final int AXIS_TOOL_MAJOR = 6; // 0x6
+    field @Deprecated public static final int AXIS_TOOL_MINOR = 7; // 0x7
+    field @Deprecated public static final int AXIS_TOUCH_MAJOR = 4; // 0x4
+    field @Deprecated public static final int AXIS_TOUCH_MINOR = 5; // 0x5
+    field @Deprecated public static final int AXIS_VSCROLL = 9; // 0x9
+    field @Deprecated public static final int AXIS_WHEEL = 21; // 0x15
+    field @Deprecated public static final int AXIS_X = 0; // 0x0
+    field @Deprecated public static final int AXIS_Y = 1; // 0x1
+    field @Deprecated public static final int AXIS_Z = 11; // 0xb
+    field @Deprecated public static final int BUTTON_PRIMARY = 1; // 0x1
+  }
+
+  public interface NestedScrollingChild {
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+    method public boolean hasNestedScrollingParent();
+    method public boolean isNestedScrollingEnabled();
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(@androidx.core.view.ViewCompat.ScrollAxis int);
+    method public void stopNestedScroll();
+  }
+
+  public interface NestedScrollingChild2 extends androidx.core.view.NestedScrollingChild {
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean hasNestedScrollingParent(@androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean startNestedScroll(@androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void stopNestedScroll(@androidx.core.view.ViewCompat.NestedScrollType int);
+  }
+
+  public interface NestedScrollingChild3 extends androidx.core.view.NestedScrollingChild2 {
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int, int[]);
+  }
+
+  public class NestedScrollingChildHelper {
+    ctor public NestedScrollingChildHelper(android.view.View);
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int, int[]?);
+    method public boolean hasNestedScrollingParent();
+    method public boolean hasNestedScrollingParent(@androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean isNestedScrollingEnabled();
+    method public void onDetachedFromWindow();
+    method public void onStopNestedScroll(android.view.View);
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(@androidx.core.view.ViewCompat.ScrollAxis int);
+    method public boolean startNestedScroll(@androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void stopNestedScroll();
+    method public void stopNestedScroll(@androidx.core.view.ViewCompat.NestedScrollType int);
+  }
+
+  public interface NestedScrollingParent {
+    method @androidx.core.view.ViewCompat.ScrollAxis public int getNestedScrollAxes();
+    method public boolean onNestedFling(android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.view.View, float, float);
+    method public void onNestedPreScroll(android.view.View, int, int, int[]);
+    method public void onNestedScroll(android.view.View, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int);
+    method public void onStopNestedScroll(android.view.View);
+  }
+
+  public interface NestedScrollingParent2 extends androidx.core.view.NestedScrollingParent {
+    method public void onNestedPreScroll(android.view.View, int, int, int[], @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void onStopNestedScroll(android.view.View, @androidx.core.view.ViewCompat.NestedScrollType int);
+  }
+
+  public interface NestedScrollingParent3 extends androidx.core.view.NestedScrollingParent2 {
+    method public void onNestedScroll(android.view.View, int, int, int, int, @androidx.core.view.ViewCompat.NestedScrollType int, int[]);
+  }
+
+  public class NestedScrollingParentHelper {
+    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+    method @androidx.core.view.ViewCompat.ScrollAxis public int getNestedScrollAxes();
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void onStopNestedScroll(android.view.View);
+    method public void onStopNestedScroll(android.view.View, @androidx.core.view.ViewCompat.NestedScrollType int);
+  }
+
+  public interface OnApplyWindowInsetsListener {
+    method public androidx.core.view.WindowInsetsCompat! onApplyWindowInsets(android.view.View!, androidx.core.view.WindowInsetsCompat!);
+  }
+
+  public interface OnReceiveContentListener {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+  }
+
+  public interface OnReceiveContentViewBehavior {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+  }
+
+  public final class OneShotPreDrawListener implements android.view.View.OnAttachStateChangeListener android.view.ViewTreeObserver.OnPreDrawListener {
+    method public static androidx.core.view.OneShotPreDrawListener add(android.view.View, Runnable);
+    method public boolean onPreDraw();
+    method public void onViewAttachedToWindow(android.view.View!);
+    method public void onViewDetachedFromWindow(android.view.View!);
+    method public void removeListener();
+  }
+
+  public final class PointerIconCompat {
+    method public static androidx.core.view.PointerIconCompat! create(android.graphics.Bitmap!, float, float);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public Object! getPointerIcon();
+    method public static androidx.core.view.PointerIconCompat! getSystemIcon(android.content.Context!, int);
+    method public static androidx.core.view.PointerIconCompat! load(android.content.res.Resources!, int);
+    field public static final int TYPE_ALIAS = 1010; // 0x3f2
+    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int TYPE_ARROW = 1000; // 0x3e8
+    field public static final int TYPE_CELL = 1006; // 0x3ee
+    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int TYPE_COPY = 1011; // 0x3f3
+    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+    field public static final int TYPE_GRAB = 1020; // 0x3fc
+    field public static final int TYPE_GRABBING = 1021; // 0x3fd
+    field public static final int TYPE_HAND = 1002; // 0x3ea
+    field public static final int TYPE_HELP = 1003; // 0x3eb
+    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_TEXT = 1008; // 0x3f0
+    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int TYPE_WAIT = 1004; // 0x3ec
+    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
+  public final class ScaleGestureDetectorCompat {
+    method @Deprecated public static boolean isQuickScaleEnabled(Object!);
+    method public static boolean isQuickScaleEnabled(android.view.ScaleGestureDetector!);
+    method @Deprecated public static void setQuickScaleEnabled(Object!, boolean);
+    method public static void setQuickScaleEnabled(android.view.ScaleGestureDetector!, boolean);
+  }
+
+  public interface ScrollingView {
+    method public int computeHorizontalScrollExtent();
+    method public int computeHorizontalScrollOffset();
+    method public int computeHorizontalScrollRange();
+    method public int computeVerticalScrollExtent();
+    method public int computeVerticalScrollOffset();
+    method public int computeVerticalScrollRange();
+  }
+
+  public interface TintableBackgroundView {
+    method public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  @Deprecated public final class VelocityTrackerCompat {
+    method @Deprecated public static float getXVelocity(android.view.VelocityTracker!, int);
+    method @Deprecated public static float getYVelocity(android.view.VelocityTracker!, int);
+  }
+
+  public class ViewCompat {
+    ctor @Deprecated protected ViewCompat();
+    method public static int addAccessibilityAction(android.view.View, CharSequence, androidx.core.view.accessibility.AccessibilityViewCommand);
+    method public static void addKeyboardNavigationClusters(android.view.View, java.util.Collection<android.view.View!>, int);
+    method public static void addOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+    method public static androidx.core.view.ViewPropertyAnimatorCompat animate(android.view.View);
+    method @Deprecated public static boolean canScrollHorizontally(android.view.View!, int);
+    method @Deprecated public static boolean canScrollVertically(android.view.View!, int);
+    method public static void cancelDragAndDrop(android.view.View);
+    method @Deprecated public static int combineMeasuredStates(int, int);
+    method public static androidx.core.view.WindowInsetsCompat computeSystemWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat, android.graphics.Rect);
+    method public static androidx.core.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+    method public static void dispatchFinishTemporaryDetach(android.view.View);
+    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?);
+    method public static void dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int, int[]);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public static void dispatchStartTemporaryDetach(android.view.View);
+    method public static void enableAccessibleClickableSpanSupport(android.view.View!);
+    method public static int generateViewId();
+    method public static androidx.core.view.AccessibilityDelegateCompat? getAccessibilityDelegate(android.view.View);
+    method public static int getAccessibilityLiveRegion(android.view.View);
+    method public static androidx.core.view.accessibility.AccessibilityNodeProviderCompat! getAccessibilityNodeProvider(android.view.View);
+    method @UiThread public static CharSequence! getAccessibilityPaneTitle(android.view.View!);
+    method @Deprecated public static float getAlpha(android.view.View!);
+    method public static android.content.res.ColorStateList! getBackgroundTintList(android.view.View);
+    method public static android.graphics.PorterDuff.Mode! getBackgroundTintMode(android.view.View);
+    method public static android.graphics.Rect? getClipBounds(android.view.View);
+    method public static android.view.Display? getDisplay(android.view.View);
+    method public static float getElevation(android.view.View);
+    method public static boolean getFitsSystemWindows(android.view.View);
+    method public static int getImportantForAccessibility(android.view.View);
+    method public static int getImportantForAutofill(android.view.View);
+    method public static int getLabelFor(android.view.View);
+    method @Deprecated public static int getLayerType(android.view.View!);
+    method public static int getLayoutDirection(android.view.View);
+    method @Deprecated public static android.graphics.Matrix? getMatrix(android.view.View!);
+    method @Deprecated public static int getMeasuredHeightAndState(android.view.View!);
+    method @Deprecated public static int getMeasuredState(android.view.View!);
+    method @Deprecated public static int getMeasuredWidthAndState(android.view.View!);
+    method public static int getMinimumHeight(android.view.View);
+    method public static int getMinimumWidth(android.view.View);
+    method public static int getNextClusterForwardId(android.view.View);
+    method public static String![]? getOnReceiveContentMimeTypes(android.view.View);
+    method @Deprecated public static int getOverScrollMode(android.view.View!);
+    method @Px public static int getPaddingEnd(android.view.View);
+    method @Px public static int getPaddingStart(android.view.View);
+    method public static android.view.ViewParent! getParentForAccessibility(android.view.View);
+    method @Deprecated public static float getPivotX(android.view.View!);
+    method @Deprecated public static float getPivotY(android.view.View!);
+    method public static androidx.core.view.WindowInsetsCompat? getRootWindowInsets(android.view.View);
+    method @Deprecated public static float getRotation(android.view.View!);
+    method @Deprecated public static float getRotationX(android.view.View!);
+    method @Deprecated public static float getRotationY(android.view.View!);
+    method @Deprecated public static float getScaleX(android.view.View!);
+    method @Deprecated public static float getScaleY(android.view.View!);
+    method public static int getScrollIndicators(android.view.View);
+    method @UiThread public static final CharSequence? getStateDescription(android.view.View);
+    method public static java.util.List<android.graphics.Rect!> getSystemGestureExclusionRects(android.view.View);
+    method public static String? getTransitionName(android.view.View);
+    method @Deprecated public static float getTranslationX(android.view.View!);
+    method @Deprecated public static float getTranslationY(android.view.View!);
+    method public static float getTranslationZ(android.view.View);
+    method public static androidx.core.view.WindowInsetsControllerCompat? getWindowInsetsController(android.view.View);
+    method public static int getWindowSystemUiVisibility(android.view.View);
+    method @Deprecated public static float getX(android.view.View!);
+    method @Deprecated public static float getY(android.view.View!);
+    method public static float getZ(android.view.View);
+    method public static boolean hasAccessibilityDelegate(android.view.View);
+    method public static boolean hasExplicitFocusable(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public static boolean hasOnClickListeners(android.view.View);
+    method public static boolean hasOverlappingRendering(android.view.View);
+    method public static boolean hasTransientState(android.view.View);
+    method @UiThread public static boolean isAccessibilityHeading(android.view.View!);
+    method public static boolean isAttachedToWindow(android.view.View);
+    method public static boolean isFocusedByDefault(android.view.View);
+    method public static boolean isImportantForAccessibility(android.view.View);
+    method public static boolean isImportantForAutofill(android.view.View);
+    method public static boolean isInLayout(android.view.View);
+    method public static boolean isKeyboardNavigationCluster(android.view.View);
+    method public static boolean isLaidOut(android.view.View);
+    method public static boolean isLayoutDirectionResolved(android.view.View);
+    method public static boolean isNestedScrollingEnabled(android.view.View);
+    method @Deprecated public static boolean isOpaque(android.view.View!);
+    method public static boolean isPaddingRelative(android.view.View);
+    method @UiThread public static boolean isScreenReaderFocusable(android.view.View!);
+    method @Deprecated public static void jumpDrawablesToCurrentState(android.view.View!);
+    method public static android.view.View! keyboardNavigationClusterSearch(android.view.View, android.view.View!, @androidx.core.view.ViewCompat.FocusDirection int);
+    method public static void offsetLeftAndRight(android.view.View, int);
+    method public static void offsetTopAndBottom(android.view.View, int);
+    method public static androidx.core.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+    method @Deprecated public static void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method @Deprecated public static void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle!);
+    method public static androidx.core.view.ContentInfoCompat? performReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+    method public static void postInvalidateOnAnimation(android.view.View);
+    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+    method public static void postOnAnimation(android.view.View, Runnable!);
+    method public static void postOnAnimationDelayed(android.view.View, Runnable!, long);
+    method public static void removeAccessibilityAction(android.view.View, int);
+    method public static void removeOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+    method public static void replaceAccessibilityAction(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat, CharSequence?, androidx.core.view.accessibility.AccessibilityViewCommand?);
+    method public static void requestApplyInsets(android.view.View);
+    method public static <T extends android.view.View> T requireViewById(android.view.View, @IdRes int);
+    method @Deprecated public static int resolveSizeAndState(int, int, int);
+    method public static boolean restoreDefaultFocus(android.view.View);
+    method public static void saveAttributeDataForStyleable(android.view.View, android.content.Context, int[], android.util.AttributeSet?, android.content.res.TypedArray, int, int);
+    method public static void setAccessibilityDelegate(android.view.View, androidx.core.view.AccessibilityDelegateCompat!);
+    method @UiThread public static void setAccessibilityHeading(android.view.View!, boolean);
+    method public static void setAccessibilityLiveRegion(android.view.View, int);
+    method @UiThread public static void setAccessibilityPaneTitle(android.view.View!, CharSequence!);
+    method @Deprecated public static void setActivated(android.view.View!, boolean);
+    method @Deprecated public static void setAlpha(android.view.View!, @FloatRange(from=0.0, to=1.0) float);
+    method public static void setAutofillHints(android.view.View, java.lang.String!...);
+    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable?);
+    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList!);
+    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode!);
+    method @Deprecated public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup!, boolean);
+    method public static void setClipBounds(android.view.View, android.graphics.Rect!);
+    method public static void setElevation(android.view.View, float);
+    method @Deprecated public static void setFitsSystemWindows(android.view.View!, boolean);
+    method public static void setFocusedByDefault(android.view.View, boolean);
+    method public static void setHasTransientState(android.view.View, boolean);
+    method public static void setImportantForAccessibility(android.view.View, int);
+    method public static void setImportantForAutofill(android.view.View, int);
+    method public static void setKeyboardNavigationCluster(android.view.View, boolean);
+    method public static void setLabelFor(android.view.View, @IdRes int);
+    method public static void setLayerPaint(android.view.View, android.graphics.Paint!);
+    method @Deprecated public static void setLayerType(android.view.View!, int, android.graphics.Paint!);
+    method public static void setLayoutDirection(android.view.View, int);
+    method public static void setNestedScrollingEnabled(android.view.View, boolean);
+    method public static void setNextClusterForwardId(android.view.View, int);
+    method public static void setOnApplyWindowInsetsListener(android.view.View, androidx.core.view.OnApplyWindowInsetsListener?);
+    method public static void setOnReceiveContentListener(android.view.View, String![]?, androidx.core.view.OnReceiveContentListener?);
+    method @Deprecated public static void setOverScrollMode(android.view.View!, int);
+    method public static void setPaddingRelative(android.view.View, @Px int, @Px int, @Px int, @Px int);
+    method @Deprecated public static void setPivotX(android.view.View!, float);
+    method @Deprecated public static void setPivotY(android.view.View!, float);
+    method public static void setPointerIcon(android.view.View, androidx.core.view.PointerIconCompat!);
+    method @Deprecated public static void setRotation(android.view.View!, float);
+    method @Deprecated public static void setRotationX(android.view.View!, float);
+    method @Deprecated public static void setRotationY(android.view.View!, float);
+    method @Deprecated public static void setSaveFromParentEnabled(android.view.View!, boolean);
+    method @Deprecated public static void setScaleX(android.view.View!, float);
+    method @Deprecated public static void setScaleY(android.view.View!, float);
+    method @UiThread public static void setScreenReaderFocusable(android.view.View!, boolean);
+    method public static void setScrollIndicators(android.view.View, @androidx.core.view.ViewCompat.ScrollIndicators int);
+    method public static void setScrollIndicators(android.view.View, @androidx.core.view.ViewCompat.ScrollIndicators int, @androidx.core.view.ViewCompat.ScrollIndicators int);
+    method @UiThread public static void setStateDescription(android.view.View, CharSequence?);
+    method public static void setSystemGestureExclusionRects(android.view.View, java.util.List<android.graphics.Rect!>);
+    method public static void setTooltipText(android.view.View, CharSequence?);
+    method public static void setTransitionName(android.view.View, String!);
+    method @Deprecated public static void setTranslationX(android.view.View!, float);
+    method @Deprecated public static void setTranslationY(android.view.View!, float);
+    method public static void setTranslationZ(android.view.View, float);
+    method public static void setWindowInsetsAnimationCallback(android.view.View, androidx.core.view.WindowInsetsAnimationCompat.Callback?);
+    method @Deprecated public static void setX(android.view.View!, float);
+    method @Deprecated public static void setY(android.view.View!, float);
+    method public static void setZ(android.view.View, float);
+    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData!, android.view.View.DragShadowBuilder!, Object!, int);
+    method public static boolean startNestedScroll(android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int);
+    method public static boolean startNestedScroll(android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public static void stopNestedScroll(android.view.View);
+    method public static void stopNestedScroll(android.view.View, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder!);
+    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+    field @Deprecated public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
+    field @Deprecated public static final int LAYER_TYPE_NONE = 0; // 0x0
+    field @Deprecated public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+    field @Deprecated public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+    field @Deprecated public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+    field @Deprecated public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
+    field @Deprecated public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field @Deprecated public static final int OVER_SCROLL_ALWAYS = 0; // 0x0
+    field @Deprecated public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+    field @Deprecated public static final int OVER_SCROLL_NEVER = 2; // 0x2
+    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+    field public static final int TYPE_NON_TOUCH = 1; // 0x1
+    field public static final int TYPE_TOUCH = 0; // 0x0
+  }
+
+  @IntDef({android.view.View.FOCUS_LEFT, android.view.View.FOCUS_UP, android.view.View.FOCUS_RIGHT, android.view.View.FOCUS_DOWN, android.view.View.FOCUS_FORWARD, android.view.View.FOCUS_BACKWARD}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.FocusDirection {
+  }
+
+  @IntDef({android.view.View.FOCUS_LEFT, android.view.View.FOCUS_UP, android.view.View.FOCUS_RIGHT, android.view.View.FOCUS_DOWN}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.FocusRealDirection {
+  }
+
+  @IntDef({android.view.View.FOCUS_FORWARD, android.view.View.FOCUS_BACKWARD}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.FocusRelativeDirection {
+  }
+
+  @IntDef({androidx.core.view.ViewCompat.TYPE_TOUCH, androidx.core.view.ViewCompat.TYPE_NON_TOUCH}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.NestedScrollType {
+  }
+
+  public static interface ViewCompat.OnUnhandledKeyEventListenerCompat {
+    method public boolean onUnhandledKeyEvent(android.view.View!, android.view.KeyEvent!);
+  }
+
+  @IntDef(value={androidx.core.view.ViewCompat.SCROLL_AXIS_NONE, androidx.core.view.ViewCompat.SCROLL_AXIS_HORIZONTAL, androidx.core.view.ViewCompat.SCROLL_AXIS_VERTICAL}, flag=true) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.ScrollAxis {
+  }
+
+  @IntDef(flag=true, value={androidx.core.view.ViewCompat.SCROLL_INDICATOR_TOP, androidx.core.view.ViewCompat.SCROLL_INDICATOR_BOTTOM, androidx.core.view.ViewCompat.SCROLL_INDICATOR_LEFT, androidx.core.view.ViewCompat.SCROLL_INDICATOR_RIGHT, androidx.core.view.ViewCompat.SCROLL_INDICATOR_START, androidx.core.view.ViewCompat.SCROLL_INDICATOR_END}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.ScrollIndicators {
+  }
+
+  public final class ViewConfigurationCompat {
+    method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static int getScaledHoverSlop(android.view.ViewConfiguration!);
+    method @Deprecated public static int getScaledPagingTouchSlop(android.view.ViewConfiguration!);
+    method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method @Deprecated public static boolean hasPermanentMenuKey(android.view.ViewConfiguration!);
+    method public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(android.view.ViewConfiguration!, android.content.Context);
+  }
+
+  public final class ViewGroupCompat {
+    method public static int getLayoutMode(android.view.ViewGroup);
+    method @androidx.core.view.ViewCompat.ScrollAxis public static int getNestedScrollAxes(android.view.ViewGroup);
+    method public static boolean isTransitionGroup(android.view.ViewGroup);
+    method @Deprecated public static boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static void setLayoutMode(android.view.ViewGroup, int);
+    method @Deprecated public static void setMotionEventSplittingEnabled(android.view.ViewGroup!, boolean);
+    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+  }
+
+  public final class ViewParentCompat {
+    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent!, android.view.View!, android.view.View!, int);
+    method public static boolean onNestedFling(android.view.ViewParent!, android.view.View!, float, float, boolean);
+    method public static boolean onNestedPreFling(android.view.ViewParent!, android.view.View!, float, float);
+    method public static void onNestedPreScroll(android.view.ViewParent!, android.view.View!, int, int, int[]!);
+    method public static void onNestedPreScroll(android.view.ViewParent!, android.view.View!, int, int, int[]!, int);
+    method public static void onNestedScroll(android.view.ViewParent!, android.view.View!, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent!, android.view.View!, int, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent!, android.view.View!, int, int, int, int, int, int[]);
+    method public static void onNestedScrollAccepted(android.view.ViewParent!, android.view.View!, android.view.View!, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent!, android.view.View!, android.view.View!, int, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent!, android.view.View!, android.view.View!, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent!, android.view.View!, android.view.View!, int, int);
+    method public static void onStopNestedScroll(android.view.ViewParent!, android.view.View!);
+    method public static void onStopNestedScroll(android.view.ViewParent!, android.view.View!, int);
+    method @Deprecated public static boolean requestSendAccessibilityEvent(android.view.ViewParent!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+  }
+
+  public final class ViewPropertyAnimatorCompat {
+    method public androidx.core.view.ViewPropertyAnimatorCompat! alpha(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public android.view.animation.Interpolator! getInterpolator();
+    method public long getStartDelay();
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotation(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! rotationYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! scaleYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setDuration(long);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setInterpolator(android.view.animation.Interpolator!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setListener(androidx.core.view.ViewPropertyAnimatorListener!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setStartDelay(long);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! setUpdateListener(androidx.core.view.ViewPropertyAnimatorUpdateListener!);
+    method public void start();
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationZ(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! translationZBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! withEndAction(Runnable!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! withLayer();
+    method public androidx.core.view.ViewPropertyAnimatorCompat! withStartAction(Runnable!);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! x(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! xBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! y(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! yBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! z(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat! zBy(float);
+  }
+
+  public interface ViewPropertyAnimatorListener {
+    method public void onAnimationCancel(android.view.View!);
+    method public void onAnimationEnd(android.view.View!);
+    method public void onAnimationStart(android.view.View!);
+  }
+
+  public class ViewPropertyAnimatorListenerAdapter implements androidx.core.view.ViewPropertyAnimatorListener {
+    ctor public ViewPropertyAnimatorListenerAdapter();
+    method public void onAnimationCancel(android.view.View!);
+    method public void onAnimationEnd(android.view.View!);
+    method public void onAnimationStart(android.view.View!);
+  }
+
+  public interface ViewPropertyAnimatorUpdateListener {
+    method public void onAnimationUpdate(android.view.View!);
+  }
+
+  public final class WindowCompat {
+    method public static androidx.core.view.WindowInsetsControllerCompat? getInsetsController(android.view.Window, android.view.View);
+    method public static <T extends android.view.View> T requireViewById(android.view.Window, @IdRes int);
+    method public static void setDecorFitsSystemWindows(android.view.Window, boolean);
+    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+  }
+
+  public final class WindowInsetsAnimationCompat {
+    ctor public WindowInsetsAnimationCompat(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, android.view.animation.Interpolator?, long);
+    method @FloatRange(from=0.0f, to=1.0f) public float getAlpha();
+    method public long getDurationMillis();
+    method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+    method public float getInterpolatedFraction();
+    method public android.view.animation.Interpolator? getInterpolator();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public int getTypeMask();
+    method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float);
+    method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public static final class WindowInsetsAnimationCompat.Bounds {
+    ctor public WindowInsetsAnimationCompat.Bounds(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    ctor @RequiresApi(30) public WindowInsetsAnimationCompat.Bounds(android.view.WindowInsetsAnimation.Bounds);
+    method public androidx.core.graphics.Insets getLowerBound();
+    method public androidx.core.graphics.Insets getUpperBound();
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds inset(androidx.core.graphics.Insets);
+    method @RequiresApi(30) public android.view.WindowInsetsAnimation.Bounds toPlatformBounds();
+  }
+
+  public abstract static class WindowInsetsAnimationCompat.Callback {
+    ctor public WindowInsetsAnimationCompat.Callback(@androidx.core.view.WindowInsetsAnimationCompat.Callback.DispatchMode int);
+    method @androidx.core.view.WindowInsetsAnimationCompat.Callback.DispatchMode public final int getDispatchMode();
+    method public void onEnd(androidx.core.view.WindowInsetsAnimationCompat);
+    method public void onPrepare(androidx.core.view.WindowInsetsAnimationCompat);
+    method public abstract androidx.core.view.WindowInsetsCompat onProgress(androidx.core.view.WindowInsetsCompat, java.util.List<androidx.core.view.WindowInsetsAnimationCompat!>);
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds onStart(androidx.core.view.WindowInsetsAnimationCompat, androidx.core.view.WindowInsetsAnimationCompat.Bounds);
+    field public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1; // 0x1
+    field public static final int DISPATCH_MODE_STOP = 0; // 0x0
+  }
+
+  @IntDef({androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP, androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowInsetsAnimationCompat.Callback.DispatchMode {
+  }
+
+  public interface WindowInsetsAnimationControlListenerCompat {
+    method public void onCancelled(androidx.core.view.WindowInsetsAnimationControllerCompat?);
+    method public void onFinished(androidx.core.view.WindowInsetsAnimationControllerCompat);
+    method public void onReady(androidx.core.view.WindowInsetsAnimationControllerCompat, @androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+  }
+
+  public final class WindowInsetsAnimationControllerCompat {
+    method public void finish(boolean);
+    method public float getCurrentAlpha();
+    method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+    method public androidx.core.graphics.Insets getCurrentInsets();
+    method public androidx.core.graphics.Insets getHiddenStateInsets();
+    method public androidx.core.graphics.Insets getShownStateInsets();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public int getTypes();
+    method public boolean isCancelled();
+    method public boolean isFinished();
+    method public boolean isReady();
+    method public void setInsetsAndAlpha(androidx.core.graphics.Insets?, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public class WindowInsetsCompat {
+    ctor public WindowInsetsCompat(androidx.core.view.WindowInsetsCompat?);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeDisplayCutout();
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeStableInsets();
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeSystemWindowInsets();
+    method public androidx.core.view.DisplayCutoutCompat? getDisplayCutout();
+    method public androidx.core.graphics.Insets getInsets(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+    method public androidx.core.graphics.Insets getInsetsIgnoringVisibility(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+    method @Deprecated public androidx.core.graphics.Insets getMandatorySystemGestureInsets();
+    method @Deprecated public int getStableInsetBottom();
+    method @Deprecated public int getStableInsetLeft();
+    method @Deprecated public int getStableInsetRight();
+    method @Deprecated public int getStableInsetTop();
+    method @Deprecated public androidx.core.graphics.Insets getStableInsets();
+    method @Deprecated public androidx.core.graphics.Insets getSystemGestureInsets();
+    method @Deprecated public int getSystemWindowInsetBottom();
+    method @Deprecated public int getSystemWindowInsetLeft();
+    method @Deprecated public int getSystemWindowInsetRight();
+    method @Deprecated public int getSystemWindowInsetTop();
+    method @Deprecated public androidx.core.graphics.Insets getSystemWindowInsets();
+    method @Deprecated public androidx.core.graphics.Insets getTappableElementInsets();
+    method public boolean hasInsets();
+    method @Deprecated public boolean hasStableInsets();
+    method @Deprecated public boolean hasSystemWindowInsets();
+    method public androidx.core.view.WindowInsetsCompat inset(androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean isConsumed();
+    method public boolean isRound();
+    method public boolean isVisible(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+    method @RequiresApi(20) public android.view.WindowInsets? toWindowInsets();
+    method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets);
+    method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets, android.view.View?);
+    field public static final androidx.core.view.WindowInsetsCompat CONSUMED;
+  }
+
+  public static final class WindowInsetsCompat.Builder {
+    ctor public WindowInsetsCompat.Builder();
+    ctor public WindowInsetsCompat.Builder(androidx.core.view.WindowInsetsCompat);
+    method public androidx.core.view.WindowInsetsCompat build();
+    method public androidx.core.view.WindowInsetsCompat.Builder setDisplayCutout(androidx.core.view.DisplayCutoutCompat?);
+    method public androidx.core.view.WindowInsetsCompat.Builder setInsets(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat.Builder setInsetsIgnoringVisibility(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setMandatorySystemGestureInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setStableInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemGestureInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemWindowInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setTappableElementInsets(androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat.Builder setVisible(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, boolean);
+  }
+
+  public static final class WindowInsetsCompat.Type {
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int captionBar();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int displayCutout();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int ime();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int mandatorySystemGestures();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int navigationBars();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int statusBars();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int systemBars();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int systemGestures();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int tappableElement();
+  }
+
+  @IntDef(flag=true, value={androidx.core.view.WindowInsetsCompat.Type.STATUS_BARS, androidx.core.view.WindowInsetsCompat.Type.NAVIGATION_BARS, androidx.core.view.WindowInsetsCompat.Type.CAPTION_BAR, androidx.core.view.WindowInsetsCompat.Type.IME, androidx.core.view.WindowInsetsCompat.Type.WINDOW_DECOR, androidx.core.view.WindowInsetsCompat.Type.SYSTEM_GESTURES, androidx.core.view.WindowInsetsCompat.Type.MANDATORY_SYSTEM_GESTURES, androidx.core.view.WindowInsetsCompat.Type.TAPPABLE_ELEMENT, androidx.core.view.WindowInsetsCompat.Type.DISPLAY_CUTOUT}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowInsetsCompat.Type.InsetsType {
+  }
+
+  public final class WindowInsetsControllerCompat {
+    ctor public WindowInsetsControllerCompat(android.view.Window, android.view.View);
+    method public void addOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void controlWindowInsetsAnimation(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, long, android.view.animation.Interpolator?, android.os.CancellationSignal?, androidx.core.view.WindowInsetsAnimationControlListenerCompat);
+    method public int getSystemBarsBehavior();
+    method public void hide(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+    method public boolean isAppearanceLightNavigationBars();
+    method public boolean isAppearanceLightStatusBars();
+    method public void removeOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void setAppearanceLightNavigationBars(boolean);
+    method public void setAppearanceLightStatusBars(boolean);
+    method public void setSystemBarsBehavior(int);
+    method public void show(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+    method @RequiresApi(30) public static androidx.core.view.WindowInsetsControllerCompat toWindowInsetsControllerCompat(android.view.WindowInsetsController);
+    field public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
+    field public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0
+    field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
+  }
+
+  public static interface WindowInsetsControllerCompat.OnControllableInsetsChangedListener {
+    method public void onControllableInsetsChanged(androidx.core.view.WindowInsetsControllerCompat, @androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+  }
+
+}
+
+package androidx.core.view.accessibility {
+
+  public final class AccessibilityClickableSpanCompat extends android.text.style.ClickableSpan {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public AccessibilityClickableSpanCompat(int, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!, int);
+    method public void onClick(android.view.View);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final String SPAN_ID = "ACCESSIBILITY_CLICKABLE_SPAN_ID";
+  }
+
+  public final class AccessibilityEventCompat {
+    method @Deprecated public static void appendRecord(android.view.accessibility.AccessibilityEvent!, androidx.core.view.accessibility.AccessibilityRecordCompat!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! asRecord(android.view.accessibility.AccessibilityEvent!);
+    method public static int getAction(android.view.accessibility.AccessibilityEvent!);
+    method @androidx.core.view.accessibility.AccessibilityEventCompat.ContentChangeType public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent!);
+    method public static int getMovementGranularity(android.view.accessibility.AccessibilityEvent!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! getRecord(android.view.accessibility.AccessibilityEvent!, int);
+    method @Deprecated public static int getRecordCount(android.view.accessibility.AccessibilityEvent!);
+    method public static void setAction(android.view.accessibility.AccessibilityEvent!, int);
+    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent!, @androidx.core.view.accessibility.AccessibilityEventCompat.ContentChangeType int);
+    method public static void setMovementGranularity(android.view.accessibility.AccessibilityEvent!, int);
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
+    field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
+    field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
+    field public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 64; // 0x40
+    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+    field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+    field @Deprecated public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field @Deprecated public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+    field @Deprecated public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+    field @Deprecated public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+    field @Deprecated public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+  }
+
+  @IntDef(flag=true, value={androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_TEXT, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AccessibilityEventCompat.ContentChangeType {
+  }
+
+  public final class AccessibilityManagerCompat {
+    method @Deprecated public static boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener!);
+    method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!, int);
+    method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!);
+    method @Deprecated public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager!);
+    method @Deprecated public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener!);
+  }
+
+  @Deprecated public static interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    method @Deprecated public void onAccessibilityStateChanged(boolean);
+  }
+
+  @Deprecated public abstract static class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    ctor @Deprecated public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+  }
+
+  public static interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+    method public void onTouchExplorationStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfoCompat {
+    ctor @Deprecated public AccessibilityNodeInfoCompat(Object!);
+    method public void addAction(int);
+    method public void addAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+    method public void addChild(android.view.View!);
+    method public void addChild(android.view.View!, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void addSpansToExtras(CharSequence!, android.view.View!);
+    method public boolean canOpenPopup();
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByText(String!);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByViewId(String!);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! findFocus(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! focusSearch(int);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!>! getActionList();
+    method public int getActions();
+    method @Deprecated public void getBoundsInParent(android.graphics.Rect!);
+    method public void getBoundsInScreen(android.graphics.Rect!);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getChild(int);
+    method public int getChildCount();
+    method public CharSequence! getClassName();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.text.style.ClickableSpan![]! getClickableSpans(CharSequence!);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! getCollectionInfo();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! getCollectionItemInfo();
+    method public CharSequence! getContentDescription();
+    method public int getDrawingOrder();
+    method public CharSequence! getError();
+    method public android.os.Bundle! getExtras();
+    method public CharSequence? getHintText();
+    method @Deprecated public Object! getInfo();
+    method public int getInputType();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabelFor();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabeledBy();
+    method public int getLiveRegion();
+    method public int getMaxTextLength();
+    method public int getMovementGranularities();
+    method public CharSequence! getPackageName();
+    method public CharSequence? getPaneTitle();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getParent();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! getRangeInfo();
+    method public CharSequence? getRoleDescription();
+    method public CharSequence? getStateDescription();
+    method public CharSequence! getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public CharSequence? getTooltipText();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat? getTouchDelegateInfo();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalAfter();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalBefore();
+    method public String! getViewIdResourceName();
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getWindow();
+    method public int getWindowId();
+    method public boolean isAccessibilityFocused();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isContentInvalid();
+    method public boolean isContextClickable();
+    method public boolean isDismissable();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isHeading();
+    method public boolean isImportantForAccessibility();
+    method public boolean isLongClickable();
+    method public boolean isMultiLine();
+    method public boolean isPassword();
+    method public boolean isScreenReaderFocusable();
+    method public boolean isScrollable();
+    method public boolean isSelected();
+    method public boolean isShowingHintText();
+    method public boolean isTextEntryKey();
+    method public boolean isVisibleToUser();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!, int);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle!);
+    method public void recycle();
+    method public boolean refresh();
+    method public boolean removeAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+    method public boolean removeChild(android.view.View!);
+    method public boolean removeChild(android.view.View!, int);
+    method public void setAccessibilityFocused(boolean);
+    method @Deprecated public void setBoundsInParent(android.graphics.Rect!);
+    method public void setBoundsInScreen(android.graphics.Rect!);
+    method public void setCanOpenPopup(boolean);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(CharSequence!);
+    method public void setClickable(boolean);
+    method public void setCollectionInfo(Object!);
+    method public void setCollectionItemInfo(Object!);
+    method public void setContentDescription(CharSequence!);
+    method public void setContentInvalid(boolean);
+    method public void setContextClickable(boolean);
+    method public void setDismissable(boolean);
+    method public void setDrawingOrder(int);
+    method public void setEditable(boolean);
+    method public void setEnabled(boolean);
+    method public void setError(CharSequence!);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setHeading(boolean);
+    method public void setHintText(CharSequence?);
+    method public void setImportantForAccessibility(boolean);
+    method public void setInputType(int);
+    method public void setLabelFor(android.view.View!);
+    method public void setLabelFor(android.view.View!, int);
+    method public void setLabeledBy(android.view.View!);
+    method public void setLabeledBy(android.view.View!, int);
+    method public void setLiveRegion(int);
+    method public void setLongClickable(boolean);
+    method public void setMaxTextLength(int);
+    method public void setMovementGranularities(int);
+    method public void setMultiLine(boolean);
+    method public void setPackageName(CharSequence!);
+    method public void setPaneTitle(CharSequence?);
+    method public void setParent(android.view.View!);
+    method public void setParent(android.view.View!, int);
+    method public void setPassword(boolean);
+    method public void setRangeInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat!);
+    method public void setRoleDescription(CharSequence?);
+    method public void setScreenReaderFocusable(boolean);
+    method public void setScrollable(boolean);
+    method public void setSelected(boolean);
+    method public void setShowingHintText(boolean);
+    method public void setSource(android.view.View!);
+    method public void setSource(android.view.View!, int);
+    method public void setStateDescription(CharSequence?);
+    method public void setText(CharSequence!);
+    method public void setTextEntryKey(boolean);
+    method public void setTextSelection(int, int);
+    method public void setTooltipText(CharSequence?);
+    method public void setTouchDelegateInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat);
+    method public void setTraversalAfter(android.view.View!);
+    method public void setTraversalAfter(android.view.View!, int);
+    method public void setTraversalBefore(android.view.View!);
+    method public void setTraversalBefore(android.view.View!, int);
+    method public void setViewIdResourceName(String!);
+    method public void setVisibleToUser(boolean);
+    method public android.view.accessibility.AccessibilityNodeInfo! unwrap();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! wrap(android.view.accessibility.AccessibilityNodeInfo);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+    field public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+    field public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+    field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final String ACTION_ARGUMENT_MOVE_WINDOW_X = "ACTION_ARGUMENT_MOVE_WINDOW_X";
+    field public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y = "ACTION_ARGUMENT_MOVE_WINDOW_Y";
+    field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
+    field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+    field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+    field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+    field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 16; // 0x10
+    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+    field public static final int ACTION_COPY = 16384; // 0x4000
+    field public static final int ACTION_CUT = 65536; // 0x10000
+    field public static final int ACTION_DISMISS = 1048576; // 0x100000
+    field public static final int ACTION_EXPAND = 262144; // 0x40000
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+    field public static final int ACTION_PASTE = 32768; // 0x8000
+    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int mParentVirtualDescendantId;
+  }
+
+  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, CharSequence!);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, CharSequence!, androidx.core.view.accessibility.AccessibilityViewCommand!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! createReplacementAction(CharSequence!, androidx.core.view.accessibility.AccessibilityViewCommand!);
+    method public int getId();
+    method public CharSequence! getLabel();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean perform(android.view.View!, android.os.Bundle!);
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_ACCESSIBILITY_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_SELECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COLLAPSE;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CONTEXT_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COPY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CUT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_DISMISS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_EXPAND;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_HIDE_TOOLTIP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_IME_ENTER;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_LONG_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_MOVE_WINDOW;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_HTML_ELEMENT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_DOWN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_LEFT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_RIGHT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_UP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PASTE;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PRESS_AND_HOLD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_HTML_ELEMENT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_BACKWARD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_DOWN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_FORWARD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_LEFT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_RIGHT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_TO_POSITION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_UP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SELECT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_PROGRESS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_SELECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_TEXT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_ON_SCREEN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_TOOLTIP;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected final androidx.core.view.accessibility.AccessibilityViewCommand! mCommand;
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+    method public int getColumnCount();
+    method public int getRowCount();
+    method public int getSelectionMode();
+    method public boolean isHierarchical();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean, int);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean);
+    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+    field public static final int SELECTION_MODE_NONE = 0; // 0x0
+    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    method @Deprecated public boolean isHeading();
+    method public boolean isSelected();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean, boolean);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean);
+  }
+
+  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    method public float getCurrent();
+    method public float getMax();
+    method public float getMin();
+    method public int getType();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! obtain(int, float, float, float);
+    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field public static final int RANGE_TYPE_INT = 0; // 0x0
+    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+  }
+
+  public static final class AccessibilityNodeInfoCompat.TouchDelegateInfoCompat {
+    ctor public AccessibilityNodeInfoCompat.TouchDelegateInfoCompat(java.util.Map<android.graphics.Region!,android.view.View!>);
+    method public android.graphics.Region? getRegionAt(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getRegionCount();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getTargetForRegion(android.graphics.Region);
+  }
+
+  public class AccessibilityNodeProviderCompat {
+    ctor public AccessibilityNodeProviderCompat();
+    ctor public AccessibilityNodeProviderCompat(Object!);
+    method public void addExtraDataToAccessibilityNodeInfo(int, androidx.core.view.accessibility.AccessibilityNodeInfoCompat, String, android.os.Bundle?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? createAccessibilityNodeInfo(int);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>? findAccessibilityNodeInfosByText(String!, int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? findFocus(int);
+    method public Object! getProvider();
+    method public boolean performAction(int, int, android.os.Bundle!);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+  }
+
+  public class AccessibilityRecordCompat {
+    ctor @Deprecated public AccessibilityRecordCompat(Object!);
+    method @Deprecated public boolean equals(Object?);
+    method @Deprecated public int getAddedCount();
+    method @Deprecated public CharSequence! getBeforeText();
+    method @Deprecated public CharSequence! getClassName();
+    method @Deprecated public CharSequence! getContentDescription();
+    method @Deprecated public int getCurrentItemIndex();
+    method @Deprecated public int getFromIndex();
+    method @Deprecated public Object! getImpl();
+    method @Deprecated public int getItemCount();
+    method @Deprecated public int getMaxScrollX();
+    method public static int getMaxScrollX(android.view.accessibility.AccessibilityRecord!);
+    method @Deprecated public int getMaxScrollY();
+    method public static int getMaxScrollY(android.view.accessibility.AccessibilityRecord!);
+    method @Deprecated public android.os.Parcelable! getParcelableData();
+    method @Deprecated public int getRemovedCount();
+    method @Deprecated public int getScrollX();
+    method @Deprecated public int getScrollY();
+    method @Deprecated public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getSource();
+    method @Deprecated public java.util.List<java.lang.CharSequence!>! getText();
+    method @Deprecated public int getToIndex();
+    method @Deprecated public int getWindowId();
+    method @Deprecated public int hashCode();
+    method @Deprecated public boolean isChecked();
+    method @Deprecated public boolean isEnabled();
+    method @Deprecated public boolean isFullScreen();
+    method @Deprecated public boolean isPassword();
+    method @Deprecated public boolean isScrollable();
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain(androidx.core.view.accessibility.AccessibilityRecordCompat!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain();
+    method @Deprecated public void recycle();
+    method @Deprecated public void setAddedCount(int);
+    method @Deprecated public void setBeforeText(CharSequence!);
+    method @Deprecated public void setChecked(boolean);
+    method @Deprecated public void setClassName(CharSequence!);
+    method @Deprecated public void setContentDescription(CharSequence!);
+    method @Deprecated public void setCurrentItemIndex(int);
+    method @Deprecated public void setEnabled(boolean);
+    method @Deprecated public void setFromIndex(int);
+    method @Deprecated public void setFullScreen(boolean);
+    method @Deprecated public void setItemCount(int);
+    method @Deprecated public void setMaxScrollX(int);
+    method public static void setMaxScrollX(android.view.accessibility.AccessibilityRecord!, int);
+    method @Deprecated public void setMaxScrollY(int);
+    method public static void setMaxScrollY(android.view.accessibility.AccessibilityRecord!, int);
+    method @Deprecated public void setParcelableData(android.os.Parcelable!);
+    method @Deprecated public void setPassword(boolean);
+    method @Deprecated public void setRemovedCount(int);
+    method @Deprecated public void setScrollX(int);
+    method @Deprecated public void setScrollY(int);
+    method @Deprecated public void setScrollable(boolean);
+    method @Deprecated public void setSource(android.view.View!);
+    method @Deprecated public void setSource(android.view.View!, int);
+    method public static void setSource(android.view.accessibility.AccessibilityRecord, android.view.View!, int);
+    method @Deprecated public void setToIndex(int);
+  }
+
+  public interface AccessibilityViewCommand {
+    method public boolean perform(android.view.View, androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments?);
+  }
+
+  public abstract static class AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.CommandArguments();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setBundle(android.os.Bundle!);
+  }
+
+  public static final class AccessibilityViewCommand.MoveAtGranularityArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveAtGranularityArguments();
+    method public boolean getExtendSelection();
+    method public int getGranularity();
+  }
+
+  public static final class AccessibilityViewCommand.MoveHtmlArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveHtmlArguments();
+    method public String! getHTMLElement();
+  }
+
+  public static final class AccessibilityViewCommand.MoveWindowArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveWindowArguments();
+    method public int getX();
+    method public int getY();
+  }
+
+  public static final class AccessibilityViewCommand.ScrollToPositionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.ScrollToPositionArguments();
+    method public int getColumn();
+    method public int getRow();
+  }
+
+  public static final class AccessibilityViewCommand.SetProgressArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetProgressArguments();
+    method public float getProgress();
+  }
+
+  public static final class AccessibilityViewCommand.SetSelectionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetSelectionArguments();
+    method public int getEnd();
+    method public int getStart();
+  }
+
+  public static final class AccessibilityViewCommand.SetTextArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetTextArguments();
+    method public CharSequence! getText();
+  }
+
+  public class AccessibilityWindowInfoCompat {
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getAnchor();
+    method public void getBoundsInScreen(android.graphics.Rect!);
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getChild(int);
+    method public int getChildCount();
+    method public int getId();
+    method public int getLayer();
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getParent();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getRoot();
+    method public CharSequence! getTitle();
+    method public int getType();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat! obtain();
+    method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat! obtain(androidx.core.view.accessibility.AccessibilityWindowInfoCompat!);
+    method public void recycle();
+    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
+}
+
+package androidx.core.view.animation {
+
+  public final class PathInterpolatorCompat {
+    method public static android.view.animation.Interpolator! create(android.graphics.Path!);
+    method public static android.view.animation.Interpolator! create(float, float);
+    method public static android.view.animation.Interpolator! create(float, float, float, float);
+  }
+
+}
+
+package androidx.core.view.inputmethod {
+
+  public final class EditorInfoCompat {
+    ctor @Deprecated public EditorInfoCompat();
+    method public static String![] getContentMimeTypes(android.view.inputmethod.EditorInfo!);
+    method public static CharSequence? getInitialSelectedText(android.view.inputmethod.EditorInfo, int);
+    method public static CharSequence? getInitialTextAfterCursor(android.view.inputmethod.EditorInfo, int, int);
+    method public static CharSequence? getInitialTextBeforeCursor(android.view.inputmethod.EditorInfo, int, int);
+    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, String![]?);
+    method public static void setInitialSurroundingSubText(android.view.inputmethod.EditorInfo, CharSequence, int);
+    method public static void setInitialSurroundingText(android.view.inputmethod.EditorInfo, CharSequence);
+    field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+    field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+  }
+
+  public final class InputConnectionCompat {
+    ctor @Deprecated public InputConnectionCompat();
+    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle?);
+    method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+    field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
+  }
+
+  public static interface InputConnectionCompat.OnCommitContentListener {
+    method public boolean onCommitContent(androidx.core.view.inputmethod.InputContentInfoCompat!, int, android.os.Bundle!);
+  }
+
+  public final class InputContentInfoCompat {
+    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri?);
+    method public android.net.Uri getContentUri();
+    method public android.content.ClipDescription getDescription();
+    method public android.net.Uri? getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
+    method public Object? unwrap();
+    method public static androidx.core.view.inputmethod.InputContentInfoCompat? wrap(Object?);
+  }
+
+}
+
+package androidx.core.widget {
+
+  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+    ctor public AutoScrollHelper(android.view.View);
+    method public abstract boolean canTargetScrollHorizontally(int);
+    method public abstract boolean canTargetScrollVertically(int);
+    method public boolean isEnabled();
+    method public boolean isExclusive();
+    method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+    method public abstract void scrollTargetBy(int, int);
+    method public androidx.core.widget.AutoScrollHelper setActivationDelay(int);
+    method public androidx.core.widget.AutoScrollHelper setEdgeType(int);
+    method public androidx.core.widget.AutoScrollHelper! setEnabled(boolean);
+    method public androidx.core.widget.AutoScrollHelper! setExclusive(boolean);
+    method public androidx.core.widget.AutoScrollHelper setMaximumEdges(float, float);
+    method public androidx.core.widget.AutoScrollHelper setMaximumVelocity(float, float);
+    method public androidx.core.widget.AutoScrollHelper setMinimumVelocity(float, float);
+    method public androidx.core.widget.AutoScrollHelper setRampDownDuration(int);
+    method public androidx.core.widget.AutoScrollHelper setRampUpDuration(int);
+    method public androidx.core.widget.AutoScrollHelper setRelativeEdges(float, float);
+    method public androidx.core.widget.AutoScrollHelper setRelativeVelocity(float, float);
+    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+    field public static final float NO_MAX = 3.4028235E38f;
+    field public static final float NO_MIN = 0.0f;
+    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface AutoSizeableTextView {
+    method public int getAutoSizeMaxTextSize();
+    method public int getAutoSizeMinTextSize();
+    method public int getAutoSizeStepGranularity();
+    method public int[]! getAutoSizeTextAvailableSizes();
+    method @androidx.core.widget.TextViewCompat.AutoSizeTextType public int getAutoSizeTextType();
+    method public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setAutoSizeTextTypeWithDefaults(@androidx.core.widget.TextViewCompat.AutoSizeTextType int);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final boolean PLATFORM_SUPPORTS_AUTOSIZE;
+  }
+
+  public final class CompoundButtonCompat {
+    method public static android.graphics.drawable.Drawable? getButtonDrawable(android.widget.CompoundButton);
+    method public static android.content.res.ColorStateList? getButtonTintList(android.widget.CompoundButton);
+    method public static android.graphics.PorterDuff.Mode? getButtonTintMode(android.widget.CompoundButton);
+    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList?);
+    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode?);
+  }
+
+  public class ContentLoadingProgressBar extends android.widget.ProgressBar {
+    ctor public ContentLoadingProgressBar(android.content.Context);
+    ctor public ContentLoadingProgressBar(android.content.Context, android.util.AttributeSet?);
+    method public void hide();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void show();
+  }
+
+  public final class EdgeEffectCompat {
+    ctor @Deprecated public EdgeEffectCompat(android.content.Context!);
+    method @Deprecated public boolean draw(android.graphics.Canvas!);
+    method @Deprecated public void finish();
+    method @Deprecated public boolean isFinished();
+    method @Deprecated public boolean onAbsorb(int);
+    method @Deprecated public boolean onPull(float);
+    method @Deprecated public boolean onPull(float, float);
+    method public static void onPull(android.widget.EdgeEffect, float, float);
+    method @Deprecated public boolean onRelease();
+    method @Deprecated public void setSize(int, int);
+  }
+
+  public class ImageViewCompat {
+    method public static android.content.res.ColorStateList? getImageTintList(android.widget.ImageView);
+    method public static android.graphics.PorterDuff.Mode? getImageTintMode(android.widget.ImageView);
+    method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList?);
+    method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode?);
+  }
+
+  public final class ListPopupWindowCompat {
+    method @Deprecated public static android.view.View.OnTouchListener! createDragToOpenListener(Object!, android.view.View!);
+    method public static android.view.View.OnTouchListener? createDragToOpenListener(android.widget.ListPopupWindow, android.view.View);
+  }
+
+  public class ListViewAutoScrollHelper extends androidx.core.widget.AutoScrollHelper {
+    ctor public ListViewAutoScrollHelper(android.widget.ListView);
+    method public boolean canTargetScrollHorizontally(int);
+    method public boolean canTargetScrollVertically(int);
+    method public void scrollTargetBy(int, int);
+  }
+
+  public final class ListViewCompat {
+    method public static boolean canScrollList(android.widget.ListView, int);
+    method public static void scrollListBy(android.widget.ListView, int);
+  }
+
+  public class NestedScrollView extends android.widget.FrameLayout implements androidx.core.view.NestedScrollingChild3 androidx.core.view.NestedScrollingParent3 androidx.core.view.ScrollingView {
+    ctor public NestedScrollView(android.content.Context);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?, int);
+    method public boolean arrowScroll(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollExtent();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollOffset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollRange();
+    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollExtent();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollOffset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollRange();
+    method public boolean dispatchNestedPreScroll(int, int, int[]!, int[]!, int);
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]!, int);
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fling(int);
+    method public boolean fullScroll(int);
+    method public int getMaxScrollAmount();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isFillViewport();
+    method public boolean isSmoothScrollingEnabled();
+    method public void onAttachedToWindow();
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+    method public boolean pageScroll(int);
+    method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(androidx.core.widget.NestedScrollView.OnScrollChangeListener?);
+    method public void setSmoothScrollingEnabled(boolean);
+    method public final void smoothScrollBy(int, int);
+    method public final void smoothScrollBy(int, int, int);
+    method public final void smoothScrollTo(int, int);
+    method public final void smoothScrollTo(int, int, int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public static interface NestedScrollView.OnScrollChangeListener {
+    method public void onScrollChange(androidx.core.widget.NestedScrollView!, int, int, int, int);
+  }
+
+  public final class PopupMenuCompat {
+    method public static android.view.View.OnTouchListener? getDragToOpenListener(Object);
+  }
+
+  public final class PopupWindowCompat {
+    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+    method public static int getWindowLayoutType(android.widget.PopupWindow);
+    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+  }
+
+  @Deprecated public final class ScrollerCompat {
+    method @Deprecated public void abortAnimation();
+    method @Deprecated public boolean computeScrollOffset();
+    method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!);
+    method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!, android.view.animation.Interpolator!);
+    method @Deprecated public void fling(int, int, int, int, int, int, int, int);
+    method @Deprecated public void fling(int, int, int, int, int, int, int, int, int, int);
+    method @Deprecated public float getCurrVelocity();
+    method @Deprecated public int getCurrX();
+    method @Deprecated public int getCurrY();
+    method @Deprecated public int getFinalX();
+    method @Deprecated public int getFinalY();
+    method @Deprecated public boolean isFinished();
+    method @Deprecated public boolean isOverScrolled();
+    method @Deprecated public void notifyHorizontalEdgeReached(int, int, int);
+    method @Deprecated public void notifyVerticalEdgeReached(int, int, int);
+    method @Deprecated public boolean springBack(int, int, int, int, int, int);
+    method @Deprecated public void startScroll(int, int, int, int);
+    method @Deprecated public void startScroll(int, int, int, int, int);
+  }
+
+  public final class TextViewCompat {
+    method public static int getAutoSizeMaxTextSize(android.widget.TextView);
+    method public static int getAutoSizeMinTextSize(android.widget.TextView);
+    method public static int getAutoSizeStepGranularity(android.widget.TextView);
+    method public static int[] getAutoSizeTextAvailableSizes(android.widget.TextView);
+    method public static int getAutoSizeTextType(android.widget.TextView);
+    method public static android.content.res.ColorStateList? getCompoundDrawableTintList(android.widget.TextView);
+    method public static android.graphics.PorterDuff.Mode? getCompoundDrawableTintMode(android.widget.TextView);
+    method public static android.graphics.drawable.Drawable![] getCompoundDrawablesRelative(android.widget.TextView);
+    method public static int getFirstBaselineToTopHeight(android.widget.TextView);
+    method public static int getLastBaselineToBottomHeight(android.widget.TextView);
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
+    method public static androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParams(android.widget.TextView);
+    method public static void setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeWithDefaults(android.widget.TextView, int);
+    method public static void setCompoundDrawableTintList(android.widget.TextView, android.content.res.ColorStateList?);
+    method public static void setCompoundDrawableTintMode(android.widget.TextView, android.graphics.PorterDuff.Mode?);
+    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, @DrawableRes int, @DrawableRes int, @DrawableRes int, @DrawableRes int);
+    method public static void setCustomSelectionActionModeCallback(android.widget.TextView, android.view.ActionMode.Callback);
+    method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
+    method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
+    method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.view.ActionMode.Callback wrapCustomSelectionActionModeCallback(android.widget.TextView, android.view.ActionMode.Callback);
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
+  }
+
+  @IntDef({androidx.core.widget.TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE, androidx.core.widget.TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface TextViewCompat.AutoSizeTextType {
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class TextViewOnReceiveContentListener implements androidx.core.view.OnReceiveContentListener {
+    ctor public TextViewOnReceiveContentListener();
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+  }
+
+  public interface TintableCompoundButton {
+    method public android.content.res.ColorStateList? getSupportButtonTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public interface TintableCompoundDrawablesView {
+    method public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface TintableImageSourceView {
+    method public android.content.res.ColorStateList? getSupportImageTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportImageTintMode();
+    method public void setSupportImageTintList(android.content.res.ColorStateList?);
+    method public void setSupportImageTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+}
+
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 729b498..6e89c98 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -945,8 +945,8 @@
   }
 
   public final class ShareCompat {
-    method public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
-    method public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
     method public static android.content.ComponentName? getCallingActivity(android.app.Activity);
     method public static String? getCallingPackage(android.app.Activity);
     field public static final String EXTRA_CALLING_ACTIVITY = "androidx.core.app.EXTRA_CALLING_ACTIVITY";
@@ -1143,6 +1143,8 @@
 
   public final class PackageInfoCompat {
     method public static long getLongVersionCode(android.content.pm.PackageInfo);
+    method public static java.util.List<android.content.pm.Signature!> getSignatures(android.content.pm.PackageManager, String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static boolean hasSignatures(android.content.pm.PackageManager, String, @Size(min=1) java.util.Map<byte[]!,java.lang.Integer!>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
   }
 
   public final class PermissionInfoCompat {
@@ -1834,7 +1836,7 @@
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O_MR1) public static boolean isAtLeastOMR1();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.P) public static boolean isAtLeastP();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
-    method @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
     method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
   }
 
@@ -2296,6 +2298,37 @@
     method public void onActionProviderVisibilityChanged(boolean);
   }
 
+  public final class ContentInfoCompat {
+    method public android.content.ClipData getClip();
+    method public android.os.Bundle? getExtras();
+    method @androidx.core.view.ContentInfoCompat.Flags public int getFlags();
+    method public android.net.Uri? getLinkUri();
+    method @androidx.core.view.ContentInfoCompat.Source public int getSource();
+    method public android.util.Pair<androidx.core.view.ContentInfoCompat!,androidx.core.view.ContentInfoCompat!> partition(androidx.core.util.Predicate<android.content.ClipData.Item!>);
+    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+  }
+
+  public static final class ContentInfoCompat.Builder {
+    ctor public ContentInfoCompat.Builder(androidx.core.view.ContentInfoCompat);
+    ctor public ContentInfoCompat.Builder(android.content.ClipData, @androidx.core.view.ContentInfoCompat.Source int);
+    method public androidx.core.view.ContentInfoCompat build();
+    method public androidx.core.view.ContentInfoCompat.Builder setClip(android.content.ClipData);
+    method public androidx.core.view.ContentInfoCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.view.ContentInfoCompat.Builder setFlags(@androidx.core.view.ContentInfoCompat.Flags int);
+    method public androidx.core.view.ContentInfoCompat.Builder setLinkUri(android.net.Uri?);
+    method public androidx.core.view.ContentInfoCompat.Builder setSource(@androidx.core.view.ContentInfoCompat.Source int);
+  }
+
+  @IntDef(flag=true, value={androidx.core.view.ContentInfoCompat.FLAG_CONVERT_TO_PLAIN_TEXT}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ContentInfoCompat.Flags {
+  }
+
+  @IntDef({androidx.core.view.ContentInfoCompat.SOURCE_APP, androidx.core.view.ContentInfoCompat.SOURCE_CLIPBOARD, androidx.core.view.ContentInfoCompat.SOURCE_INPUT_METHOD, androidx.core.view.ContentInfoCompat.SOURCE_DRAG_AND_DROP}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ContentInfoCompat.Source {
+  }
+
   public final class DisplayCompat {
     method public static androidx.core.view.DisplayCompat.ModeCompat![] getSupportedModes(android.content.Context, android.view.Display);
   }
@@ -2602,6 +2635,14 @@
     method public androidx.core.view.WindowInsetsCompat! onApplyWindowInsets(android.view.View!, androidx.core.view.WindowInsetsCompat!);
   }
 
+  public interface OnReceiveContentListener {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+  }
+
+  public interface OnReceiveContentViewBehavior {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+  }
+
   public final class OneShotPreDrawListener implements android.view.View.OnAttachStateChangeListener android.view.ViewTreeObserver.OnPreDrawListener {
     method public static androidx.core.view.OneShotPreDrawListener add(android.view.View, Runnable);
     method public boolean onPreDraw();
@@ -2714,6 +2755,7 @@
     method public static int getMinimumHeight(android.view.View);
     method public static int getMinimumWidth(android.view.View);
     method public static int getNextClusterForwardId(android.view.View);
+    method public static String![]? getOnReceiveContentMimeTypes(android.view.View);
     method @Deprecated public static int getOverScrollMode(android.view.View!);
     method @Px public static int getPaddingEnd(android.view.View);
     method @Px public static int getPaddingStart(android.view.View);
@@ -2767,6 +2809,7 @@
     method public static void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
     method @Deprecated public static void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
     method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle!);
+    method public static androidx.core.view.ContentInfoCompat? performReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
     method public static void postInvalidateOnAnimation(android.view.View);
     method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
     method public static void postOnAnimation(android.view.View, Runnable!);
@@ -2805,6 +2848,7 @@
     method public static void setNestedScrollingEnabled(android.view.View, boolean);
     method public static void setNextClusterForwardId(android.view.View, int);
     method public static void setOnApplyWindowInsetsListener(android.view.View, androidx.core.view.OnApplyWindowInsetsListener?);
+    method public static void setOnReceiveContentListener(android.view.View, String![]?, androidx.core.view.OnReceiveContentListener?);
     method @Deprecated public static void setOverScrollMode(android.view.View!, int);
     method public static void setPaddingRelative(android.view.View, @Px int, @Px int, @Px int, @Px int);
     method @Deprecated public static void setPivotX(android.view.View!, float);
@@ -2826,6 +2870,7 @@
     method @Deprecated public static void setTranslationX(android.view.View!, float);
     method @Deprecated public static void setTranslationY(android.view.View!, float);
     method public static void setTranslationZ(android.view.View, float);
+    method public static void setWindowInsetsAnimationCallback(android.view.View, androidx.core.view.WindowInsetsAnimationCompat.Callback?);
     method @Deprecated public static void setX(android.view.View!, float);
     method @Deprecated public static void setY(android.view.View!, float);
     method public static void setZ(android.view.View, float);
@@ -2996,6 +3041,61 @@
     field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
   }
 
+  public final class WindowInsetsAnimationCompat {
+    ctor public WindowInsetsAnimationCompat(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, android.view.animation.Interpolator?, long);
+    method @FloatRange(from=0.0f, to=1.0f) public float getAlpha();
+    method public long getDurationMillis();
+    method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+    method public float getInterpolatedFraction();
+    method public android.view.animation.Interpolator? getInterpolator();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public int getTypeMask();
+    method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float);
+    method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public static final class WindowInsetsAnimationCompat.Bounds {
+    ctor public WindowInsetsAnimationCompat.Bounds(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    ctor @RequiresApi(30) public WindowInsetsAnimationCompat.Bounds(android.view.WindowInsetsAnimation.Bounds);
+    method public androidx.core.graphics.Insets getLowerBound();
+    method public androidx.core.graphics.Insets getUpperBound();
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds inset(androidx.core.graphics.Insets);
+    method @RequiresApi(30) public android.view.WindowInsetsAnimation.Bounds toPlatformBounds();
+  }
+
+  public abstract static class WindowInsetsAnimationCompat.Callback {
+    ctor public WindowInsetsAnimationCompat.Callback(@androidx.core.view.WindowInsetsAnimationCompat.Callback.DispatchMode int);
+    method @androidx.core.view.WindowInsetsAnimationCompat.Callback.DispatchMode public final int getDispatchMode();
+    method public void onEnd(androidx.core.view.WindowInsetsAnimationCompat);
+    method public void onPrepare(androidx.core.view.WindowInsetsAnimationCompat);
+    method public abstract androidx.core.view.WindowInsetsCompat onProgress(androidx.core.view.WindowInsetsCompat, java.util.List<androidx.core.view.WindowInsetsAnimationCompat!>);
+    method public androidx.core.view.WindowInsetsAnimationCompat.Bounds onStart(androidx.core.view.WindowInsetsAnimationCompat, androidx.core.view.WindowInsetsAnimationCompat.Bounds);
+    field public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1; // 0x1
+    field public static final int DISPATCH_MODE_STOP = 0; // 0x0
+  }
+
+  @IntDef({androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP, androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowInsetsAnimationCompat.Callback.DispatchMode {
+  }
+
+  public interface WindowInsetsAnimationControlListenerCompat {
+    method public void onCancelled(androidx.core.view.WindowInsetsAnimationControllerCompat?);
+    method public void onFinished(androidx.core.view.WindowInsetsAnimationControllerCompat);
+    method public void onReady(androidx.core.view.WindowInsetsAnimationControllerCompat, @androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+  }
+
+  public final class WindowInsetsAnimationControllerCompat {
+    method public void finish(boolean);
+    method public float getCurrentAlpha();
+    method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+    method public androidx.core.graphics.Insets getCurrentInsets();
+    method public androidx.core.graphics.Insets getHiddenStateInsets();
+    method public androidx.core.graphics.Insets getShownStateInsets();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public int getTypes();
+    method public boolean isCancelled();
+    method public boolean isFinished();
+    method public boolean isReady();
+    method public void setInsetsAndAlpha(androidx.core.graphics.Insets?, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+  }
+
   public class WindowInsetsCompat {
     ctor public WindowInsetsCompat(androidx.core.view.WindowInsetsCompat?);
     method @Deprecated public androidx.core.view.WindowInsetsCompat consumeDisplayCutout();
@@ -3065,10 +3165,13 @@
 
   public final class WindowInsetsControllerCompat {
     ctor public WindowInsetsControllerCompat(android.view.Window, android.view.View);
+    method public void addOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void controlWindowInsetsAnimation(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, long, android.view.animation.Interpolator?, android.os.CancellationSignal?, androidx.core.view.WindowInsetsAnimationControlListenerCompat);
     method public int getSystemBarsBehavior();
     method public void hide(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
     method public boolean isAppearanceLightNavigationBars();
     method public boolean isAppearanceLightStatusBars();
+    method public void removeOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
     method public void setAppearanceLightNavigationBars(boolean);
     method public void setAppearanceLightStatusBars(boolean);
     method public void setSystemBarsBehavior(int);
@@ -3079,6 +3182,10 @@
     field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
   }
 
+  public static interface WindowInsetsControllerCompat.OnControllableInsetsChangedListener {
+    method public void onControllableInsetsChanged(androidx.core.view.WindowInsetsControllerCompat, @androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+  }
+
 }
 
 package androidx.core.view.accessibility {
@@ -3776,17 +3883,6 @@
     method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
   }
 
-  public abstract class RichContentReceiverCompat<T extends android.view.View> {
-    ctor public RichContentReceiverCompat();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final androidx.core.view.inputmethod.InputConnectionCompat.OnCommitContentListener buildOnCommitContentListener(T);
-    method public abstract java.util.Set<java.lang.String!> getSupportedMimeTypes();
-    method public abstract boolean onReceive(T, android.content.ClipData, int, int);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final void populateEditorInfoContentMimeTypes(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo?);
-    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
-    field public static final int SOURCE_CLIPBOARD = 0; // 0x0
-    field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
-  }
-
   @Deprecated public final class ScrollerCompat {
     method @Deprecated public void abortAnimation();
     method @Deprecated public boolean computeScrollOffset();
@@ -3845,10 +3941,9 @@
   @IntDef({androidx.core.widget.TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE, androidx.core.widget.TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface TextViewCompat.AutoSizeTextType {
   }
 
-  public abstract class TextViewRichContentReceiverCompat extends androidx.core.widget.RichContentReceiverCompat<android.widget.TextView> {
-    ctor public TextViewRichContentReceiverCompat();
-    method public java.util.Set<java.lang.String!> getSupportedMimeTypes();
-    method public boolean onReceive(android.widget.TextView, android.content.ClipData, int, int);
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class TextViewOnReceiveContentListener implements androidx.core.view.OnReceiveContentListener {
+    ctor public TextViewOnReceiveContentListener();
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
   }
 
   public interface TintableCompoundButton {
diff --git a/core/core/build.gradle b/core/core/build.gradle
index 7790a23..e0c74b9 100644
--- a/core/core/build.gradle
+++ b/core/core/build.gradle
@@ -10,7 +10,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0-alpha01")
+    api(project(":annotation:annotation"))
     api("androidx.lifecycle:lifecycle-runtime:2.0.0")
     api("androidx.versionedparcelable:versionedparcelable:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
@@ -26,13 +26,20 @@
     androidTestImplementation(TRUTH)
     androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MULTIDEX)
+
+    // Including both dexmakers allows support for all API levels plus final mocking support on
+    // API 28+. The implementation is swapped based on the finality of the mock type. This
+    // delegation is handled manually inside androidx.core.util.mockito.CustomMockMaker.
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO_INLINE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0") {
         exclude group: 'androidx.core', module: 'core'
     }
     androidTestImplementation project(':internal-testutils-runtime'), {
         exclude group: 'androidx.core', module: 'core'
     }
+    androidTestImplementation project(':internal-testutils-mockito')
 
     testImplementation(ANDROIDX_TEST_CORE)
     testImplementation(ANDROIDX_TEST_RUNNER)
@@ -55,6 +62,17 @@
     buildTypes.all {
         consumerProguardFiles 'proguard-rules.pro'
     }
+
+    packagingOptions {
+        // Drop the file from external dependencies, preferring the local file inside androidTest
+        pickFirsts = [
+                "mockito-extensions/org.mockito.plugins.MockMaker",
+                "mockito-extensions/org.mockito.plugins.StackTraceCleanerProvider"
+        ]
+    }
+    defaultConfig {
+        multiDexEnabled = true
+    }
 }
 
 androidx {
diff --git a/core/core/lint-baseline.xml b/core/core/lint-baseline.xml
index 7f2ea99..8c91e30 100644
--- a/core/core/lint-baseline.xml
+++ b/core/core/lint-baseline.xml
@@ -11537,17 +11537,6 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 16, the call containing class androidx.core.widget.TextViewRichContentReceiverCompat is not annotated with @RequiresApi(x) where x is at least 16. Either annotate the containing class with at least @RequiresApi(16) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(16)."
-        errorLine1="                    paste = clip.getItemAt(i).coerceToStyledText(context);"
-        errorLine2="                                              ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/widget/TextViewRichContentReceiverCompat.java"
-            line="82"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="UnsafeNewApiCall"
         message="This call is to a method from API 29, the call containing class androidx.core.os.TraceCompat 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="            return Trace.isEnabled();"
         errorLine2="                         ~~~~~~~~~">
diff --git a/core/core/src/androidTest/AndroidManifest.xml b/core/core/src/androidTest/AndroidManifest.xml
index d8170ee..9c5a7f8 100644
--- a/core/core/src/androidTest/AndroidManifest.xml
+++ b/core/core/src/androidTest/AndroidManifest.xml
@@ -34,7 +34,7 @@
 
         <activity android:name="androidx.core.widget.TextViewTestActivity"/>
 
-        <activity android:name="androidx.core.widget.RichContentReceiverTestActivity"/>
+        <activity android:name="androidx.core.widget.ReceiveContentTestActivity"/>
 
         <activity android:name="androidx.core.widget.TestContentViewActivity"/>
 
diff --git a/core/core/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatHasSignaturesTest.kt b/core/core/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatHasSignaturesTest.kt
new file mode 100644
index 0000000..53f5500
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatHasSignaturesTest.kt
@@ -0,0 +1,421 @@
+/*
+ * 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.core.content.pm
+
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.content.pm.Signature
+import android.content.pm.SigningInfo
+import android.os.Build
+import androidx.core.content.pm.PackageInfoCompatHasSignaturesTest.Companion.Params.QueryType
+import androidx.core.content.pm.PackageInfoCompatHasSignaturesTest.MockCerts.MockSignatures
+import androidx.core.content.pm.PackageInfoCompatHasSignaturesTest.MockCerts.MockSigningInfo
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.testutils.mockito.mockThrowOnUnmocked
+import androidx.testutils.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
+import org.mockito.internal.util.reflection.FieldSetter
+import java.security.MessageDigest
+
+/**
+ * Verifies [PackageInfoCompat.hasSignatures].
+ *
+ * Due to testability restrictions with the [SigningInfo] and [Signature] classes and
+ * infrastructure for install test packages in a device test, this test uses mocked classes to
+ * verify the correct method calls. Mocking in general is preferable to signing several test
+ * packages as this isolates the test parameters to inside the test class.
+ *
+ * As final class mocking is only available starting from [Build.VERSION_CODES.P], this test
+ * manually runs itself for both [Build.VERSION_CODES.O] and the current device SDK version
+ * by swapping [Build.VERSION.SDK_INT].
+ */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+@LargeTest
+@RunWith(Parameterized::class)
+class PackageInfoCompatHasSignaturesTest {
+
+    companion object {
+        // Following are random public certs (effectively random strings) as this test does not
+        // validate the actual signature integrity. Only the fact that the hashes and comparisons
+        // work and return the correct values.
+
+        private const val CERT_1 = "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d494" +
+            "9422b44434341574767417749424167495548384f42374c355a53594f7852577056774e454f4c336" +
+            "5726c5077774451594a4b6f5a496876634e4151454c0a425141774454454c4d416b4741315545426" +
+            "84d4356564d774942634e4d6a41774f5449794d6a4d774d6a557a576867504d7a41794d4441784d6" +
+            "a51794d7a41790a4e544e614d413078437a414a42674e5642415954416c56544d4947664d4130474" +
+            "35371475349623344514542415155414134474e4144434269514b42675144450a6f3650386341636" +
+            "c77734a646e773457415a755a685244795031556473334d5766703738434448344548614d682f393" +
+            "54a7941316e5a776e2f644174747375640a6e464356713065592b32736d373663334d454a542b456" +
+            "86b443170792f6148324f366c3639314d2b334e7a6a616272752f4c457451364d736232494553454" +
+            "2690a7a63415350756a4a635458586b346a6d44535a4d6d6359653259466d506b633151534f31387" +
+            "875446a514944415141426f314d775554416442674e56485134450a4667515534746446716839634" +
+            "16d4d35707665674d514265476c442b4b774d77487759445652306a42426777466f4155347464467" +
+            "1683963416d4d35707665670a4d514265476c442b4b774d7744775944565230544151482f4241557" +
+            "7417745422f7a414e42676b71686b6947397730424151734641414f426751436a70535a760a4d546" +
+            "76f584c3042304b393577486b61353476685a6c2f5a4c6231427243752f686431746761736766434" +
+            "9566d4d34754335614774697a422b4a3335462f4f2b0a5344572b62585854314c634b4951795a625" +
+            "66772335537736c39584f5773322f55474a33653739555948473144656f497235367534475074312" +
+            "b5338746347500a464b36496e4e42534a56584a325231446b7a754e5843476d63766a4d7a4e426b7" +
+            "47034504d773d3d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a"
+
+        private const val CERT_2 = "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d494" +
+            "9422b4443434157476741774942416749554739426d31332f566c61747370564461486d46574f6c7" +
+            "65a696b45774451594a4b6f5a496876634e4151454c0a425141774454454c4d416b4741315545426" +
+            "84d4356564d774942634e4d6a41774f5449794d6a4d774d7a4130576867504d7a41794d4441784d6" +
+            "a51794d7a417a0a4d4452614d413078437a414a42674e5642415954416c56544d4947664d4130474" +
+            "35371475349623344514542415155414134474e4144434269514b42675144510a595875516f67783" +
+            "4324c77572b3568656b6f694c50507178655964494250555668743442584d6e494f7835434449665" +
+            "96d6461424650645865685546395036340a7974576a2b316963677452776e4c2f62487a525953413" +
+            "637514c39492b7a45456e2b7342777779566f51325858644c51546f49394f537a54444375744f4c4" +
+            "2430a6f65754f46727373566642676f4d6838685a4f5a31775448442f706c6b38543541384463313" +
+            "7505159774944415141426f314d775554416442674e56485134450a466751554c7a5754614673507" +
+            "23161526d304166556569704b346d6d75785977487759445652306a42426777466f41554c7a57546" +
+            "1467350723161526d3041660a556569704b346d6d7578597744775944565230544151482f4241557" +
+            "7417745422f7a414e42676b71686b6947397730424151734641414f42675141496147524d0a4d423" +
+            "74c74464957714847542f69766f56572b4f6a58664f477332554f75416455776d7a6b374b7a57727" +
+            "874744639616a355250307756637755625654444e740a464c326b4c3171574450513471613333643" +
+            "34744325555416b49474b724d514668523839756a303438514c7871386a72466f663447324572755" +
+            "85353354d79790a5669573735357038354f50704c635a753939796c2b536d7675633938685170796" +
+            "a6f564f6c773d3d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a"
+
+        private const val CERT_3 = "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d494" +
+            "9422b444343415747674177494241674955484f6f6d736b2b642f79336c4854434e3371675166413" +
+            "335646e51774451594a4b6f5a496876634e4151454c0a425141774454454c4d416b4741315545426" +
+            "84d4356564d774942634e4d6a41774f5449794d6a4d774d7a4578576867504d7a41794d4441784d6" +
+            "a51794d7a417a0a4d5446614d413078437a414a42674e5642415954416c56544d4947664d4130474" +
+            "35371475349623344514542415155414134474e4144434269514b42675144520a6355494b3450724" +
+            "d396930685834546168485055334c575665677630546668307273785153637042496a73306b6a6a6" +
+            "34e78342f31363948674c70476f5a334d0a63424350612f61574a4778794c7145514537774b77644" +
+            "a6148596b4b56706e55706a4d313030634b6b6b4a356565336b56414958746f2f6c436b626b554a6" +
+            "1730a47334f71307677774936656130707336684350313863693066727844766d6630536e2b54615" +
+            "2396a31774944415141426f314d775554416442674e56485134450a466751554464553443534c393" +
+            "746516d774954555332444e4472356b464c5177487759445652306a42426777466f4155446455344" +
+            "3534c393746516d774954550a5332444e4472356b464c517744775944565230544151482f4241557" +
+            "7417745422f7a414e42676b71686b6947397730424151734641414f426751437a567054470a59796" +
+            "1444a6c456279447775443457616b38306d5a4153613534646a69446e6335324d30776e614145776" +
+            "84e496978623547465a50357878337859302f494c520a6a72544a6e6744377643586c556f5256384" +
+            "379794653534169306f3977544b475554434d762b303446324a6c474a4b7665486a346d473544746" +
+            "6335331574b520a6644454a792b456376563658314b716a73466d524a4a6d7a30347464525363304" +
+            "c74622f2f673d3d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a"
+
+        private const val CERT_4 = "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d494" +
+            "9422b444343415747674177494241674955526f49427173485858413246636938324d412b73706d5" +
+            "6684d7634774451594a4b6f5a496876634e4151454c0a425141774454454c4d416b4741315545426" +
+            "84d4356564d774942634e4d6a41774f5449304d546b784d7a4d77576867504d7a41794d4441784d6" +
+            "a59784f54457a0a4d7a42614d413078437a414a42674e5642415954416c56544d4947664d4130474" +
+            "35371475349623344514542415155414134474e4144434269514b426751432b0a306549525344554" +
+            "e4972666663486f4d61697431705a6b39534769616c41694e56484b6e4950466876754233497a475" +
+            "05a4d476f6d6a3956534667766e7047360a4f7166453033734e575949503944776772485546692f6" +
+            "e356f45504f742f617643746b4b71623957737531777643746b37795163354d626276644e6b78344" +
+            "c740a3679724a7151545946424479356c49624c67454b4d744a5344584246356a38747173326e705" +
+            "145514f774944415141426f314d775554416442674e56485134450a4667515557354c6e5751344f3" +
+            "2523576515731355452564955726f744e476b77487759445652306a42426777466f415557354c6e5" +
+            "751344f32523576515731350a5452564955726f744e476b7744775944565230544151482f4241557" +
+            "7417745422f7a414e42676b71686b6947397730424151734641414f42675141685768654f0a77525" +
+            "85339365536444a705459597374754741634a77414e434d3244503938325653613136766e7769653" +
+            "842477a6a724a51794f354e4a4846637a4f566e54330a626834496a65337751787551334138566e4" +
+            "54d334230683553373030524c524337373936555a787465683874304f6c7a515031703358452b776" +
+            "571797a6c4e330a4f494f435a486f6b494b6f4957527964734e58547a55353448625850597275556" +
+            "e71574451673d3d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a"
+
+        private const val TEST_PKG_NAME = "com.example.app"
+
+        private val nullCerts: List<Certificate>? = null
+        private val emptyCerts = emptyList<Certificate>()
+        private val multiSignerCerts = listOf(CERT_1, CERT_3).map(::Certificate)
+        private val pastHistoryCerts = listOf(CERT_1, CERT_2, CERT_3).map(::Certificate)
+        private val noHistoryCerts = listOf(CERT_1).map(::Certificate)
+        private val extraCert = Certificate(CERT_4)
+
+        data class Params(
+            val sdkVersion: Int,
+            val mockCerts: MockCerts,
+            val queryType: QueryType,
+            val certType: CertType,
+            val matchExact: Boolean
+        ) {
+            enum class CertType(val flag: Int) {
+                X509(PackageManager.CERT_INPUT_RAW_X509),
+                SHA256(PackageManager.CERT_INPUT_SHA256)
+            }
+
+            enum class QueryType { NONE, EXACT_COUNT, FEWER, MORE }
+
+            val queryCerts = when (queryType) {
+                QueryType.NONE -> emptyList()
+                QueryType.EXACT_COUNT -> mockCerts.certificates.orEmpty()
+                QueryType.FEWER -> mockCerts.certificates!!.drop(1)
+                QueryType.MORE -> mockCerts.certificates.orEmpty() + extraCert
+            }
+
+            val success = when (mockCerts.certificates) {
+                // If the certs returned in the packgae are null/empty, the query can never succeed
+                nullCerts, emptyCerts -> false
+                // Otherwise success depends on what the query set is
+                else -> when (queryType) {
+                    // None always fails, to ensure verify cannot accidentally succeed
+                    QueryType.NONE -> false
+                    // If querying the exact same certs, then always succeed
+                    QueryType.EXACT_COUNT -> true
+                    // Otherwise if querying fewer, only succeed if not matching exactly all
+                    QueryType.FEWER -> !matchExact
+                    // Otherwise matching more than available, which should always fail
+                    QueryType.MORE -> false
+                }
+            }
+
+            // For naming the test method variant
+            override fun toString(): String {
+                val certsVariant = when (mockCerts.certificates) {
+                    nullCerts -> "null"
+                    emptyCerts -> "empty"
+                    multiSignerCerts -> "multiSign"
+                    pastHistoryCerts -> "pastHistory"
+                    noHistoryCerts -> "noHistory"
+                    else -> throw IllegalArgumentException("Invalid mockCerts $mockCerts")
+                }
+
+                @Suppress("DEPRECATION")
+                val queryFlag = when (val flag = mockCerts.flag) {
+                    PackageManager.GET_SIGNATURES -> "GET_SIGNATURES"
+                    PackageManager.GET_SIGNING_CERTIFICATES -> "GET_SIGNING_CERTIFICATES"
+                    else -> throw IllegalArgumentException("Invalid certs type $flag")
+                }
+
+                val sdkVersionName = sdkVersion.takeUnless { it == Build.VERSION.SDK_INT }
+                    ?: "current"
+
+                return "$queryFlag," +
+                    "$certsVariant${certType.name}," +
+                    "sdkVersion=$sdkVersionName," +
+                    "query=$queryType," +
+                    "matchExact=$matchExact"
+            }
+        }
+
+        @Suppress("DEPRECATION")
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun parameters(): Array<Params> {
+            return listOf(
+                listOf(
+                    MockSignatures(nullCerts),
+                    MockSignatures(emptyCerts),
+                    MockSignatures(multiSignerCerts),
+                    // Legacy GET_SIGNATURES cannot include certificate history
+                    MockSignatures(noHistoryCerts)
+                ).associateWith { Build.VERSION_CODES.O_MR1 },
+                listOf(
+                    MockSigningInfo(
+                        multiSigners = false,
+                        hasHistory = null,
+                        contentsSigners = null,
+                        certHistory = null
+                    ),
+                    MockSigningInfo(
+                        multiSigners = false,
+                        hasHistory = null,
+                        contentsSigners = null,
+                        certHistory = emptyCerts
+                    ),
+                    MockSigningInfo(
+                        multiSigners = true,
+                        hasHistory = null,
+                        contentsSigners = multiSignerCerts,
+                        certHistory = null
+                    ),
+                    MockSigningInfo(
+                        multiSigners = false,
+                        hasHistory = true,
+                        contentsSigners = null,
+                        certHistory = pastHistoryCerts
+                    ),
+                    MockSigningInfo(
+                        multiSigners = false,
+                        hasHistory = false,
+                        contentsSigners = null,
+                        certHistory = noHistoryCerts
+                    )
+                ).associateWith { Build.VERSION.SDK_INT }
+            )
+                .flatMap {
+                    // Multiply all base params by QueryType, CertType and matchExact values to
+                    // get the complete set of possibilities
+                    it.entries.flatMap { (mockCerts, sdkVersion) ->
+                        listOfNotNull(
+                            QueryType.NONE,
+                            QueryType.EXACT_COUNT,
+                            QueryType.MORE,
+                            QueryType.FEWER.takeIf {
+                                val certificates = mockCerts.certificates
+                                !certificates.isNullOrEmpty() && certificates.size > 1
+                            }
+                        ).flatMap { queryType ->
+                            listOf(
+                                Params.CertType.X509,
+                                Params.CertType.SHA256
+                            ).flatMap { certType ->
+                                listOf(true, false).map { matchExact ->
+                                    Params(sdkVersion, mockCerts, queryType, certType, matchExact)
+                                }
+                            }
+                        }
+                    }
+                }
+                .toTypedArray()
+        }
+
+        private val sdkIntField = Build.VERSION::class.java.getDeclaredField("SDK_INT")
+
+        private fun setDeviceSdkVersion(sdkVersion: Int) {
+            FieldSetter.setField(Build.VERSION::class.java, sdkIntField, sdkVersion)
+            assertThat(Build.VERSION.SDK_INT).isEqualTo(sdkVersion)
+        }
+    }
+
+    @Parameterized.Parameter(0)
+    lateinit var params: Params
+
+    private var savedSdkVersion: Int = Build.VERSION.SDK_INT
+
+    @Before
+    fun saveSdkVersion() {
+        savedSdkVersion = Build.VERSION.SDK_INT
+    }
+
+    @After
+    fun resetSdkVersion() {
+        if (Build.VERSION.SDK_INT != savedSdkVersion) {
+            setDeviceSdkVersion(savedSdkVersion)
+        }
+    }
+
+    @Test
+    fun verify() {
+        val mock = mockPackageManager()
+        val certs = params.queryCerts.map { it.bytes(params.certType) }
+            .associateWith { params.certType.flag }
+
+        // SDK_INT must be changed after mocks are built, since MockMaker will do an SDK check
+        if (Build.VERSION.SDK_INT != params.sdkVersion) {
+            setDeviceSdkVersion(params.sdkVersion)
+        }
+
+        assertThat(PackageInfoCompat.hasSignatures(mock, TEST_PKG_NAME, certs, params.matchExact))
+            .isEqualTo(params.success)
+
+        if (Build.VERSION.SDK_INT != savedSdkVersion) {
+            setDeviceSdkVersion(savedSdkVersion)
+        }
+    }
+
+    private fun mockPackageManager() = mockThrowOnUnmocked<PackageManager> {
+        val mockCerts = params.mockCerts
+        whenever(getPackageInfo(TEST_PKG_NAME, params.mockCerts.flag)) {
+            PackageInfo().apply {
+                when (mockCerts) {
+                    is MockSignatures -> {
+                        @Suppress("DEPRECATION")
+                        signatures = mockCerts.certificates?.map { it.signature }?.toTypedArray()
+                    }
+                    is MockSigningInfo -> {
+                        signingInfo = mockThrowOnUnmocked<SigningInfo> {
+                            whenever(hasMultipleSigners()) { mockCerts.multiSigners }
+
+                            mockCerts.hasHistory?.let {
+                                // Only allow this method if params specify it. to ensure past
+                                // certificates aren't considered when multi-signing is enabled
+                                whenever(hasPastSigningCertificates()) { it }
+                            }
+
+                            mockCerts.contentsSigners
+                                ?.map { it.signature }
+                                ?.toTypedArray()
+                                ?.let { whenever(apkContentsSigners) { it } }
+
+                            // Only allow fetching history if not multi signed
+                            if (!hasMultipleSigners()) {
+                                whenever(signingCertificateHistory) {
+                                    mockCerts.certHistory
+                                        ?.map { it.signature }
+                                        ?.toTypedArray()
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!params.matchExact && params.sdkVersion >= Build.VERSION_CODES.P) {
+            whenever(hasSigningCertificate(eq(TEST_PKG_NAME), any(), anyInt())) {
+                val certs = params.mockCerts.certificates?.asSequence() ?: return@whenever false
+                val query = getArgument(1) as ByteArray
+                val certType = when (val type = getArgument(2) as Int) {
+                    PackageManager.CERT_INPUT_RAW_X509 -> Params.CertType.X509
+                    PackageManager.CERT_INPUT_SHA256 -> Params.CertType.SHA256
+                    else -> throw IllegalArgumentException("Invalid type $type")
+                }
+                certs.map { it.bytes(certType) }.contains(query)
+            }
+        }
+    }
+
+    sealed class MockCerts {
+        abstract val certificates: List<Certificate>?
+        abstract val flag: Int
+
+        data class MockSignatures(override val certificates: List<Certificate>?) : MockCerts() {
+            @Suppress("DEPRECATION")
+            override val flag = PackageManager.GET_SIGNATURES
+        }
+
+        data class MockSigningInfo(
+            val multiSigners: Boolean,
+            val hasHistory: Boolean?,
+            val contentsSigners: List<Certificate>?,
+            val certHistory: List<Certificate>?
+        ) : MockCerts() {
+            override val certificates = contentsSigners ?: certHistory
+            override val flag = PackageManager.GET_SIGNING_CERTIFICATES
+        }
+    }
+
+    /**
+     * [Signature] wrapper to cache arrays and digests.
+     */
+    data class Certificate(val publicCertX509: String) {
+        val signature = Signature(publicCertX509)
+        private val x509Bytes = signature.toByteArray()!!
+        private val sha256Bytes = MessageDigest.getInstance("SHA256").digest(x509Bytes)
+
+        fun bytes(certType: Params.CertType): ByteArray = when (certType) {
+            Params.CertType.X509 -> x509Bytes
+            Params.CertType.SHA256 -> sha256Bytes
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/core/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatTest.java
index 3ea56a4..b0f7a5b 100644
--- a/core/core/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatTest.java
@@ -18,17 +18,34 @@
 
 import static android.os.Build.VERSION_CODES.P;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 
+import android.content.Context;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
 
+import androidx.collection.ArrayMap;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Test;
 
+import java.util.List;
+import java.util.Map;
+
 @SmallTest
 public final class PackageInfoCompatTest {
+
+    private static final String NON_EXISTENT_PACKAGE = "com.example.app.non_existent_package_name";
+
+    private final Context mContext =
+            InstrumentationRegistry.getInstrumentation().getTargetContext();
+    private PackageManager mPackageManager = mContext.getPackageManager();
+
     @Test
     public void getLongVersionCodeLowerBitsOnly() {
         PackageInfo info = new PackageInfo();
@@ -45,4 +62,49 @@
 
         assertEquals(Long.MAX_VALUE, PackageInfoCompat.getLongVersionCode(info));
     }
+
+    /**
+     * Only verifies non-null return, to avoid hard coding certs. Actual equality and proper
+     * return value is verified as part of {@link PackageInfoCompatHasSignaturesTest}.
+     */
+    @Test
+    public void getSignaturesNonNull() throws PackageManager.NameNotFoundException {
+        List<Signature> signatures = PackageInfoCompat.getSignatures(mPackageManager,
+                mContext.getPackageName());
+
+        assertThat(signatures).isNotEmpty();
+    }
+
+    @Test(expected = PackageManager.NameNotFoundException.class)
+    public void getSignaturesThrowOnNotFound() throws PackageManager.NameNotFoundException {
+        PackageInfoCompat.getSignatures(mPackageManager, NON_EXISTENT_PACKAGE);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void hasSignaturesThrowOnInvalidType() throws PackageManager.NameNotFoundException {
+        Map<byte[], Integer> map = new ArrayMap<>(1);
+        map.put(new byte[100], PackageManager.CERT_INPUT_SHA256 + 1);
+        PackageInfoCompat.hasSignatures(mPackageManager, mContext.getPackageName(), map, false);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void hasSignaturesThrowOnNullBytes() throws PackageManager.NameNotFoundException {
+        Map<byte[], Integer> map = new ArrayMap<>(1);
+        map.put(null, PackageManager.CERT_INPUT_SHA256);
+        PackageInfoCompat.hasSignatures(mPackageManager, mContext.getPackageName(), map, false);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void hasSignaturesThrowOnNullType() throws PackageManager.NameNotFoundException {
+        Map<byte[], Integer> map = new ArrayMap<>(1);
+        map.put(new byte[100], null);
+        PackageInfoCompat.hasSignatures(mPackageManager, mContext.getPackageName(), map, false);
+    }
+
+    @Test(expected = PackageManager.NameNotFoundException.class)
+    public void hasSignaturesThrowOnNotFound() throws PackageManager.NameNotFoundException {
+        Map<byte[], Integer> map = new ArrayMap<>(1);
+        map.put(new byte[100], PackageManager.CERT_INPUT_SHA256);
+        PackageInfoCompat.hasSignatures(mPackageManager, NON_EXISTENT_PACKAGE, map, false);
+    }
 }
diff --git a/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutManagerCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutManagerCompatTest.java
index dd8ee2d..157ed9c 100644
--- a/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutManagerCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutManagerCompatTest.java
@@ -85,6 +85,7 @@
 import java.util.Arrays;
 import java.util.List;
 
+@SuppressWarnings("unchecked")
 @RunWith(AndroidJUnit4.class)
 public class ShortcutManagerCompatTest extends BaseInstrumentationTestCase<TestActivity> {
 
diff --git a/core/core/src/androidTest/java/androidx/core/util/mockito/CustomMockMaker.kt b/core/core/src/androidTest/java/androidx/core/util/mockito/CustomMockMaker.kt
new file mode 100644
index 0000000..bfa1be2
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/util/mockito/CustomMockMaker.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.core.util.mockito
+
+import android.os.Build
+import com.android.dx.mockito.DexmakerMockMaker
+import com.android.dx.mockito.inline.InlineDexmakerMockMaker
+import org.mockito.invocation.MockHandler
+import org.mockito.mock.MockCreationSettings
+import org.mockito.plugins.InlineMockMaker
+import org.mockito.plugins.MockMaker
+
+/**
+ * There is no official supported method for mixing dexmaker-mockito with dexmaker-mockito-inline,
+ * so this has to be done manually.
+ *
+ * Inside the build.gradle, dexmaker-mockito is taken first and preferred, and this custom
+ * implementation is responsible for delegating to the inline variant should the regular variant
+ * fall to instantiate a mock.
+ *
+ * This allows Mockito to mock final classes on test run on API 28+ devices, while still
+ * functioning for normal non-final mocks API <28.
+ *
+ * This class is placed in the core sources since the use case is rather unique to
+ * [androidx.core.content.pm.PackageInfoCompatHasSignaturesTest], and other testing solutions should
+ * be considered before using this in other modules.
+ */
+class CustomMockMaker : InlineMockMaker {
+
+    companion object {
+        private val MOCK_MAKERS = mutableListOf<MockMaker>(DexmakerMockMaker()).apply {
+            // Inline only works on API 28+
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+                this += InlineDexmakerMockMaker()
+            }
+        }
+    }
+
+    override fun <T> createMock(settings: MockCreationSettings<T>, handler: MockHandler<*>): T? {
+        var lastException: Exception? = null
+        MOCK_MAKERS
+            .filter { it.isTypeMockable(settings.typeToMock).mockable() }
+            .forEach {
+                val mock = try {
+                    it.createMock(settings, handler)
+                } catch (e: Exception) {
+                    lastException = e
+                    null
+                }
+
+                if (mock != null) {
+                    return mock
+                }
+            }
+
+        lastException?.let { throw it }
+        return null
+    }
+
+    override fun getHandler(mock: Any?): MockHandler<*>? {
+        MOCK_MAKERS.forEach {
+            val handler = it.getHandler(mock)
+            if (handler != null) {
+                return handler
+            }
+        }
+        return null
+    }
+
+    override fun resetMock(
+        mock: Any?,
+        newHandler: MockHandler<*>?,
+        settings: MockCreationSettings<*>?
+    ) {
+        MOCK_MAKERS.forEach {
+            it.resetMock(mock, newHandler, settings)
+        }
+    }
+
+    override fun isTypeMockable(type: Class<*>?): MockMaker.TypeMockability? {
+        MOCK_MAKERS.forEachIndexed { index, mockMaker ->
+            val mockability = mockMaker.isTypeMockable(type)
+            // Prefer the first mockable instance, or the last one available
+            if (mockability.mockable() || index == MOCK_MAKERS.size - 1) {
+                return mockability
+            }
+        }
+        return null
+    }
+
+    override fun clearMock(mock: Any?) {
+        MOCK_MAKERS.forEach {
+            (it as? InlineMockMaker)?.clearMock(mock)
+        }
+    }
+
+    override fun clearAllMocks() {
+        MOCK_MAKERS.forEach {
+            (it as? InlineMockMaker)?.clearAllMocks()
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/core/src/androidTest/java/androidx/core/util/mockito/CustomStackTraceCleaner.kt b/core/core/src/androidTest/java/androidx/core/util/mockito/CustomStackTraceCleaner.kt
new file mode 100644
index 0000000..d927ef5
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/util/mockito/CustomStackTraceCleaner.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.core.util.mockito
+
+import com.android.dx.mockito.DexmakerMockMaker
+import com.android.dx.mockito.inline.DexmakerStackTraceCleaner
+import org.mockito.exceptions.stacktrace.StackTraceCleaner
+import org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleaner
+
+/**
+ * Similar to [CustomMockMaker], delegates the stack cleaner logic to mix dexmaker-mockito and
+ * dexmaker-mockito-inline.
+ */
+class CustomStackTraceCleaner : StackTraceCleaner {
+
+    companion object {
+        private val CLEANER_WRAPPER = DefaultStackTraceCleaner()
+            .let { DexmakerMockMaker().getStackTraceCleaner(it) }
+            .let { DexmakerStackTraceCleaner().getStackTraceCleaner(it) }
+    }
+
+    override fun isIn(candidate: StackTraceElement?) = CLEANER_WRAPPER.isIn(candidate)
+}
\ No newline at end of file
diff --git a/core/core/src/androidTest/java/androidx/core/view/ContentInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/ContentInfoCompatTest.java
new file mode 100644
index 0000000..15b5fa7b
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/ContentInfoCompatTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.core.view;
+
+import static androidx.core.view.ContentInfoCompat.SOURCE_CLIPBOARD;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ClipData;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Pair;
+
+import androidx.core.util.Predicate;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ContentInfoCompatTest {
+
+    @Test
+    public void testPartition_multipleItems() throws Exception {
+        Uri sampleUri = Uri.parse("content://com.example/path");
+        ClipData clip = ClipData.newPlainText("", "Hello");
+        clip.addItem(new ClipData.Item("Hi", "<b>Salut</b>"));
+        clip.addItem(new ClipData.Item(sampleUri));
+        ContentInfoCompat payload = new ContentInfoCompat.Builder(clip, SOURCE_CLIPBOARD)
+                .setFlags(ContentInfoCompat.FLAG_CONVERT_TO_PLAIN_TEXT)
+                .setLinkUri(Uri.parse("http://example.com"))
+                .setExtras(new Bundle())
+                .build();
+
+        // Test splitting when some items match and some don't.
+        Pair<ContentInfoCompat, ContentInfoCompat> split;
+        split = payload.partition(new Predicate<ClipData.Item>() {
+            @Override
+            public boolean test(ClipData.Item item) {
+                return item.getUri() != null;
+            }
+        });
+        assertThat(split.first.getClip().getItemCount()).isEqualTo(1);
+        assertThat(split.second.getClip().getItemCount()).isEqualTo(2);
+        assertThat(split.first.getClip().getItemAt(0).getUri()).isEqualTo(sampleUri);
+        assertThat(split.first.getClip().getDescription()).isNotSameInstanceAs(
+                payload.getClip().getDescription());
+        assertThat(split.second.getClip().getDescription()).isNotSameInstanceAs(
+                payload.getClip().getDescription());
+        assertThat(split.first.getSource()).isEqualTo(SOURCE_CLIPBOARD);
+        assertThat(split.first.getLinkUri()).isNotNull();
+        assertThat(split.first.getExtras()).isNotNull();
+        assertThat(split.second.getSource()).isEqualTo(SOURCE_CLIPBOARD);
+        assertThat(split.second.getLinkUri()).isNotNull();
+        assertThat(split.second.getExtras()).isNotNull();
+
+        // Test splitting when none of the items match.
+        split = payload.partition(new Predicate<ClipData.Item>() {
+            @Override
+            public boolean test(ClipData.Item item) {
+                return false;
+            }
+        });
+        assertThat(split.first).isNull();
+        assertThat(split.second).isSameInstanceAs(payload);
+
+        // Test splitting when all of the items match.
+        split = payload.partition(new Predicate<ClipData.Item>() {
+            @Override
+            public boolean test(ClipData.Item item) {
+                return true;
+            }
+        });
+        assertThat(split.first).isSameInstanceAs(payload);
+        assertThat(split.second).isNull();
+    }
+
+    @Test
+    public void testPartition_singleItem() throws Exception {
+        ClipData clip = ClipData.newPlainText("", "Hello");
+        ContentInfoCompat payload = new ContentInfoCompat.Builder(clip, SOURCE_CLIPBOARD)
+                .setFlags(ContentInfoCompat.FLAG_CONVERT_TO_PLAIN_TEXT)
+                .setLinkUri(Uri.parse("http://example.com"))
+                .setExtras(new Bundle())
+                .build();
+
+        Pair<ContentInfoCompat, ContentInfoCompat> split;
+        split = payload.partition(new Predicate<ClipData.Item>() {
+            @Override
+            public boolean test(ClipData.Item item) {
+                return false;
+            }
+        });
+        assertThat(split.first).isNull();
+        assertThat(split.second).isSameInstanceAs(payload);
+
+        split = payload.partition(new Predicate<ClipData.Item>() {
+            @Override
+            public boolean test(ClipData.Item item) {
+                return true;
+            }
+        });
+        assertThat(split.first).isSameInstanceAs(payload);
+        assertThat(split.second).isNull();
+    }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/ViewCompatReceiveContentTest.java b/core/core/src/androidTest/java/androidx/core/view/ViewCompatReceiveContentTest.java
new file mode 100644
index 0000000..97d1d7b
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/ViewCompatReceiveContentTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.core.view;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.R;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewCompatReceiveContentTest {
+    @Rule
+    public final ActivityTestRule<ViewCompatActivity> mActivityTestRule =
+            new ActivityTestRule<>(ViewCompatActivity.class);
+
+    private View mView;
+    private OnReceiveContentListener mMockReceiver;
+
+    @UiThreadTest
+    @Before
+    public void before() {
+        final Activity activity = mActivityTestRule.getActivity();
+        mView = activity.findViewById(androidx.core.test.R.id.view);
+        mMockReceiver = Mockito.mock(OnReceiveContentListener.class);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testSetOnReceiveContentListener() throws Exception {
+        // Verify that by default the getters returns null.
+        assertThat(ViewCompat.getOnReceiveContentMimeTypes(mView)).isNull();
+        assertThat(getListener(mView)).isNull();
+
+        // Verify that setting non-null MIME types and a non-null receiver works.
+        String[] mimeTypes = new String[] {"image/*", "video/mp4"};
+        ViewCompat.setOnReceiveContentListener(mView, mimeTypes, mMockReceiver);
+        assertThat(ViewCompat.getOnReceiveContentMimeTypes(mView)).isSameInstanceAs(mimeTypes);
+        assertThat(getListener(mView)).isSameInstanceAs(mMockReceiver);
+
+        // Verify that setting null MIME types and a null receiver works.
+        ViewCompat.setOnReceiveContentListener(mView, null, null);
+        assertThat(ViewCompat.getOnReceiveContentMimeTypes(mView)).isNull();
+        assertThat(getListener(mView)).isNull();
+
+        // Verify that setting empty MIME types and a null receiver works.
+        ViewCompat.setOnReceiveContentListener(mView, new String[0], null);
+        assertThat(ViewCompat.getOnReceiveContentMimeTypes(mView)).isNull();
+        assertThat(getListener(mView)).isNull();
+
+        // Verify that setting MIME types with a null receiver works.
+        ViewCompat.setOnReceiveContentListener(mView, mimeTypes, null);
+        assertThat(ViewCompat.getOnReceiveContentMimeTypes(mView)).isSameInstanceAs(mimeTypes);
+        assertThat(getListener(mView)).isNull();
+
+        // Verify that setting null or empty MIME types with a non-null receiver is not allowed.
+        try {
+            ViewCompat.setOnReceiveContentListener(mView, null, mMockReceiver);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) { }
+        try {
+            ViewCompat.setOnReceiveContentListener(mView, new String[0], mMockReceiver);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) { }
+
+        // Verify that passing "*/*" as a MIME type is not allowed.
+        try {
+            ViewCompat.setOnReceiveContentListener(mView, new String[] {"image/gif", "*/*"},
+                    mMockReceiver);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) { }
+    }
+
+    @Nullable
+    private static OnReceiveContentListener getListener(@NonNull View view) {
+        return (OnReceiveContentListener) view.getTag(R.id.tag_on_receive_content_listener);
+    }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/WindowInsetsAnimationCompatActivityTest.kt b/core/core/src/androidTest/java/androidx/core/view/WindowInsetsAnimationCompatActivityTest.kt
new file mode 100644
index 0000000..22cfe2b
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/WindowInsetsAnimationCompatActivityTest.kt
@@ -0,0 +1,286 @@
+/*
+ * 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.core.view
+
+import android.os.Build
+import android.view.View
+import androidx.annotation.RequiresApi
+import androidx.core.test.R
+import androidx.core.view.WindowInsetsCompat.Type.systemBars
+import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso.onIdle
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.testutils.withActivity
+import com.google.common.truth.Truth
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+@SdkSuppress(minSdkVersion = 21)
+@RequiresApi(21)
+@LargeTest
+public class WindowInsetsAnimationCompatActivityTest {
+
+    private lateinit var scenario: ActivityScenario<WindowInsetsCompatActivity>
+
+    @Before
+    public fun setup() {
+        scenario = ActivityScenario.launch(WindowInsetsCompatActivity::class.java)
+        onIdle()
+        // Close the IME if it's open, so we start from a known scenario
+        onView(withId(R.id.edittext)).perform(ViewActions.closeSoftKeyboard())
+        scenario.onActivity {
+            WindowCompat.setDecorFitsSystemWindows(it.window, false)
+            WindowCompat.getInsetsController(it.window, it.window.decorView)!!.show(systemBars())
+        }
+        onIdle()
+    }
+
+    @Test
+    public fun add_remove_listener() {
+        assumeNotCuttlefish()
+
+        val container = scenario.withActivity { findViewById(R.id.container) }
+        var applyInsetsCalled = false
+        var insetsAnimationCallbackCalled = false
+        var latch = CountDownLatch(2)
+        val animationCallback = createCallback(
+            onPrepare = {
+                insetsAnimationCallbackCalled = true
+                latch.countDown()
+            }
+        )
+        val insetListener: (v: View, insets: WindowInsetsCompat) -> WindowInsetsCompat =
+            { _, insetsCompat ->
+                applyInsetsCalled = true
+                latch.countDown()
+                insetsCompat
+            }
+
+        // Check that both ApplyWindowInsets and the Animation Callback are called
+        ViewCompat.setOnApplyWindowInsetsListener(container, insetListener)
+        ViewCompat.setWindowInsetsAnimationCallback(container, animationCallback)
+        triggerInsetAnimation(container)
+        latch.await(4, TimeUnit.SECONDS)
+        assertTrue(
+            "The WindowInsetsAnimationCallback has not been called",
+            insetsAnimationCallbackCalled
+        )
+        assertTrue(
+            "onApplyWindowInsetsListener has not been called",
+            applyInsetsCalled
+        )
+        resetBars(container)
+
+        // Remove the applyWindowInsets listener and check that the animation callback is still
+        // called
+        applyInsetsCalled = false
+        insetsAnimationCallbackCalled = false
+        latch = CountDownLatch(1)
+        ViewCompat.setOnApplyWindowInsetsListener(container, null)
+        triggerInsetAnimation(container)
+        latch.await(4, TimeUnit.SECONDS)
+        assertFalse(
+            "onApplyWindowInsetsListener should NOT have been called",
+            applyInsetsCalled
+        )
+        assertTrue(
+            "The WindowInsetsAnimationCallback has not been called",
+            insetsAnimationCallbackCalled
+        )
+
+        resetBars(container)
+
+        // Add an applyWindowInsets listener and remove the animation callback and check if the
+        // listener is called
+        applyInsetsCalled = false
+        insetsAnimationCallbackCalled = false
+        latch = CountDownLatch(1)
+        ViewCompat.setOnApplyWindowInsetsListener(container, insetListener)
+        ViewCompat.setWindowInsetsAnimationCallback(container, null)
+        triggerInsetAnimation(container)
+        latch.await(4, TimeUnit.SECONDS)
+        assertTrue("onApplyWindowInsetsListener has not been called", applyInsetsCalled)
+        assertFalse(
+            "The WindowInsetsAnimationCallback should NOT have been called",
+            insetsAnimationCallbackCalled
+        )
+    }
+
+    @Test
+    public fun all_callbacks_called() {
+        assumeNotCuttlefish()
+
+        val container = scenario.withActivity { findViewById(R.id.container) }
+
+        val res = mutableSetOf<String>()
+        val progress = mutableListOf<Float>()
+        val latch = CountDownLatch(3)
+        ViewCompat.setWindowInsetsAnimationCallback(
+            container,
+            object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
+                override fun onPrepare(animation: WindowInsetsAnimationCompat) {
+                    res.add("prepare")
+                    latch.countDown()
+                }
+
+                override fun onProgress(
+                    insets: WindowInsetsCompat,
+                    runningAnimations: MutableList<WindowInsetsAnimationCompat>
+                ): WindowInsetsCompat {
+                    res.add("progress")
+                    progress.add(runningAnimations[0].fraction)
+                    return insets
+                }
+
+                override fun onStart(
+                    animation: WindowInsetsAnimationCompat,
+                    bounds: WindowInsetsAnimationCompat.Bounds
+                ): WindowInsetsAnimationCompat.Bounds {
+                    res.add("start")
+                    latch.countDown()
+                    return bounds
+                }
+
+                override fun onEnd(animation: WindowInsetsAnimationCompat) {
+                    res.add("end")
+                    latch.countDown()
+                }
+            }
+        )
+        triggerInsetAnimation(container)
+        latch.await(5, TimeUnit.SECONDS)
+        Truth.assertThat(res).containsExactly("prepare", "start", "progress", "end").inOrder()
+        Truth.assertThat(progress).containsAtLeast(0.0f, 1.0f)
+        Truth.assertThat(progress).isInOrder()
+    }
+
+    private fun triggerInsetAnimation(container: View) {
+        scenario.onActivity {
+            ViewCompat.getWindowInsetsController(container)!!.hide(systemBars())
+        }
+    }
+
+    private fun resetBars(container: View) {
+        scenario.onActivity {
+            ViewCompat.getWindowInsetsController(container)!!.show(systemBars())
+        }
+    }
+
+    @Test
+    public fun update_apply_listener() {
+        val container = scenario.withActivity { findViewById(R.id.container) }
+        var applyInsetsCalled1 = false
+        var applyInsetsCalled2 = false
+        var applyInsetsCalled3 = false
+        var insetsAnimationCallbackCalled1 = false
+        var insetsAnimationCallbackCalled2 = false
+        val latch = CountDownLatch(2)
+        val animationCallback1 = createCallback(
+            onPrepare = {
+                insetsAnimationCallbackCalled1 = true
+            }
+        )
+        val animationCallback2 = createCallback(
+            onPrepare = {
+                insetsAnimationCallbackCalled2 = true
+                latch.countDown()
+            }
+        )
+        val insetListener1: (v: View, insets: WindowInsetsCompat) -> WindowInsetsCompat =
+            { _, insetsCompat ->
+                applyInsetsCalled1 = true
+                insetsCompat
+            }
+
+        val insetListener2: (v: View, insets: WindowInsetsCompat) -> WindowInsetsCompat =
+            { _, insetsCompat ->
+                applyInsetsCalled2 = true
+                insetsCompat
+            }
+
+        val insetListener3: (v: View, insets: WindowInsetsCompat) -> WindowInsetsCompat =
+            { _, insetsCompat ->
+                applyInsetsCalled3 = true
+                latch.countDown()
+                insetsCompat
+            }
+
+        // Check that both ApplyWindowInsets and the Animation Callback are called
+        ViewCompat.setOnApplyWindowInsetsListener(container, insetListener1)
+        ViewCompat.setWindowInsetsAnimationCallback(container, animationCallback1)
+        ViewCompat.setOnApplyWindowInsetsListener(container, insetListener2)
+        ViewCompat.setWindowInsetsAnimationCallback(container, animationCallback2)
+        ViewCompat.setOnApplyWindowInsetsListener(container, insetListener3)
+        triggerInsetAnimation(container)
+        latch.await(5, TimeUnit.SECONDS)
+        assertFalse(
+            "The WindowInsetsAnimationCallback #1 should have not been called",
+            insetsAnimationCallbackCalled1
+        )
+        assertFalse(
+            "The onApplyWindowInsetsListener #1 should have not been called",
+            applyInsetsCalled1
+        )
+        assertTrue(
+            "The WindowInsetsAnimationCallback #2 has not been called",
+            insetsAnimationCallbackCalled2
+        )
+        assertFalse(
+            "onApplyWindowInsetsListener #2 should not have been called",
+            applyInsetsCalled2
+        )
+        assertTrue(
+            "onApplyWindowInsetsListener #3 has not been called",
+            applyInsetsCalled3
+        )
+    }
+
+    private fun createCallback(
+        onPrepare: ((WindowInsetsAnimationCompat) -> Unit)? = null
+
+    ): WindowInsetsAnimationCompat.Callback {
+        return object :
+            WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
+
+            override fun onPrepare(animation: WindowInsetsAnimationCompat) {
+                onPrepare?.invoke(animation)
+            }
+
+            override fun onProgress(
+                insets: WindowInsetsCompat,
+                runningAnimations: MutableList<WindowInsetsAnimationCompat>
+            ): WindowInsetsCompat = insets
+        }
+    }
+
+    private fun assumeNotCuttlefish() {
+        // TODO: remove this if b/159103848 is resolved
+        Assume.assumeFalse(
+            "Unable to test: Cuttlefish devices default to the virtual keyboard being disabled.",
+            Build.MODEL.contains("Cuttlefish", ignoreCase = true)
+        )
+    }
+}
\ No newline at end of file
diff --git a/core/core/src/androidTest/java/androidx/core/view/WindowInsetsCompatActivityTest.kt b/core/core/src/androidTest/java/androidx/core/view/WindowInsetsCompatActivityTest.kt
index f100253..6331c51 100644
--- a/core/core/src/androidTest/java/androidx/core/view/WindowInsetsCompatActivityTest.kt
+++ b/core/core/src/androidTest/java/androidx/core/view/WindowInsetsCompatActivityTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.core.view
 
+import android.content.Intent
 import android.content.pm.ActivityInfo
 import android.os.Build
 import android.view.View
@@ -26,6 +27,7 @@
 import androidx.core.test.R
 import androidx.core.view.WindowInsetsCompat.Type
 import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso
 import androidx.test.espresso.Espresso.onIdle
 import androidx.test.espresso.Espresso.onView
 import androidx.test.espresso.action.ViewActions.click
@@ -33,6 +35,7 @@
 import androidx.test.espresso.assertion.ViewAssertions.matches
 import androidx.test.espresso.matcher.ViewMatchers.assertThat
 import androidx.test.espresso.matcher.ViewMatchers.hasFocus
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
 import androidx.test.espresso.matcher.ViewMatchers.withId
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -181,6 +184,54 @@
         }
     }
 
+    @SdkSuppress(minSdkVersion = 23, maxSdkVersion = 29)
+    @Test
+    @Ignore("IME tests are inherently flaky, but still useful for local testing.")
+    public fun ime_insets_cleared_on_back() {
+        // Test do not currently work on Cuttlefish
+        assumeNotCuttlefish()
+        assumeSoftInputMode(SOFT_INPUT_ADJUST_RESIZE)
+
+        val expectedListenerPasses = 2
+        val latch = CountDownLatch(expectedListenerPasses)
+        val received = AtomicReference<WindowInsetsCompat>()
+        val container: View = scenario.withActivity { findViewById(R.id.container) }
+
+        // Tell the window that our view will fit system windows
+        scenario.onActivity { activity ->
+            WindowCompat.setDecorFitsSystemWindows(activity.window, false)
+        }
+
+        onView(withId(R.id.edittext))
+            .perform(click())
+            .check(matches(hasFocus()))
+
+        // Set a listener to catch WindowInsets
+        ViewCompat
+            .setOnApplyWindowInsetsListener(container.rootView) { _, insets: WindowInsetsCompat ->
+                received.set(insets)
+                latch.countDown()
+                WindowInsetsCompat.CONSUMED
+            }
+
+        scenario.onActivity { activity ->
+            activity.startActivity(Intent(activity, activity::class.java))
+        }
+
+        Espresso.pressBackUnconditionally()
+        onView(withId(R.id.edittext))
+            .check(matches(isDisplayed()))
+        assertThat(
+            "OnApplyWindowListener should have been called $expectedListenerPasses times but was " +
+                "called ${expectedListenerPasses - latch.count} times",
+            latch.await(2, TimeUnit.SECONDS), `is`(true)
+        )
+
+        // Check that the IME insets is equal to 0
+        val insets = received.get()
+        assertEquals(0, insets.getInsets(Type.ime()).bottom)
+    }
+
     @SdkSuppress(minSdkVersion = 23)
     @Test
     @Ignore("IME tests are inherently flaky, but still useful for local testing.")
diff --git a/core/core/src/androidTest/java/androidx/core/view/WindowInsetsCompatTest.kt b/core/core/src/androidTest/java/androidx/core/view/WindowInsetsCompatTest.kt
index 8c4eb55..e17940d 100644
--- a/core/core/src/androidTest/java/androidx/core/view/WindowInsetsCompatTest.kt
+++ b/core/core/src/androidTest/java/androidx/core/view/WindowInsetsCompatTest.kt
@@ -22,6 +22,7 @@
 import androidx.test.filters.SmallTest
 import org.hamcrest.Matchers.notNullValue
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertThat
 import org.junit.Assert.assertTrue
@@ -218,19 +219,37 @@
     }
 
     @Test
-    fun test_equals() {
+    public fun test_equals() {
         val result = WindowInsetsCompat.Builder()
             .setInsets(Type.systemBars(), Insets.of(1, 2, 3, 4))
             .setInsetsIgnoringVisibility(Type.systemBars(), Insets.of(11, 12, 13, 14))
             .build()
+        result.setRootViewData(Insets.of(0, 0, 0, 15))
         val result2 = WindowInsetsCompat.Builder()
             .setInsets(Type.systemBars(), Insets.of(1, 2, 3, 4))
             .setInsetsIgnoringVisibility(Type.systemBars(), Insets.of(11, 12, 13, 14))
             .build()
+        result2.setRootViewData(Insets.of(0, 0, 0, 15))
         assertEquals(result, result2)
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = 20)
+    public fun test_not_equals_root_visible_insets() {
+        val result = WindowInsetsCompat.Builder()
+            .setInsets(Type.systemBars(), Insets.of(1, 2, 3, 4))
+            .setInsetsIgnoringVisibility(Type.systemBars(), Insets.of(11, 12, 13, 14))
+            .build()
+        result.setRootViewData(Insets.of(0, 0, 0, 15))
+        val result2 = WindowInsetsCompat.Builder()
+            .setInsets(Type.systemBars(), Insets.of(1, 2, 3, 4))
+            .setInsetsIgnoringVisibility(Type.systemBars(), Insets.of(11, 12, 13, 14))
+            .build()
+        result2.setRootViewData(Insets.of(0, 0, 0, 16))
+        assertNotEquals(result, result2)
+    }
+
+    @Test
     fun test_hashCode() {
         val result = WindowInsetsCompat.Builder()
             .setInsets(Type.systemBars(), Insets.of(1, 2, 3, 4))
diff --git a/core/core/src/androidTest/java/androidx/core/widget/RichContentReceiverTestActivity.java b/core/core/src/androidTest/java/androidx/core/widget/ReceiveContentTestActivity.java
similarity index 85%
rename from core/core/src/androidTest/java/androidx/core/widget/RichContentReceiverTestActivity.java
rename to core/core/src/androidTest/java/androidx/core/widget/ReceiveContentTestActivity.java
index 83a6ba0..bc5a607 100644
--- a/core/core/src/androidTest/java/androidx/core/widget/RichContentReceiverTestActivity.java
+++ b/core/core/src/androidTest/java/androidx/core/widget/ReceiveContentTestActivity.java
@@ -20,9 +20,9 @@
 
 import androidx.core.test.R;
 
-public class RichContentReceiverTestActivity extends BaseTestActivity {
+public class ReceiveContentTestActivity extends BaseTestActivity {
     @Override
     protected int getContentViewLayoutResId() {
-        return R.layout.rich_content_receiver_activity;
+        return R.layout.receive_content_activity;
     }
 }
diff --git a/core/core/src/androidTest/java/androidx/core/widget/RichContentReceiverCompatTest.java b/core/core/src/androidTest/java/androidx/core/widget/TextViewOnReceiveContentListenerTest.java
similarity index 74%
rename from core/core/src/androidTest/java/androidx/core/widget/RichContentReceiverCompatTest.java
rename to core/core/src/androidTest/java/androidx/core/widget/TextViewOnReceiveContentListenerTest.java
index 6310271..8ffc978 100644
--- a/core/core/src/androidTest/java/androidx/core/widget/RichContentReceiverCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/widget/TextViewOnReceiveContentListenerTest.java
@@ -16,28 +16,26 @@
 
 package androidx.core.widget;
 
-import static androidx.core.widget.RichContentReceiverCompat.FLAG_CONVERT_TO_PLAIN_TEXT;
-import static androidx.core.widget.RichContentReceiverCompat.SOURCE_CLIPBOARD;
-import static androidx.core.widget.RichContentReceiverCompat.SOURCE_INPUT_METHOD;
+import static androidx.core.view.ContentInfoCompat.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static androidx.core.view.ContentInfoCompat.SOURCE_CLIPBOARD;
+import static androidx.core.view.ContentInfoCompat.SOURCE_DRAG_AND_DROP;
+import static androidx.core.view.ContentInfoCompat.SOURCE_INPUT_METHOD;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static java.util.Collections.singleton;
-
 import android.content.ClipData;
 import android.net.Uri;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.UnderlineSpan;
-import android.view.inputmethod.BaseInputConnection;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
 import android.widget.EditText;
-import android.widget.TextView;
 
 import androidx.core.test.R;
-import androidx.core.view.inputmethod.EditorInfoCompat;
+import androidx.core.view.ContentInfoCompat;
+import androidx.core.view.ContentInfoCompat.Flags;
+import androidx.core.view.ContentInfoCompat.Source;
+import androidx.core.view.OnReceiveContentListener;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
@@ -51,26 +49,20 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
-public class RichContentReceiverCompatTest {
+public class TextViewOnReceiveContentListenerTest {
 
     @Rule
-    public final ActivityTestRule<RichContentReceiverTestActivity> mActivityTestRule =
-            new ActivityTestRule<>(RichContentReceiverTestActivity.class);
+    public final ActivityTestRule<ReceiveContentTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(ReceiveContentTestActivity.class);
 
     private EditText mEditText;
-    private RichContentReceiverCompat<TextView> mReceiver;
+    private TextViewOnReceiveContentListener mReceiver;
 
     @Before
     public void before() {
-        RichContentReceiverTestActivity activity = mActivityTestRule.getActivity();
-        mEditText = activity.findViewById(R.id.edit_text_for_rich_content_receiver);
-        mReceiver = new TextViewRichContentReceiverCompat() {};
-    }
-
-    @UiThreadTest
-    @Test
-    public void testGetSupportedMimeTypes() throws Exception {
-        assertThat(mReceiver.getSupportedMimeTypes()).isEqualTo(singleton("text/*"));
+        ReceiveContentTestActivity activity = mActivityTestRule.getActivity();
+        mEditText = activity.findViewById(R.id.edit_text);
+        mReceiver = new TextViewOnReceiveContentListener();
     }
 
     @UiThreadTest
@@ -249,42 +241,36 @@
 
     @UiThreadTest
     @Test
-    public void testPopulateEditorInfoContentMimeTypes() throws Exception {
-        InputConnection ic = new BaseInputConnection(mEditText, true);
-        EditorInfo outAttrs = new EditorInfo();
+    public void testOnReceive_dragAndDrop_text() throws Exception {
+        setTextAndCursor("xz", 1);
 
-        // The field contentMimeTypes in outAttrs should be set to the MIME types of the receiver.
-        mReceiver.populateEditorInfoContentMimeTypes(ic, outAttrs);
-        assertThat(EditorInfoCompat.getContentMimeTypes(outAttrs)).isEqualTo(
-                new String[] {"text/*"});
+        ClipData clip = ClipData.newPlainText("test", "y");
+        boolean result = onReceive(mReceiver, clip, SOURCE_DRAG_AND_DROP, 0);
 
-        // If the field contentMimeTypes in outAttrs already has a value assigned, it should be
-        // overwritten with the MIME types of the receiver.
-        EditorInfoCompat.setContentMimeTypes(outAttrs, new String[] {"video/mp4"});
-        mReceiver.populateEditorInfoContentMimeTypes(ic, outAttrs);
-        assertThat(EditorInfoCompat.getContentMimeTypes(outAttrs)).isEqualTo(
-                new String[] {"text/*"});
+        assertThat(result).isTrue();
+        assertTextAndCursorPosition("xyz", 2);
     }
 
     @UiThreadTest
     @Test
-    public void testPopulateEditorInfoContentMimeTypes_nulls() throws Exception {
-        InputConnection ic = new BaseInputConnection(mEditText, true);
-        EditorInfo outAttrs = new EditorInfo();
+    public void testOnReceive_dragAndDrop_multipleItemsInClipData() throws Exception {
+        setTextAndCursor("xz", 1);
 
-        // If the ic arg is null, outAttrs should not be populated.
-        mReceiver.populateEditorInfoContentMimeTypes(null, outAttrs);
-        assertThat(EditorInfoCompat.getContentMimeTypes(outAttrs)).isEqualTo(new String[0]);
+        ClipData clip = ClipData.newPlainText("test", "ONE");
+        clip.addItem(new ClipData.Item("TWO"));
+        clip.addItem(new ClipData.Item("THREE"));
+        boolean result = onReceive(mReceiver, clip, SOURCE_DRAG_AND_DROP, 0);
 
-        // If the outAttrs arg is null, it should not be populated.
-        mReceiver.populateEditorInfoContentMimeTypes(ic, null);
-        assertThat(EditorInfoCompat.getContentMimeTypes(outAttrs)).isEqualTo(new String[0]);
+        assertThat(result).isTrue();
+        assertTextAndCursorPosition("xONETWOTHREEz", 12);
     }
 
-    private boolean onReceive(final RichContentReceiverCompat<TextView> receiver,
-            final ClipData clip, @RichContentReceiverCompat.Source final int source,
-            final int flags) {
-        return receiver.onReceive(mEditText, clip, source, flags);
+    private boolean onReceive(final OnReceiveContentListener receiver, ClipData clip,
+            @Source int source, @Flags int flags) {
+        ContentInfoCompat payload = new ContentInfoCompat.Builder(clip, source)
+                .setFlags(flags)
+                .build();
+        return receiver.onReceiveContent(mEditText, payload) == null;
     }
 
     private void setTextAndCursor(final String text, final int cursorPosition) {
diff --git a/core/core/src/androidTest/res/layout/rich_content_receiver_activity.xml b/core/core/src/androidTest/res/layout/receive_content_activity.xml
similarity index 93%
rename from core/core/src/androidTest/res/layout/rich_content_receiver_activity.xml
rename to core/core/src/androidTest/res/layout/receive_content_activity.xml
index 81b6479..a4ea03c 100644
--- a/core/core/src/androidTest/res/layout/rich_content_receiver_activity.xml
+++ b/core/core/src/androidTest/res/layout/receive_content_activity.xml
@@ -21,7 +21,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <EditText
-        android:id="@+id/edit_text_for_rich_content_receiver"
+        android:id="@+id/edit_text"
         android:layout_width="200dip"
         android:layout_height="60dip" />
 </FrameLayout>
diff --git a/core/core/src/androidTest/resources/mockito-extensions/org.mockito.plugins.MockMaker b/core/core/src/androidTest/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..7f9e2f4
--- /dev/null
+++ b/core/core/src/androidTest/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+androidx.core.util.mockito.CustomMockMaker
\ No newline at end of file
diff --git a/core/core/src/androidTest/resources/mockito-extensions/org.mockito.plugins.StackTraceCleaner b/core/core/src/androidTest/resources/mockito-extensions/org.mockito.plugins.StackTraceCleaner
new file mode 100644
index 0000000..d380fb4
--- /dev/null
+++ b/core/core/src/androidTest/resources/mockito-extensions/org.mockito.plugins.StackTraceCleaner
@@ -0,0 +1 @@
+androidx.core.util.mockito.CustomStackTraceCleaner
\ No newline at end of file
diff --git a/core/core/src/main/java/androidx/core/app/ShareCompat.java b/core/core/src/main/java/androidx/core/app/ShareCompat.java
index 79bce4d8..8d6b8ac 100644
--- a/core/core/src/main/java/androidx/core/app/ShareCompat.java
+++ b/core/core/src/main/java/androidx/core/app/ShareCompat.java
@@ -230,7 +230,10 @@
      *
      * @param item MenuItem to configure for sharing
      * @param shareIntent IntentBuilder with data about the content to share
+     *
+     * @deprecated Use the system sharesheet. See https://developer.android.com/training/sharing/send
      */
+    @Deprecated
     public static void configureMenuItem(@NonNull MenuItem item,
             @NonNull IntentBuilder shareIntent) {
         ActionProvider itemProvider = item.getActionProvider();
@@ -259,7 +262,10 @@
      * @param menuItemId ID of the share item within menu
      * @param shareIntent IntentBuilder with data about the content to share
      * @see #configureMenuItem(MenuItem, IntentBuilder)
+     *
+     * @deprecated Use the system sharesheet. See https://developer.android.com/training/sharing/send
      */
+    @Deprecated
     public static void configureMenuItem(@NonNull Menu menu, @IdRes int menuItemId,
             @NonNull IntentBuilder shareIntent) {
         MenuItem item = menu.findItem(menuItemId);
@@ -423,12 +429,6 @@
 
         /**
          * Start a chooser activity for the current share intent.
-         *
-         * <p>Note that under most circumstances you should use
-         * {@link ShareCompat#configureMenuItem(MenuItem, IntentBuilder)
-         *  ShareCompat.configureMenuItem()} to add a Share item to the menu while
-         * presenting a detail view of the content to be shared instead
-         * of invoking this directly.</p>
          */
         public void startChooser() {
             mContext.startActivity(createChooserIntent());
diff --git a/core/core/src/main/java/androidx/core/content/pm/PackageInfoCompat.java b/core/core/src/main/java/androidx/core/content/pm/PackageInfoCompat.java
index b7e7452..20ef5e5 100644
--- a/core/core/src/main/java/androidx/core/content/pm/PackageInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/content/pm/PackageInfoCompat.java
@@ -16,10 +16,25 @@
 
 package androidx.core.content.pm;
 
+import android.annotation.SuppressLint;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.os.Build;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.Size;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /** Helper for accessing features in {@link PackageInfo}. */
 public final class PackageInfoCompat {
@@ -38,6 +53,236 @@
         return info.versionCode;
     }
 
+    /**
+     * Retrieve the {@link Signature} array for the given package. This returns some of
+     * certificates, depending on whether the package in question is multi-signed or has signing
+     * history.
+     *
+     * <note>
+     * <p>
+     * Security/identity verification should <b>not</b> be done with this method. This is only
+     * intended to return some array of certificates that correspond to a package.
+     * </p>
+     * <p>
+     * If verification if required, either use
+     * {@link #hasSignatures(PackageManager, String, Map, boolean)} or manually verify the set of
+     * certificates using {@link PackageManager#GET_SIGNING_CERTIFICATES} or
+     * {@link PackageManager#GET_SIGNATURES}.
+     * </p>
+     * </note>
+     *
+     * @param packageManager The {@link PackageManager} instance to query against.
+     * @param packageName    The package to query the {@param packageManager} for. Query by app
+     *                       UID is only supported by manually choosing a package name
+     *                       returned in {@link PackageManager#getPackagesForUid(int)}.
+     * @return an array of certificates the app is signed with
+     * @throws PackageManager.NameNotFoundException if the package cannot be found through the
+     *                                              provided {@param packageManager}
+     */
+    @NonNull
+    public static List<Signature> getSignatures(@NonNull PackageManager packageManager,
+            @NonNull String packageName) throws PackageManager.NameNotFoundException {
+        Signature[] array;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            PackageInfo pkgInfo = packageManager.getPackageInfo(packageName,
+                    PackageManager.GET_SIGNING_CERTIFICATES);
+            SigningInfo signingInfo = pkgInfo.signingInfo;
+            if (Api28Impl.hasMultipleSigners(signingInfo)) {
+                array = Api28Impl.getApkContentsSigners(signingInfo);
+            } else {
+                array = Api28Impl.getSigningCertificateHistory(signingInfo);
+            }
+        } else {
+            // Lint warning's vulnerability is explicitly not handled for this method.
+            @SuppressLint("PackageManagerGetSignatures")
+            PackageInfo pkgInfo = packageManager.getPackageInfo(packageName,
+                    PackageManager.GET_SIGNATURES);
+            array = pkgInfo.signatures;
+        }
+
+        // Framework code implies nullable/empty, although it may be impossible in practice.
+        if (array == null) {
+            return Collections.emptyList();
+        } else {
+            return Arrays.asList(array);
+        }
+    }
+
+    /**
+     * Check if a package on device contains set of a certificates. Supported types are raw X509 or
+     * SHA-256 bytes.
+     *
+     * @param packageManager      The {@link PackageManager} instance to query against.
+     * @param packageName         The package to query the {@param packageManager} for. Query by
+     *                            app UID is only supported by manually choosing a package name
+     *                            returned in {@link PackageManager#getPackagesForUid(int)}.
+     * @param certificatesAndType The bytes of the certificate mapped to the type, either
+     *                            {@link PackageManager#CERT_INPUT_RAW_X509} or
+     *                            {@link PackageManager#CERT_INPUT_SHA256}. A single or multiple
+     *                            certificates may be included.
+     * @param matchExact          Whether or not to check for presence of all signatures exactly.
+     *                            If false, then the check will succeed if the query contains a
+     *                            subset of the package certificates. Matching exactly is strongly
+     *                            recommended when running on devices below
+     *                            {@link Build.VERSION_CODES#LOLLIPOP} due to the fake ID
+     *                            vulnerability that allows a package to be modified to include
+     *                            an unverified signature.
+     * @return true if the package is considered signed by the given certificate set, or false
+     * otherwise
+     * @throws PackageManager.NameNotFoundException if the package cannot be found through the
+     *                                              provided {@param packageManager}
+     */
+    public static boolean hasSignatures(@NonNull PackageManager packageManager,
+            @NonNull String packageName,
+            @Size(min = 1) @NonNull Map<byte[], Integer> certificatesAndType, boolean matchExact)
+            throws PackageManager.NameNotFoundException {
+        // If empty is passed in, return false to prevent accidentally succeeding
+        if (certificatesAndType.isEmpty()) {
+            return false;
+        }
+
+        Set<byte[]> expectedCertBytes = certificatesAndType.keySet();
+
+        // The type has to be checked before any API level branching. If a new type is ever added,
+        // this code should fail and will have to be updated manually. To do otherwise would
+        // introduce a behavioral difference between the API level that added the new type and
+        // devices on prior API levels, which may not be caught by a developer calling this
+        // method if they do not test on an old API level.
+        for (byte[] bytes : expectedCertBytes) {
+            if (bytes == null) {
+                throw new IllegalArgumentException("Cert byte array cannot be null when verifying "
+                        + packageName);
+            }
+            Integer type = certificatesAndType.get(bytes);
+            if (type == null) {
+                throw new IllegalArgumentException("Type must be specified for cert when verifying "
+                        + packageName);
+            }
+
+            switch (type) {
+                case PackageManager.CERT_INPUT_RAW_X509:
+                case PackageManager.CERT_INPUT_SHA256:
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported certificate type " + type
+                            + " when verifying " + packageName);
+            }
+        }
+
+        // getSignatures is called first to throw NameNotFoundException if necessary
+        final List<Signature> signers = getSignatures(packageManager, packageName);
+
+        // The vulnerability requiring matchExact is not necessary on P, but the signatures
+        // must still be checked manually in order to match the behavior described by the
+        // method. Otherwise matchExact == true will allow additional certificates if run
+        // on a device >= P.
+        if (!matchExact && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            // If not matching exact, delegate to the API 28 PackageManager API for checking
+            // individual certificates. This is less performant, but goes through a formally
+            // supported API.
+            for (byte[] bytes : expectedCertBytes) {
+                Integer type = certificatesAndType.get(bytes);
+                //noinspection ConstantConditions type cannot be null
+                if (!Api28Impl.hasSigningCertificate(packageManager, packageName, bytes, type)) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        // Fail if the query is larger than the actual set, or the size doesn't match and it should.
+        if (signers.size() == 0
+                || certificatesAndType.size() > signers.size()
+                || (matchExact && certificatesAndType.size() != signers.size())) {
+            return false;
+        }
+
+        @SuppressLint("InlinedApi")
+        boolean hasSha256 = certificatesAndType.containsValue(PackageManager.CERT_INPUT_SHA256);
+        byte[][] sha256Digests = null;
+        if (hasSha256) {
+            // Since the search does several array contains checks, cache the SHA256 digests here.
+            sha256Digests = new byte[signers.size()][];
+            for (int index = 0; index < signers.size(); index++) {
+                sha256Digests[index] = computeSHA256Digest(signers.get(index).toByteArray());
+            }
+        }
+
+        for (byte[] bytes : expectedCertBytes) {
+            Integer type = certificatesAndType.get(bytes);
+            //noinspection ConstantConditions type cannot be null
+            switch (type) {
+                case PackageManager.CERT_INPUT_RAW_X509:
+                    // RAW_X509 is the type that Signatures are and always have been stored as,
+                    // so defer to the Signature equals method for the platform.
+                    Signature expectedSignature = new Signature(bytes);
+                    if (!signers.contains(expectedSignature)) {
+                        return false;
+                    }
+                    break;
+                case PackageManager.CERT_INPUT_SHA256:
+                    // sha256Digests cannot be null due to pre-checked containsValue for its type
+                    //noinspection ConstantConditions
+                    if (!byteArrayContains(sha256Digests, bytes)) {
+                        return false;
+                    }
+                    break;
+                default:
+                    // Impossible to reach this point due to check at beginning of method.
+                    throw new IllegalArgumentException("Unsupported certificate type " + type);
+            }
+
+            // If this point is reached, all searches have succeeded
+            return true;
+        }
+
+        return false;
+    }
+
+    private static boolean byteArrayContains(@NonNull byte[][] array, @NonNull byte[] expected) {
+        for (byte[] item : array) {
+            if (Arrays.equals(expected, item)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static byte[] computeSHA256Digest(byte[] bytes) {
+        try {
+            return MessageDigest.getInstance("SHA256").digest(bytes);
+        } catch (NoSuchAlgorithmException e) {
+            // Can't happen, SHA256 required since API level 1
+            throw new RuntimeException("Device doesn't support SHA256 cert checking", e);
+        }
+    }
+
     private PackageInfoCompat() {
     }
+
+    @RequiresApi(Build.VERSION_CODES.P)
+    private static class Api28Impl {
+        private Api28Impl() {
+        }
+
+        static boolean hasSigningCertificate(@NonNull PackageManager packageManager,
+                @NonNull String packageName, @NonNull byte[] bytes, int type) {
+            return packageManager.hasSigningCertificate(packageName, bytes, type);
+        }
+
+        static boolean hasMultipleSigners(@NonNull SigningInfo signingInfo) {
+            return signingInfo.hasMultipleSigners();
+        }
+
+        @Nullable
+        static Signature[] getApkContentsSigners(@NonNull SigningInfo signingInfo) {
+            return signingInfo.getApkContentsSigners();
+        }
+
+        @Nullable
+        static Signature[] getSigningCertificateHistory(@NonNull SigningInfo signingInfo) {
+            return signingInfo.getSigningCertificateHistory();
+        }
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java b/core/core/src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java
index 3ca617e..1e2a225 100644
--- a/core/core/src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java
@@ -586,7 +586,11 @@
     }
 
     /**
-     * Delete dynamic shortcuts by ID.
+     * Delete dynamic shortcuts by ID. Note that if a shortcut is set as long-lived, it may still
+     * be available in the system as a cached shortcut even after being removed from the list of
+     * dynamic shortcuts.
+     *
+     * @see #removeLongLivedShortcuts
      */
     public static void removeDynamicShortcuts(@NonNull Context context,
             @NonNull List<String> shortcutIds) {
@@ -598,7 +602,11 @@
     }
 
     /**
-     * Delete all dynamic shortcuts from the caller app.
+     * Delete all dynamic shortcuts from the caller app. Note that if a shortcut is set as
+     * long-lived, it may still be available in the system as a cached shortcut even after being
+     * removed from the list of dynamic shortcuts.
+     *
+     * @see #removeLongLivedShortcuts
      */
     public static void removeAllDynamicShortcuts(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 25) {
diff --git a/core/core/src/main/java/androidx/core/os/BuildCompat.java b/core/core/src/main/java/androidx/core/os/BuildCompat.java
index e14d683..ba91322 100644
--- a/core/core/src/main/java/androidx/core/os/BuildCompat.java
+++ b/core/core/src/main/java/androidx/core/os/BuildCompat.java
@@ -114,18 +114,17 @@
     }
 
     /**
-     * Checks if the device is running on a pre-release version of Android R or a release
-     * version of Android R or newer.
+     * Checks if the device is running on release version of Android R or newer.
      * <p>
-     * <strong>Note:</strong> When Android R is finalized for release, this method will be
-     * deprecated and all calls should be replaced with
-     * {@code Build.VERSION.SDK_INT >= Build.VERSION_CODES.R}.
-     *
      * @return {@code true} if R APIs are available for use, {@code false} otherwise
+     * @deprecated Android R is a finalized release and this method is no longer necessary. It
+     *             will be removed in a future release of the Support Library. Instead, use
+     *             {@code Build.VERSION.SDK_INT >= Build.VERSION_CODES.R}.
      */
     @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R)
+    @Deprecated
     public static boolean isAtLeastR() {
-        return VERSION.SDK_INT >= 30 || VERSION.CODENAME.equals("R");
+        return VERSION.SDK_INT >= 30;
     }
 
     /**
diff --git a/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java b/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java
new file mode 100644
index 0000000..68b7112
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java
@@ -0,0 +1,368 @@
+/*
+ * 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.core.view;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Pair;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.core.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Holds all the relevant data for a request to {@link OnReceiveContentListener}.
+ */
+// This class has the "Compat" suffix because it will integrate with (ie, wrap) the SDK API once
+// that is available.
+public final class ContentInfoCompat {
+
+    /**
+     * Specifies the UI through which content is being inserted. Future versions of Android may
+     * support additional values.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+    @IntDef(value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD, SOURCE_DRAG_AND_DROP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Source {
+    }
+
+    /**
+     * Specifies that the operation was triggered by the app that contains the target view.
+     */
+    public static final int SOURCE_APP = 0;
+
+    /**
+     * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
+     * "Paste as plain text" action in the insertion/selection menu).
+     */
+    public static final int SOURCE_CLIPBOARD = 1;
+
+    /**
+     * Specifies that the operation was triggered from the soft keyboard (also known as input
+     * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard
+     * for more info.
+     */
+    public static final int SOURCE_INPUT_METHOD = 2;
+
+    /**
+     * Specifies that the operation was triggered by the drag/drop framework. See
+     * https://developer.android.com/guide/topics/ui/drag-drop for more info.
+     */
+    public static final int SOURCE_DRAG_AND_DROP = 3;
+
+    /**
+     * Returns the symbolic name of the given source.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+    @NonNull
+    static String sourceToString(@Source int source) {
+        switch (source) {
+            case SOURCE_APP: return "SOURCE_APP";
+            case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
+            case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
+            case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
+        }
+        return String.valueOf(source);
+    }
+
+    /**
+     * Flags to configure the insertion behavior.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+    @IntDef(flag = true, value = {FLAG_CONVERT_TO_PLAIN_TEXT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Flags {
+    }
+
+    /**
+     * Flag requesting that the content should be converted to plain text prior to inserting.
+     */
+    public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
+
+    /**
+     * Returns the symbolic names of the set flags or {@code "0"} if no flags are set.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+    @NonNull
+    static String flagsToString(@Flags int flags) {
+        if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
+            return "FLAG_CONVERT_TO_PLAIN_TEXT";
+        }
+        return String.valueOf(flags);
+    }
+
+    @NonNull
+    final ClipData mClip;
+    @Source
+    final int mSource;
+    @Flags
+    final int mFlags;
+    @Nullable
+    final Uri mLinkUri;
+    @Nullable
+    final Bundle mExtras;
+
+    ContentInfoCompat(Builder b) {
+        this.mClip = Preconditions.checkNotNull(b.mClip);
+        this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_DRAG_AND_DROP,
+                "source");
+        this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
+        this.mLinkUri = b.mLinkUri;
+        this.mExtras = b.mExtras;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "ContentInfoCompat{"
+                + "clip=" + mClip
+                + ", source=" + sourceToString(mSource)
+                + ", flags=" + flagsToString(mFlags)
+                + ", linkUri=" + mLinkUri
+                + ", extras=" + mExtras
+                + "}";
+    }
+
+    /**
+     * The data to be inserted.
+     */
+    @NonNull
+    public ClipData getClip() {
+        return mClip;
+    }
+
+    /**
+     * The source of the operation. See {@code SOURCE_} constants. Future versions of Android
+     * may pass additional values.
+     */
+    @Source
+    public int getSource() {
+        return mSource;
+    }
+
+    /**
+     * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
+     */
+    @Flags
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Optional http/https URI for the content that may be provided by the IME. This is only
+     * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
+     * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
+     * IME.
+     */
+    @Nullable
+    public Uri getLinkUri() {
+        return mLinkUri;
+    }
+
+    /**
+     * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
+     * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
+     * the IME.
+     */
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Partitions this content based on the given predicate.
+     *
+     * <p>This function classifies the content and organizes it into a pair, grouping the items
+     * that matched vs didn't match the predicate.
+     *
+     * <p>Except for the {@link ClipData} items, the returned objects will contain all the same
+     * metadata as this {@link ContentInfoCompat}.
+     *
+     * @param itemPredicate The predicate to test each {@link ClipData.Item} to determine which
+     *                      partition to place it into.
+     * @return A pair containing the partitioned content. The pair's first object will have the
+     * content that matched the predicate, or null if none of the items matched. The pair's
+     * second object will have the content that didn't match the predicate, or null if all of
+     * the items matched.
+     */
+    @NonNull
+    public Pair<ContentInfoCompat, ContentInfoCompat> partition(
+            @NonNull androidx.core.util.Predicate<ClipData.Item> itemPredicate) {
+        if (mClip.getItemCount() == 1) {
+            boolean matched = itemPredicate.test(mClip.getItemAt(0));
+            return Pair.create(matched ? this : null, matched ? null : this);
+        }
+        ArrayList<ClipData.Item> acceptedItems = new ArrayList<>();
+        ArrayList<ClipData.Item> remainingItems = new ArrayList<>();
+        for (int i = 0; i < mClip.getItemCount(); i++) {
+            ClipData.Item item = mClip.getItemAt(i);
+            if (itemPredicate.test(item)) {
+                acceptedItems.add(item);
+            } else {
+                remainingItems.add(item);
+            }
+        }
+        if (acceptedItems.isEmpty()) {
+            return Pair.create(null, this);
+        }
+        if (remainingItems.isEmpty()) {
+            return Pair.create(this, null);
+        }
+        ContentInfoCompat accepted = new Builder(this)
+                .setClip(buildClipData(mClip.getDescription(), acceptedItems))
+                .build();
+        ContentInfoCompat remaining = new Builder(this)
+                .setClip(buildClipData(mClip.getDescription(), remainingItems))
+                .build();
+        return Pair.create(accepted, remaining);
+    }
+
+    private static ClipData buildClipData(ClipDescription description,
+            List<ClipData.Item> items) {
+        ClipData clip = new ClipData(new ClipDescription(description), items.get(0));
+        for (int i = 1; i < items.size(); i++) {
+            clip.addItem(items.get(i));
+        }
+        return clip;
+    }
+
+    /**
+     * Builder for {@link ContentInfoCompat}.
+     */
+    public static final class Builder {
+        @NonNull
+        ClipData mClip;
+        @Source
+        int mSource;
+        @Flags
+        int mFlags;
+        @Nullable
+        Uri mLinkUri;
+        @Nullable
+        Bundle mExtras;
+
+        /**
+         * Creates a new builder initialized with the data from the given builder.
+         */
+        public Builder(@NonNull ContentInfoCompat other) {
+            mClip = other.mClip;
+            mSource = other.mSource;
+            mFlags = other.mFlags;
+            mLinkUri = other.mLinkUri;
+            mExtras = other.mExtras;
+        }
+
+        /**
+         * Creates a new builder.
+         *
+         * @param clip   The data to insert.
+         * @param source The source of the operation. See {@code SOURCE_} constants.
+         */
+        public Builder(@NonNull ClipData clip, @Source int source) {
+            mClip = clip;
+            mSource = source;
+        }
+
+        /**
+         * Sets the data to be inserted.
+         *
+         * @param clip The data to insert.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setClip(@NonNull ClipData clip) {
+            mClip = clip;
+            return this;
+        }
+
+        /**
+         * Sets the source of the operation.
+         *
+         * @param source The source of the operation. See {@code SOURCE_} constants.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setSource(@Source int source) {
+            mSource = source;
+            return this;
+        }
+
+        /**
+         * Sets flags that control content insertion behavior.
+         *
+         * @param flags Optional flags to configure the insertion behavior. Use 0 for default
+         *              behavior. See {@code FLAG_} constants.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setFlags(@Flags int flags) {
+            mFlags = flags;
+            return this;
+        }
+
+        /**
+         * Sets the http/https URI for the content. See
+         * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info.
+         *
+         * @param linkUri Optional http/https URI for the content.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setLinkUri(@Nullable Uri linkUri) {
+            mLinkUri = linkUri;
+            return this;
+        }
+
+        /**
+         * Sets additional metadata.
+         *
+         * @param extras Optional bundle with additional metadata.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * @return A new {@link ContentInfoCompat} instance with the data from this builder.
+         */
+        @NonNull
+        public ContentInfoCompat build() {
+            return new ContentInfoCompat(this);
+        }
+    }
+}
diff --git a/core/core/src/main/java/androidx/core/view/OnReceiveContentListener.java b/core/core/src/main/java/androidx/core/view/OnReceiveContentListener.java
new file mode 100644
index 0000000..2c42b9d
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/view/OnReceiveContentListener.java
@@ -0,0 +1,99 @@
+/*
+ * 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.core.view;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Listener for apps to implement handling for insertion of content. Content may be both text and
+ * non-text (plain/styled text, HTML, images, videos, audio files, etc).
+ *
+ * <p>This listener can be attached to different types of UI components using
+ * {@link ViewCompat#setOnReceiveContentListener}.
+ *
+ * <p>Here is a sample implementation that handles content URIs and delegates the processing for
+ * text and everything else to the platform:<br>
+ * <pre class="prettyprint">
+ * // (1) Define the listener
+ * public class MyReceiver implements OnReceiveContentListener {
+ *     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};
+ *
+ *     &#64;Override
+ *     public ContentInfoCompat onReceiveContent(View view, ContentInfoCompat contentInfo) {
+ *         Pair&lt;ContentInfoCompat, ContentInfoCompat&gt; split = contentInfo.partition(
+ *                 item -&gt; item.getUri() != null);
+ *         ContentInfo uriContent = split.first;
+ *         ContentInfo remaining = split.second;
+ *         if (uriContent != null) {
+ *             ClipData clip = uriContent.getClip();
+ *             for (int i = 0; i < clip.getItemCount(); i++) {
+ *                 Uri uri = clip.getItemAt(i).getUri();
+ *                 // ... app-specific logic to handle the URI ...
+ *             }
+ *         }
+ *         // Return anything that we didn't handle ourselves. This preserves the default platform
+ *         // behavior for text and anything else for which we are not implementing custom handling.
+ *         return remaining;
+ *     }
+ * }
+ *
+ * // (2) Register the listener
+ * public class MyActivity extends Activity {
+ *     &#64;Override
+ *     public void onCreate(Bundle savedInstanceState) {
+ *         // ...
+ *
+ *         AppCompatEditText myInput = findViewById(R.id.my_input);
+ *         ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, new MyReceiver());
+ *     }
+ * </pre>
+ */
+public interface OnReceiveContentListener {
+    /**
+     * Receive the given content.
+     *
+     * <p>Implementations should handle any content items of interest and return all unhandled
+     * items to preserve the default platform behavior for content that does not have app-specific
+     * handling. For example, an implementation may provide handling for content URIs (to provide
+     * support for inserting images, etc) and delegate the processing of text to the platform to
+     * preserve the common behavior for inserting text. See the class javadoc for a sample
+     * implementation and see {@link ContentInfoCompat#partition} for a convenient way to split the
+     * passed-in content.
+     *
+     * <p>If implementing handling for text: if the view has a selection, the selection should
+     * be overwritten by the passed-in content; if there's no selection, the passed-in content
+     * should be inserted at the current cursor position.
+     *
+     * <p>If implementing handling for non-text content (e.g. images): the content may be
+     * inserted inline, or it may be added as an attachment (could potentially be shown in a
+     * completely separate view).
+     *
+     * @param view The view where the content insertion was requested.
+     * @param payload The content to insert and related metadata.
+     *
+     * @return The portion of the passed-in content whose processing should be delegated to
+     * the platform. Return null if all content was handled in some way. Actual insertion of
+     * the content may be processed asynchronously in the background and may or may not
+     * succeed even if this method returns null. For example, an app may end up not inserting
+     * an item if it exceeds the app's size limit for that type of content.
+     */
+    @Nullable
+    ContentInfoCompat onReceiveContent(@NonNull View view, @NonNull ContentInfoCompat payload);
+}
diff --git a/core/core/src/main/java/androidx/core/view/OnReceiveContentViewBehavior.java b/core/core/src/main/java/androidx/core/view/OnReceiveContentViewBehavior.java
new file mode 100644
index 0000000..9b67b78
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/view/OnReceiveContentViewBehavior.java
@@ -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.core.view;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Interface for widgets to implement default behavior for receiving content. Content may be both
+ * text and non-text (plain/styled text, HTML, images, videos, audio files, etc).
+ *
+ * <p>Widgets should implement this interface to define the default behavior for receiving content.
+ * Apps wishing to provide custom behavior for receiving content should set a listener via
+ * {@link ViewCompat#setOnReceiveContentListener}. See {@link ViewCompat#performReceiveContent} for
+ * more info.
+ */
+public interface OnReceiveContentViewBehavior {
+    /**
+     * Implements a view's default behavior for receiving content.
+     *
+     * @param payload The content to insert and related metadata.
+     *
+     * @return The portion of the passed-in content that was not handled (may be all, some, or none
+     * of the passed-in content).
+     */
+    @Nullable
+    ContentInfoCompat onReceiveContent(@NonNull ContentInfoCompat payload);
+}
diff --git a/core/core/src/main/java/androidx/core/view/ViewCompat.java b/core/core/src/main/java/androidx/core/view/ViewCompat.java
index c5c8c38..12e9f72 100644
--- a/core/core/src/main/java/androidx/core/view/ViewCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ViewCompat.java
@@ -22,6 +22,7 @@
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
@@ -53,6 +54,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.inputmethod.InputConnection;
 
 import androidx.annotation.FloatRange;
 import androidx.annotation.IdRes;
@@ -65,6 +67,7 @@
 import androidx.annotation.UiThread;
 import androidx.collection.SimpleArrayMap;
 import androidx.core.R;
+import androidx.core.util.Preconditions;
 import androidx.core.view.AccessibilityDelegateCompat.AccessibilityDelegateAdapter;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
@@ -78,6 +81,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -2500,23 +2504,10 @@
      * Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying
      * window insets to this view. This will only take effect on devices with API 21 or above.
      */
-    public static void setOnApplyWindowInsetsListener(@NonNull View v,
+    public static void setOnApplyWindowInsetsListener(@NonNull final View v,
             final @Nullable OnApplyWindowInsetsListener listener) {
         if (Build.VERSION.SDK_INT >= 21) {
-            if (listener == null) {
-                v.setOnApplyWindowInsetsListener(null);
-                return;
-            }
-
-            v.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
-                @Override
-                public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
-                    WindowInsetsCompat compatInsets = WindowInsetsCompat
-                            .toWindowInsetsCompat(insets, view);
-                    compatInsets = listener.onApplyWindowInsets(view, compatInsets);
-                    return compatInsets.toWindowInsets();
-                }
-            });
+            Api21Impl.setOnApplyWindowInsetsListener(v, listener);
         }
     }
 
@@ -2668,6 +2659,155 @@
     }
 
     /**
+     * Sets a {@link WindowInsetsAnimationCompat.Callback} to be notified about animations of
+     * windows that cause insets.
+     * <p>
+     * The callback's {@link WindowInsetsAnimationCompat.Callback#getDispatchMode()
+     * dispatch mode} will affect whether animation callbacks are dispatched to the children of
+     * this view.
+     * <p>
+     * Prior to API 30, if an {@link OnApplyWindowInsetsListener} is used on the same
+     * view, be sure to always use the {@link ViewCompat} version of
+     * {@link #setOnApplyWindowInsetsListener(View, OnApplyWindowInsetsListener)}, otherwise the
+     * listener will be overridden by this method.
+     * <p>
+     * The insets dispatch needs to reach this view for the listener to be called. If any view
+     * consumed the insets earlier in the dispatch, this won't be called.
+     * <p>
+     * Prior to API 21, this method has no effect.
+     *
+     * @param callback The callback to set, or <code>null</code> to remove the currently installed
+     *                 callback
+     */
+    public static void setWindowInsetsAnimationCallback(@NonNull View view,
+            @Nullable final WindowInsetsAnimationCompat.Callback callback) {
+        WindowInsetsAnimationCompat.setCallback(view, callback);
+    }
+
+    /**
+     * Sets the listener to be used to handle insertion of content into the given view.
+     *
+     * <p>Depending on the type of view, this listener may be invoked for different scenarios. For
+     * example, for an AppCompatEditText, this listener will be invoked for the following scenarios:
+     * <ol>
+     *     <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the
+     *     insertion/selection menu)
+     *     <li>Content insertion from the keyboard (from {@link InputConnection#commitContent})
+     * </ol>
+     *
+     * <p>When setting a listener, clients should also declare the MIME types accepted by it.
+     * When invoked with other types of content, the listener may reject the content (defer to
+     * the default platform behavior) or execute some other fallback logic. The MIME types
+     * declared here allow different features to optionally alter their behavior. For example,
+     * the soft keyboard may choose to hide its UI for inserting GIFs for a particular input
+     * field if the MIME types set here for that field don't include "image/gif" or "image/*".
+     *
+     * <p>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC
+     * MIME types. As a result, you should always write your MIME types with lowercase letters,
+     * or use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to
+     * lowercase.
+     *
+     * @param view The target view.
+     * @param mimeTypes The MIME types accepted by the given listener. These may use patterns
+     *                  such as "image/*", but may not start with a wildcard. This argument must
+     *                  not be null or empty if a non-null listener is passed in.
+     * @param listener The listener to use. This can be null to reset to the default behavior.
+     */
+    public static void setOnReceiveContentListener(@NonNull View view, @Nullable String[] mimeTypes,
+            @Nullable OnReceiveContentListener listener) {
+        mimeTypes = (mimeTypes == null || mimeTypes.length == 0) ? null : mimeTypes;
+        if (listener != null) {
+            Preconditions.checkArgument(mimeTypes != null,
+                    "When the listener is set, MIME types must also be set");
+        }
+        if (mimeTypes != null) {
+            boolean hasLeadingWildcard = false;
+            for (String mimeType : mimeTypes) {
+                if (mimeType.startsWith("*")) {
+                    hasLeadingWildcard = true;
+                    break;
+                }
+            }
+            Preconditions.checkArgument(!hasLeadingWildcard,
+                    "A MIME type set here must not start with *: " + Arrays.toString(mimeTypes));
+        }
+        view.setTag(R.id.tag_on_receive_content_mime_types, mimeTypes);
+        view.setTag(R.id.tag_on_receive_content_listener, listener);
+    }
+
+    /**
+     * Returns the MIME types accepted by the listener configured on the given view via
+     * {@link #setOnReceiveContentListener}. By default returns null.
+     *
+     * <p>Different features (e.g. pasting from the clipboard, inserting stickers from the soft
+     * keyboard, etc) may optionally use this metadata to conditionally alter their behavior. For
+     * example, a soft keyboard may choose to hide its UI for inserting GIFs for a particular
+     * input field if the MIME types returned here for that field don't include "image/gif" or
+     * "image/*".
+     *
+     * <p>Note: Comparisons of MIME types should be performed using utilities such as
+     * {@link ClipDescription#compareMimeTypes} rather than simple string equality, in order to
+     * correctly handle patterns such as "text/*", "image/*", etc. Note that MIME type matching
+     * in the Android framework is case-sensitive, unlike formal RFC MIME types. As a result,
+     * you should always write your MIME types with lowercase letters, or use
+     * {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to
+     * lowercase.
+     *
+     * @param view The target view.
+     *
+     * @return The MIME types accepted by the {@link OnReceiveContentListener} for the given view
+     * (may include patterns such as "image/*").
+     */
+    @Nullable
+    public static String[] getOnReceiveContentMimeTypes(@NonNull View view) {
+        return (String[]) view.getTag(R.id.tag_on_receive_content_mime_types);
+    }
+
+    /**
+     * Receives the given content.
+     *
+     * <p>If a listener is set, invokes the listener. If the listener returns a non-null result,
+     * executes the fallback handling for the portion of the content returned by the listener.
+     *
+     * <p>If no listener is set, executes the fallback handling.
+     *
+     * <p>The fallback handling is defined by the target view if the view implements
+     * {@link OnReceiveContentViewBehavior}, or is simply a no-op.
+     *
+     * @param view The target view.
+     * @param payload The content to insert and related metadata.
+     *
+     * @return The portion of the passed-in content that was not handled (may be all, some, or none
+     * of the passed-in content).
+     */
+    @Nullable
+    public static ContentInfoCompat performReceiveContent(@NonNull View view,
+            @NonNull ContentInfoCompat payload) {
+        OnReceiveContentListener listener =
+                (OnReceiveContentListener) view.getTag(R.id.tag_on_receive_content_listener);
+        if (listener != null) {
+            ContentInfoCompat remaining = listener.onReceiveContent(view, payload);
+            return (remaining == null) ? null : getFallback(view).onReceiveContent(remaining);
+        }
+        return getFallback(view).onReceiveContent(payload);
+    }
+
+    private static OnReceiveContentViewBehavior getFallback(@NonNull View view) {
+        if (view instanceof OnReceiveContentViewBehavior) {
+            return ((OnReceiveContentViewBehavior) view);
+        }
+        return NO_OP_ON_RECEIVE_CONTENT_VIEW_BEHAVIOR;
+    }
+
+    private static final OnReceiveContentViewBehavior NO_OP_ON_RECEIVE_CONTENT_VIEW_BEHAVIOR =
+            new OnReceiveContentViewBehavior() {
+                @Override
+                public ContentInfoCompat onReceiveContent(@NonNull ContentInfoCompat payload) {
+                    return payload;
+                }
+            };
+
+    /**
      * Controls whether the entire hierarchy under this view will save its
      * state when a state saving traversal occurs from its parent.
      *
@@ -4584,6 +4724,77 @@
                 return insets;
             }
         }
+
+        static void setOnApplyWindowInsetsListener(final @NonNull View v,
+                final @Nullable OnApplyWindowInsetsListener listener) {
+            // For backward compatibility of WindowInsetsAnimation, we use an
+            // OnApplyWindowInsetsListener. We use the view tags to keep track of both listeners
+            if (Build.VERSION.SDK_INT < 30) {
+                v.setTag(R.id.tag_on_apply_window_listener, listener);
+            }
+
+            if (listener == null) {
+                // If the listener is null, we need to make sure our compat listener, if any, is
+                // set in-lieu of the listener being removed.
+                View.OnApplyWindowInsetsListener compatInsetsAnimationCallback =
+                        (View.OnApplyWindowInsetsListener) v.getTag(
+                                R.id.tag_window_insets_animation_callback);
+                v.setOnApplyWindowInsetsListener(compatInsetsAnimationCallback);
+                return;
+            }
+
+            v.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+                WindowInsetsCompat mLastInsets = null;
+                WindowInsets mReturnedInsets = null;
+
+                @Override
+                public WindowInsets onApplyWindowInsets(final View view,
+                        final WindowInsets insets) {
+                    WindowInsetsCompat compatInsets = WindowInsetsCompat.toWindowInsetsCompat(
+                            insets, view);
+                    if (Build.VERSION.SDK_INT < 30) {
+                        callCompatInsetAnimationCallback(insets, v);
+
+                        if (compatInsets.equals(mLastInsets)) {
+                            // We got the same insets we just return the previously computed insets.
+                            return mReturnedInsets;
+                        }
+                        mLastInsets = compatInsets;
+                    }
+                    compatInsets = listener.onApplyWindowInsets(view, compatInsets);
+
+                    if (Build.VERSION.SDK_INT >= 30) {
+                        return compatInsets.toWindowInsets();
+                    }
+
+                    // On API < 30, the visibleInsets, used to built WindowInsetsCompat, are
+                    // updated after the insets dispatch so we don't have the updated visible
+                    // insets at that point. As a workaround, we re-apply the insets so we know
+                    // that we'll have the right value the next time it's called.
+                    requestApplyInsets(view);
+                    // Keep a copy in case the insets haven't changed on the next call so we don't
+                    // need to call the listener again.
+                    mReturnedInsets = compatInsets.toWindowInsets();
+                    return mReturnedInsets;
+                }
+            });
+        }
+
+        /**
+         * The backport of {@link WindowInsetsAnimationCompat.Callback} on API < 30 relies on
+         * onApplyWindowInsetsListener, so if this callback is set, we'll call it in this method
+         */
+        static void callCompatInsetAnimationCallback(final @NonNull WindowInsets insets,
+                final @NonNull View v) {
+            // In case a WindowInsetsAnimationCompat.Callback is set, make sure to
+            // call its compat listener.
+            View.OnApplyWindowInsetsListener insetsAnimationCallback =
+                    (View.OnApplyWindowInsetsListener) v.getTag(
+                            R.id.tag_window_insets_animation_callback);
+            if (insetsAnimationCallback != null) {
+                insetsAnimationCallback.onApplyWindowInsets(v, insets);
+            }
+        }
     }
 
     @RequiresApi(23)
diff --git a/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationCompat.java b/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationCompat.java
new file mode 100644
index 0000000..15171ed
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationCompat.java
@@ -0,0 +1,914 @@
+/*
+ * 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.core.view;
+
+import static androidx.core.view.WindowInsetsCompat.toWindowInsetsCompat;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.FloatRange;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.core.R;
+import androidx.core.graphics.Insets;
+import androidx.core.view.WindowInsetsCompat.Type;
+import androidx.core.view.WindowInsetsCompat.Type.InsetsType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Class representing an animation of a set of windows that cause insets.
+ */
+public final class WindowInsetsAnimationCompat {
+
+    private Impl mImpl;
+
+    /**
+     * Creates a new {@link WindowInsetsAnimationCompat} object.
+     * <p>
+     * This should only be used for testing, as usually the system creates this object for the
+     * application to listen to with {@link WindowInsetsAnimationCompat.Callback}.
+     * </p>
+     *
+     * @param typeMask       The bitmask of {@link WindowInsetsCompat.Type}s that are animating.
+     * @param interpolator   The interpolator of the animation.
+     * @param durationMillis The duration of the animation in
+     *                       {@link java.util.concurrent.TimeUnit#MILLISECONDS}.
+     */
+    public WindowInsetsAnimationCompat(
+            @InsetsType int typeMask, @Nullable Interpolator interpolator,
+            long durationMillis) {
+        if (Build.VERSION.SDK_INT >= 30) {
+            mImpl = new Impl30(typeMask, interpolator, durationMillis);
+        } else if (Build.VERSION.SDK_INT >= 21) {
+            mImpl = new Impl21(typeMask, interpolator, durationMillis);
+        } else {
+            mImpl = new Impl(0, interpolator, durationMillis);
+        }
+    }
+
+    @RequiresApi(30)
+    private WindowInsetsAnimationCompat(@NonNull WindowInsetsAnimation animation) {
+        this(0, null, 0);
+        if (Build.VERSION.SDK_INT >= 30) {
+            mImpl = new Impl30(animation);
+        }
+    }
+
+    /**
+     * @return The bitmask of {@link Type} that are animating.
+     */
+    @InsetsType
+    public int getTypeMask() {
+        return mImpl.getTypeMask();
+    }
+
+    /**
+     * Returns the raw fractional progress of this animation between
+     * start state of the animation and the end state of the animation. Note
+     * that this progress is the global progress of the animation, whereas
+     * {@link WindowInsetsAnimationCompat.Callback#onProgress} will only dispatch the insets that
+     * may be inset with {@link WindowInsetsCompat#inset} by parents of views in the hierarchy.
+     * Progress per insets animation is global for the entire animation. One animation animates
+     * all things together (in, out, ...). If they don't animate together, we'd have
+     * multiple animations.
+     * <p>
+     * Note: In case the application is controlling the animation, the valued returned here will
+     * be the same as the application passed into
+     *
+     * {@link WindowInsetsAnimationControllerCompat#setInsetsAndAlpha(
+     * androidx.core.graphics.Insets, float, float)}.
+     * </p>
+     *
+     * @return The current progress of this animation.
+     */
+    @FloatRange(from = 0f, to = 1f)
+    public float getFraction() {
+        return mImpl.getFraction();
+    }
+
+    /**
+     * Returns the interpolated fractional progress of this animation between
+     * start state of the animation and the end state of the animation. Note
+     * that this progress is the global progress of the animation, whereas
+     * {@link WindowInsetsAnimationCompat.Callback#onProgress} will only dispatch the
+     * insets that may
+     * be inset with {@link WindowInsetsCompat#inset} by parents of views in the hierarchy.
+     * Progress per insets animation is global for the entire animation. One animation animates
+     * all things together (in, out, ...). If they don't animate together, we'd have
+     * multiple animations.
+     * <p>
+     * Note: In case the application is controlling the animation, the valued returned here will
+     * be the same as the application passed into
+     * {@link WindowInsetsAnimationControllerCompat#setInsetsAndAlpha(Insets, float, float)},
+     * interpolated with the interpolator passed into
+     * {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation}.
+     * <p>
+     * Note: For system-initiated animations, this will always return a valid value between 0
+     * and 1.
+     *
+     * @return The current interpolated progress of this animation.
+     * @see #getFraction() for raw fraction.
+     */
+    public float getInterpolatedFraction() {
+        return mImpl.getInterpolatedFraction();
+    }
+
+    /**
+     * Retrieves the interpolator used for this animation, or {@code null} if this animation
+     * doesn't follow an interpolation curved. For system-initiated animations, this will never
+     * return {@code null}.
+     *
+     * @return The interpolator used for this animation.
+     */
+    @Nullable
+    public Interpolator getInterpolator() {
+        return mImpl.getInterpolator();
+    }
+
+    /**
+     * @return duration of animation in {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or
+     * -1 if the animation doesn't have a fixed duration.
+     */
+    public long getDurationMillis() {
+        return mImpl.getDurationMillis();
+    }
+
+    /**
+     * Set fraction of the progress if {@link Type} animation is controlled by the app.
+     * <p>
+     * Note: This should only be used for testing, as the system fills in the fraction for the
+     * application or the fraction that was passed into
+     * {@link WindowInsetsAnimationControllerCompat#setInsetsAndAlpha(Insets, float, float)} is
+     * being used.
+     *
+     * @param fraction fractional progress between 0 and 1 where 0 represents hidden and
+     *                 zero progress and 1 represent fully shown final state.
+     * @see #getFraction()
+     */
+    public void setFraction(@FloatRange(from = 0f, to = 1f) float fraction) {
+        mImpl.setFraction(fraction);
+    }
+
+    /**
+     * Retrieves the translucency of the windows that are animating.
+     *
+     * @return Alpha of windows that cause insets of type {@link Type}.
+     */
+    @FloatRange(from = 0f, to = 1f)
+    public float getAlpha() {
+        return mImpl.getAlpha();
+    }
+
+    /**
+     * Sets the translucency of the windows that are animating.
+     * <p>
+     * Note: This should only be used for testing, as the system fills in the alpha for the
+     * application or the alpha that was passed into
+     * {@link WindowInsetsAnimationControllerCompat#setInsetsAndAlpha(Insets, float, float)} is
+     * being used.
+     *
+     * @param alpha Alpha of windows that cause insets of type {@link Type}.
+     * @see #getAlpha()
+     */
+    public void setAlpha(@FloatRange(from = 0f, to = 1f) float alpha) {
+        mImpl.setAlpha(alpha);
+    }
+
+    /**
+     * Class representing the range of an {@link WindowInsetsAnimationCompat}
+     */
+    public static final class Bounds {
+
+        private final Insets mLowerBound;
+        private final Insets mUpperBound;
+
+        public Bounds(@NonNull Insets lowerBound, @NonNull Insets upperBound) {
+            mLowerBound = lowerBound;
+            mUpperBound = upperBound;
+        }
+
+        @RequiresApi(30)
+        public Bounds(@NonNull WindowInsetsAnimation.Bounds bounds) {
+            mLowerBound = Impl30.getLowerBounds(bounds);
+            mUpperBound = Impl30.getHigherBounds(bounds);
+        }
+
+        /**
+         * Queries the lower inset bound of the animation. If the animation is about showing or
+         * hiding a window that cause insets, the lower bound is {@link Insets#NONE} and the upper
+         * bound is the same as {@link WindowInsetsCompat#getInsets(int)} for the fully shown
+         * state. This
+         * is the same as {@link WindowInsetsAnimationControllerCompat#getHiddenStateInsets} and
+         * {@link WindowInsetsAnimationControllerCompat#getShownStateInsets} in case the listener
+         * gets invoked because of an animation that originates from
+         * {@link WindowInsetsAnimationControllerCompat}.
+         * <p>
+         * However, if the size of a window that causes insets is changing, these are the
+         * lower/upper bounds of that size animation.
+         * </p>
+         * There are no overlapping animations for a specific type, but there may be multiple
+         * animations running at the same time for different inset types.
+         *
+         * @see #getUpperBound()
+         * @see WindowInsetsAnimationControllerCompat#getHiddenStateInsets
+         */
+        @NonNull
+        public Insets getLowerBound() {
+            return mLowerBound;
+        }
+
+        /**
+         * Queries the upper inset bound of the animation. If the animation is about showing or
+         * hiding a window that cause insets, the lower bound is {@link Insets#NONE} nd the upper
+         * bound is the same as {@link WindowInsetsCompat#getInsets(int)} for the fully shown
+         * state. This is the same as
+         * {@link WindowInsetsAnimationControllerCompat#getHiddenStateInsets} and
+         * {@link WindowInsetsAnimationControllerCompat#getShownStateInsets} in case the listener
+         * gets invoked because of an animation that originates from
+         * {@link WindowInsetsAnimationControllerCompat}.
+         * <p>
+         * However, if the size of a window that causes insets is changing, these are the
+         * lower/upper bounds of that size animation.
+         * <p>
+         * There are no overlapping animations for a specific type, but there may be multiple
+         * animations running at the same time for different inset types.
+         *
+         * @see #getLowerBound()
+         * @see WindowInsetsAnimationControllerCompat#getShownStateInsets
+         */
+        @NonNull
+        public Insets getUpperBound() {
+            return mUpperBound;
+        }
+
+        /**
+         * Insets both the lower and upper bound by the specified insets. This is to be used in
+         * {@link WindowInsetsAnimationCompat.Callback#onStart} to indicate that a part of the
+         * insets has been used to offset or clip its children, and the children shouldn't worry
+         * about that part anymore.
+         *
+         * @param insets The amount to inset.
+         * @return A copy of this instance inset in the given directions.
+         * @see WindowInsetsCompat#inset
+         * @see WindowInsetsAnimationCompat.Callback#onStart
+         */
+        @NonNull
+        public Bounds inset(@NonNull Insets insets) {
+            return new Bounds(
+                    // TODO: refactor so that WindowInsets.insetInsets() is in a more appropriate
+                    //  place eventually.
+                    WindowInsetsCompat.insetInsets(
+                            mLowerBound, insets.left, insets.top, insets.right, insets.bottom),
+                    WindowInsetsCompat.insetInsets(
+                            mUpperBound, insets.left, insets.top, insets.right, insets.bottom));
+        }
+
+        @Override
+        public String toString() {
+            return "Bounds{lower=" + mLowerBound + " upper=" + mUpperBound + "}";
+        }
+
+        /**
+         * Creates a new instance of {@link WindowInsetsAnimation.Bounds} from this compat instance.
+         */
+        @RequiresApi(30)
+        @NonNull
+        public WindowInsetsAnimation.Bounds toPlatformBounds() {
+            return Impl30.createPlatformBounds(this);
+        }
+    }
+
+    @RequiresApi(30)
+    static WindowInsetsAnimationCompat toWindowInsetsAnimationCompat(
+            WindowInsetsAnimation windowInsetsAnimation) {
+        return new WindowInsetsAnimationCompat(windowInsetsAnimation);
+    }
+
+    /**
+     * Interface that allows the application to listen to animation events for windows that cause
+     * insets.
+     */
+    public abstract static class Callback {
+
+        /**
+         * Return value for {@link #getDispatchMode()}: Dispatching of animation events should
+         * stop at this level in the view hierarchy, and no animation events should be dispatch to
+         * the subtree of the view hierarchy.
+         */
+        public static final int DISPATCH_MODE_STOP = 0;
+
+        /**
+         * Return value for {@link #getDispatchMode()}: Dispatching of animation events should
+         * continue in the view hierarchy.
+         */
+        public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1;
+
+        /** @hide */
+        @IntDef(value = {
+                DISPATCH_MODE_STOP,
+                DISPATCH_MODE_CONTINUE_ON_SUBTREE
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        public @interface DispatchMode {
+        }
+
+        @DispatchMode
+        private final int mDispatchMode;
+
+        /**
+         * Creates a new {@link WindowInsetsAnimationCompat} callback with the given
+         * {@link #getDispatchMode() dispatch mode}.
+         *
+         * @param dispatchMode The dispatch mode for this callback. See {@link #getDispatchMode()}.
+         */
+        public Callback(@DispatchMode int dispatchMode) {
+            mDispatchMode = dispatchMode;
+        }
+
+        /**
+         * Retrieves the dispatch mode of this listener. Dispatch of the all animation events is
+         * hierarchical: It will starts at the root of the view hierarchy and then traverse it and
+         * invoke the callback of the specific {@link View} that is being traversed.
+         * The method may return either {@link #DISPATCH_MODE_CONTINUE_ON_SUBTREE} to indicate that
+         * animation events should be propagated to the subtree of the view hierarchy, or
+         * {@link #DISPATCH_MODE_STOP} to stop dispatching. In that case, all animation callbacks
+         * related to the animation passed in will be stopped from propagating to the subtree of the
+         * hierarchy.
+         * <p>
+         * Also note that {@link #DISPATCH_MODE_STOP} behaves the same way as
+         * returning {@link WindowInsetsCompat#CONSUMED} during the regular insets dispatch in
+         * {@link View#onApplyWindowInsets}.
+         *
+         * @return Either {@link #DISPATCH_MODE_CONTINUE_ON_SUBTREE} to indicate that dispatching of
+         * animation events will continue to the subtree of the view hierarchy, or
+         * {@link #DISPATCH_MODE_STOP} to indicate that animation events will stop
+         * dispatching.
+         */
+        @DispatchMode
+        public final int getDispatchMode() {
+            return mDispatchMode;
+        }
+
+        /**
+         * Called when an insets animation is about to start and before the views have been
+         * re-laid out due to an animation.
+         * <p>
+         * This ordering allows the application to inspect the end state after the animation has
+         * finished, and then revert to the starting state of the animation in the first
+         * {@link #onProgress} callback by using post-layout view properties like {@link View#setX}
+         * and related methods.
+         * <p>
+         * The ordering of events during an insets animation is
+         * the following:
+         * <ul>
+         *     <li>Application calls {@link WindowInsetsControllerCompat#hide(int)},
+         *     {@link WindowInsetsControllerCompat#show(int)},
+         *     {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation}</li>
+         *     <li>onPrepare is called on the view hierarchy listeners</li>
+         *     <li>{@link View#onApplyWindowInsets} will be called with the end state of the
+         *     animation</li>
+         *     <li>View hierarchy gets laid out according to the changes the application has
+         *     requested due to the new insets being dispatched</li>
+         *     <li>{@link #onStart} is called <em>before</em> the view
+         *     hierarchy gets drawn in the new laid out state</li>
+         *     <li>{@link #onProgress} is called immediately after with the animation start
+         *     state</li>
+         *     <li>The frame gets drawn.</li>
+         * </ul>
+         * <p>
+         * Note: If the animation is application controlled by using
+         * {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation}, the end state of
+         * the animation is undefined as the application may decide on the end state only by
+         * passing in {@code shown} parameter when calling
+         * {@link WindowInsetsAnimationControllerCompat#finish}. In this situation, the system
+         * will dispatch the insets in the opposite visibility state before the animation starts.
+         * Example: When controlling the input method with
+         * {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation} and the input method
+         * is currently showing, {@link View#onApplyWindowInsets} will receive a
+         * {@link WindowInsetsCompat} instance for which {@link WindowInsetsCompat#isVisible}
+         * will return {@code false} for {@link WindowInsetsCompat.Type#ime}.
+         *
+         * @param animation The animation that is about to start.
+         */
+        public void onPrepare(@NonNull WindowInsetsAnimationCompat animation) {
+        }
+
+        /**
+         * Called when an insets animation gets started.
+         * <p>
+         * This ordering allows the application to inspect the end state after the animation has
+         * finished, and then revert to the starting state of the animation in the first
+         * {@link #onProgress} callback by using post-layout view properties like {@link View#setX}
+         * and related methods.
+         * <p>
+         * The ordering of events during an insets animation is
+         * the following:
+         * <ul>
+         *     <li>Application calls {@link WindowInsetsControllerCompat#hide(int)},
+         *     {@link WindowInsetsControllerCompat#show(int)},
+         *     {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation}</li>
+         *     <li>onPrepare is called on the view hierarchy listeners</li>
+         *     <li>{@link View#onApplyWindowInsets} will be called with the end state of the
+         *     animation</li>
+         *     <li>View hierarchy gets laid out according to the changes the application has
+         *     requested due to the new insets being dispatched</li>
+         *     <li>{@link #onStart} is called <em>before</em> the view
+         *     hierarchy gets drawn in the new laid out state</li>
+         *     <li>{@link #onProgress} is called immediately after with the animation start
+         *     state</li>
+         *     <li>The frame gets drawn.</li>
+         * </ul>
+         * <p>
+         * Note that, like {@link #onProgress}, dispatch of the animation start event is
+         * hierarchical: It will starts at the root of the view hierarchy and then traverse it
+         * and invoke the callback of the specific {@link View} that is being traversed. The
+         * method may return a modified instance of the bounds by calling
+         * {@link WindowInsetsAnimationCompat.Bounds#inset} to indicate that a part of the insets
+         * have been used to offset or clip its children, and the children shouldn't worry about
+         * that part anymore. Furthermore, if {@link #getDispatchMode()} returns
+         * {@link #DISPATCH_MODE_STOP}, children of this view will not receive the callback anymore.
+         *
+         * @param animation The animation that is about to start.
+         * @param bounds    The bounds in which animation happens.
+         * @return The animation bounds representing the part of the insets that should be
+         * dispatched to
+         * the subtree of the hierarchy.
+         */
+        @NonNull
+        public WindowInsetsAnimationCompat.Bounds onStart(
+                @NonNull WindowInsetsAnimationCompat animation,
+                @NonNull WindowInsetsAnimationCompat.Bounds bounds) {
+            return bounds;
+        }
+
+        /**
+         * Called when the insets change as part of running an animation. Note that even if multiple
+         * animations for different types are running, there will only be one progress callback per
+         * frame. The {@code insets} passed as an argument represents the overall state and will
+         * include all types, regardless of whether they are animating or not.
+         * <p>
+         * Note that insets dispatch is hierarchical: It will start at the root of the view
+         * hierarchy, and then traverse it and invoke the callback of the specific {@link View}
+         * being traversed. The method may return a modified instance by calling
+         * {@link WindowInsetsCompat#inset(int, int, int, int)} to indicate that a part of the
+         * insets have been used to offset or clip its children, and the children shouldn't worry
+         * about that part anymore. Furthermore, if {@link #getDispatchMode()} returns
+         * {@link #DISPATCH_MODE_STOP}, children of this view will not receive the callback anymore.
+         *
+         * @param insets            The current insets.
+         * @param runningAnimations The currently running animations.
+         * @return The insets to dispatch to the subtree of the hierarchy.
+         */
+        @NonNull
+        public abstract WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets,
+                @NonNull List<WindowInsetsAnimationCompat> runningAnimations);
+
+        /**
+         * Called when an insets animation has ended.
+         *
+         * @param animation The animation that has ended. This will be the same instance
+         *                  as passed into {@link #onStart}
+         */
+        public void onEnd(@NonNull WindowInsetsAnimationCompat animation) {
+        }
+    }
+
+    static void setCallback(@NonNull View view, @Nullable Callback callback) {
+        if (Build.VERSION.SDK_INT >= 30) {
+            Impl30.setCallback(view, callback);
+        } else if (Build.VERSION.SDK_INT >= 21) {
+            Impl21.setCallback(view, callback);
+        }
+        // Do nothing pre 21
+    }
+
+    private static class Impl {
+        @InsetsType
+        private final int mTypeMask;
+        private float mFraction;
+        @Nullable
+        private final Interpolator mInterpolator;
+        private final long mDurationMillis;
+        private float mAlpha;
+
+        Impl(int typeMask, @Nullable Interpolator interpolator, long durationMillis) {
+            mTypeMask = typeMask;
+            mInterpolator = interpolator;
+            mDurationMillis = durationMillis;
+        }
+
+        public int getTypeMask() {
+            return mTypeMask;
+        }
+
+        public float getFraction() {
+            return mFraction;
+        }
+
+        public float getInterpolatedFraction() {
+            if (mInterpolator != null) {
+                return mInterpolator.getInterpolation(mFraction);
+            }
+            return mFraction;
+        }
+
+        @Nullable
+        public Interpolator getInterpolator() {
+            return mInterpolator;
+        }
+
+        public long getDurationMillis() {
+            return mDurationMillis;
+        }
+
+        public float getAlpha() {
+            return mAlpha;
+        }
+
+        public void setFraction(float fraction) {
+            mFraction = fraction;
+        }
+
+        public void setAlpha(float alpha) {
+            mAlpha = alpha;
+        }
+
+    }
+
+    @RequiresApi(21)
+    private static class Impl21 extends Impl {
+
+        Impl21(int typeMask, @Nullable Interpolator interpolator, long durationMillis) {
+            super(typeMask, interpolator, durationMillis);
+        }
+
+        static void setCallback(@NonNull final View view,
+                @Nullable final Callback callback) {
+
+            Object userListener = view.getTag(R.id.tag_on_apply_window_listener);
+            if (callback == null) {
+                view.setTag(R.id.tag_window_insets_animation_callback, null);
+                if (userListener == null) {
+                    // If no user defined listener is set, that means our listener is the one set.
+                    // Make sure to remove it.
+                    view.setOnApplyWindowInsetsListener(null);
+                }
+            } else {
+                View.OnApplyWindowInsetsListener proxyListener = createProxyListener(callback);
+                view.setTag(R.id.tag_window_insets_animation_callback, proxyListener);
+
+                // We rely on OnApplyWindowInsetsListener, but one might already be set by the
+                // application, so we only register it on the view if none is set yet.
+                // If one is set using ViewCompat.setOnApplyWindowInsetsListener,
+                // this Callback will be called by the exiting listener.
+                if (userListener == null) {
+                    view.setOnApplyWindowInsetsListener(proxyListener);
+                }
+            }
+        }
+
+        @NonNull
+        private static View.OnApplyWindowInsetsListener createProxyListener(
+                @NonNull final Callback callback) {
+            return new Impl21OnApplyWindowInsetsListener(callback);
+        }
+
+        @NonNull
+        static Bounds computeAnimationBounds(
+                @NonNull WindowInsetsCompat targetInsets,
+                @NonNull WindowInsetsCompat startingInsets, int mask) {
+            Insets targetInsetsInsets = targetInsets.getInsets(mask);
+            Insets startingInsetsInsets = startingInsets.getInsets(mask);
+            final Insets lowerBound = Insets.of(
+                    Math.min(targetInsetsInsets.left, startingInsetsInsets.left),
+                    Math.min(targetInsetsInsets.top, startingInsetsInsets.top),
+                    Math.min(targetInsetsInsets.right, startingInsetsInsets.right),
+                    Math.min(targetInsetsInsets.bottom, startingInsetsInsets.bottom)
+            );
+            final Insets upperBound = Insets.of(
+                    Math.max(targetInsetsInsets.left, startingInsetsInsets.left),
+                    Math.max(targetInsetsInsets.top, startingInsetsInsets.top),
+                    Math.max(targetInsetsInsets.right, startingInsetsInsets.right),
+                    Math.max(targetInsetsInsets.bottom, startingInsetsInsets.bottom)
+            );
+            return new Bounds(lowerBound, upperBound);
+        }
+
+        @SuppressLint("WrongConstant") // We iterate over all the constants.
+        static int buildAnimationMask(WindowInsetsCompat targetInsets,
+                WindowInsetsCompat currentInsets) {
+            int animatingMask = 0;
+            for (int i = WindowInsetsCompat.Type.FIRST; i <= WindowInsetsCompat.Type.LAST;
+                    i = i << 1) {
+                if (!targetInsets.getInsets(i).equals(currentInsets.getInsets(i))) {
+                    animatingMask |= i;
+                }
+            }
+            return animatingMask;
+        }
+
+        static WindowInsetsCompat interpolateInsets(
+                WindowInsetsCompat target, WindowInsetsCompat starting,
+                float fraction, int typeMask) {
+            WindowInsetsCompat.Builder builder = new WindowInsetsCompat.Builder(target);
+            for (int i = WindowInsetsCompat.Type.FIRST; i <= WindowInsetsCompat.Type.LAST;
+                    i = i << 1) {
+                if ((typeMask & i) == 0) {
+                    continue;
+                }
+                Insets targetInsets = target.getInsets(typeMask & i);
+                Insets startingInsets = starting.getInsets(typeMask & i);
+                Insets interpolatedInsets = WindowInsetsCompat.insetInsets(
+                        targetInsets,
+                        (int) (0.5 + (targetInsets.left - startingInsets.left) * (1 - fraction)),
+                        (int) (0.5 + (targetInsets.top - startingInsets.top) * (1 - fraction)),
+                        (int) (0.5 + (targetInsets.right - startingInsets.right) * (1 - fraction)),
+                        (int) (0.5 + (targetInsets.bottom - startingInsets.bottom) * (1 - fraction))
+
+                );
+                builder.setInsets(typeMask & i, interpolatedInsets);
+
+            }
+
+            return builder.build();
+        }
+
+        /**
+         * Wrapper class around a {@link Callback} that will trigger the callback when
+         * {@link View#onApplyWindowInsets(WindowInsets)} is called
+         */
+        @RequiresApi(21)
+        private static class Impl21OnApplyWindowInsetsListener implements
+                View.OnApplyWindowInsetsListener {
+
+            private static final int COMPAT_ANIMATION_DURATION = 160;
+
+            final Callback mCallback;
+            // We save the last insets to compute the starting insets for the animation.
+            private WindowInsetsCompat mLastInsets;
+
+            Impl21OnApplyWindowInsetsListener(Callback callback) {
+                mCallback = callback;
+                mLastInsets = null;
+            }
+
+            @Override
+            public WindowInsets onApplyWindowInsets(final View v, final WindowInsets insets) {
+                // We cannot rely on the compat insets value until the view is laid out.
+                if (!v.isLaidOut()) {
+                    mLastInsets = toWindowInsetsCompat(insets);
+                    return insets;
+                }
+
+                final WindowInsetsCompat targetInsets = toWindowInsetsCompat(insets);
+
+                if (mLastInsets == null) {
+                    mLastInsets = ViewCompat.getRootWindowInsets(v);
+                }
+
+                // We only run the animation when the some insets are animating
+                final int animationMask = buildAnimationMask(targetInsets, mLastInsets);
+                if (animationMask == 0) {
+                    return insets;
+                }
+
+                final WindowInsetsCompat startingInsets = this.mLastInsets;
+                final WindowInsetsAnimationCompat anim =
+                        new WindowInsetsAnimationCompat(animationMask, new DecelerateInterpolator(),
+                                COMPAT_ANIMATION_DURATION);
+                anim.setFraction(0);
+
+                final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(
+                        anim.getDurationMillis());
+
+                // Compute the bounds of the animation
+                final Bounds animationBounds = computeAnimationBounds(targetInsets,
+                        startingInsets, animationMask
+                );
+
+                mCallback.onPrepare(anim);
+
+                animator.addUpdateListener(
+                        new ValueAnimator.AnimatorUpdateListener() {
+                            @Override
+                            public void onAnimationUpdate(ValueAnimator animator) {
+                                anim.setFraction(animator.getAnimatedFraction());
+                                WindowInsetsCompat interpolateInsets = interpolateInsets(
+                                        targetInsets,
+                                        startingInsets,
+                                        anim.getInterpolatedFraction(), animationMask);
+                                List<WindowInsetsAnimationCompat> runningAnimations =
+                                        Collections.singletonList(anim);
+                                mCallback.onProgress(interpolateInsets, runningAnimations);
+                            }
+                        });
+
+                animator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animator) {
+                        anim.setFraction(1);
+                        mCallback.onEnd(anim);
+                    }
+                });
+
+                // We need to call onStart and start the animator before the next draw
+                // to ensure the animation starts before the relayout caused by the change of
+                // insets.
+                v.getViewTreeObserver().addOnPreDrawListener(
+                        new ViewTreeObserver.OnPreDrawListener() {
+                            @Override
+                            public boolean onPreDraw() {
+                                v.getViewTreeObserver().removeOnPreDrawListener(this);
+                                mCallback.onStart(anim, animationBounds);
+                                animator.start();
+                                return true;
+                            }
+                        });
+                this.mLastInsets = targetInsets;
+
+                // If the app set an on apply window listener, it will be called after this
+                // and will decide whether to call the view's onApplyWindowInsets.
+                if (v.getTag(R.id.tag_on_apply_window_listener) == null) {
+                    return insets;
+                }
+
+                return v.onApplyWindowInsets(insets);
+            }
+        }
+    }
+
+    @RequiresApi(30)
+    private static class Impl30 extends Impl {
+
+        @NonNull
+        private final WindowInsetsAnimation mWrapped;
+
+        Impl30(@NonNull WindowInsetsAnimation wrapped) {
+            super(0, null, 0);
+            mWrapped = wrapped;
+        }
+
+        Impl30(int typeMask, Interpolator interpolator, long durationMillis) {
+            this(new WindowInsetsAnimation(typeMask, interpolator, durationMillis));
+        }
+
+        @Override
+        public int getTypeMask() {
+            return mWrapped.getTypeMask();
+        }
+
+        @Override
+        @Nullable
+        public Interpolator getInterpolator() {
+            return mWrapped.getInterpolator();
+        }
+
+        @Override
+        public long getDurationMillis() {
+            return mWrapped.getDurationMillis();
+        }
+
+        @Override
+        public float getFraction() {
+            return mWrapped.getFraction();
+        }
+
+        @Override
+        public void setFraction(float fraction) {
+            mWrapped.setFraction(fraction);
+        }
+
+        @Override
+        public float getInterpolatedFraction() {
+            return mWrapped.getInterpolatedFraction();
+        }
+
+        @RequiresApi(30)
+        private static class ProxyCallback extends WindowInsetsAnimation.Callback {
+
+            private final Callback mCompat;
+
+            ProxyCallback(@NonNull final WindowInsetsAnimationCompat.Callback compat) {
+                super(compat.getDispatchMode());
+                mCompat = compat;
+            }
+
+            private List<WindowInsetsAnimationCompat> mRORunningAnimations;
+            private ArrayList<WindowInsetsAnimationCompat> mTmpRunningAnimations;
+            private final HashMap<WindowInsetsAnimation, WindowInsetsAnimationCompat>
+                    mAnimations = new HashMap<>();
+
+            @NonNull
+            private WindowInsetsAnimationCompat getWindowInsetsAnimationCompat(
+                    @NonNull WindowInsetsAnimation animation) {
+                WindowInsetsAnimationCompat animationCompat = mAnimations.get(
+                        animation);
+                if (animationCompat == null) {
+                    animationCompat = toWindowInsetsAnimationCompat(animation);
+                    mAnimations.put(animation, animationCompat);
+                }
+                return animationCompat;
+            }
+
+            @Override
+            public void onPrepare(@NonNull WindowInsetsAnimation animation) {
+                mCompat.onPrepare(getWindowInsetsAnimationCompat(animation));
+            }
+
+            @NonNull
+            @Override
+            public WindowInsetsAnimation.Bounds onStart(
+                    @NonNull WindowInsetsAnimation animation,
+                    @NonNull WindowInsetsAnimation.Bounds bounds) {
+                return mCompat.onStart(
+                        getWindowInsetsAnimationCompat(animation),
+                        new Bounds(bounds))
+                        .toPlatformBounds();
+            }
+
+            @NonNull
+            @Override
+            public WindowInsets onProgress(@NonNull WindowInsets insets,
+                    @NonNull List<WindowInsetsAnimation> runningAnimations) {
+                if (mTmpRunningAnimations == null) {
+                    mTmpRunningAnimations = new ArrayList<>(runningAnimations.size());
+                    mRORunningAnimations = Collections.unmodifiableList(mTmpRunningAnimations);
+                } else {
+                    mTmpRunningAnimations.clear();
+                }
+
+                for (int i = runningAnimations.size() - 1; i >= 0; i--) {
+                    WindowInsetsAnimation animation = runningAnimations.get(i);
+                    WindowInsetsAnimationCompat animationCompat =
+                            getWindowInsetsAnimationCompat(animation);
+                    animationCompat.setFraction(animation.getFraction());
+                    mTmpRunningAnimations.add(animationCompat);
+                }
+                return mCompat.onProgress(
+                        WindowInsetsCompat.toWindowInsetsCompat(insets),
+                        mRORunningAnimations).toWindowInsets();
+            }
+
+            @Override
+            public void onEnd(@NonNull WindowInsetsAnimation animation) {
+                mCompat.onEnd(getWindowInsetsAnimationCompat(animation));
+                mAnimations.remove(animation);
+            }
+        }
+
+        public static void setCallback(@NonNull View view, @Nullable Callback callback) {
+            WindowInsetsAnimation.Callback platformCallback =
+                    callback != null ? new ProxyCallback(callback) : null;
+            view.setWindowInsetsAnimationCallback(platformCallback);
+        }
+
+        @NonNull
+        public static WindowInsetsAnimation.Bounds createPlatformBounds(@NonNull Bounds bounds) {
+            return new WindowInsetsAnimation.Bounds(bounds.getLowerBound().toPlatformInsets(),
+                    bounds.getUpperBound().toPlatformInsets());
+        }
+
+        @NonNull
+        public static Insets getLowerBounds(@NonNull WindowInsetsAnimation.Bounds bounds) {
+            return Insets.toCompatInsets(bounds.getLowerBound());
+        }
+
+        @NonNull
+        public static Insets getHigherBounds(@NonNull WindowInsetsAnimation.Bounds bounds) {
+            return Insets.toCompatInsets(bounds.getUpperBound());
+        }
+    }
+}
diff --git a/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationControlListenerCompat.java b/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationControlListenerCompat.java
new file mode 100644
index 0000000..cc27431
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationControlListenerCompat.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 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.core.view;
+
+import android.view.inputmethod.EditorInfo;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.WindowInsetsCompat.Type;
+import androidx.core.view.WindowInsetsCompat.Type.InsetsType;
+
+/**
+ * Listener that encapsulates a request to
+ * {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation}.
+ *
+ * <p>
+ * Insets can be controlled with the supplied {@link WindowInsetsAnimationControllerCompat} from
+ * {@link #onReady} until either {@link #onFinished} or {@link #onCancelled}.
+ *
+ * <p>
+ * Once the control over insets is finished or cancelled, it will not be regained until a new
+ * request to {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation} is made.
+ *
+ * <p>
+ * The request to control insets can fail immediately. In that case {@link #onCancelled} will be
+ * invoked without a preceding {@link #onReady}.
+ *
+ * @see WindowInsetsControllerCompat#controlWindowInsetsAnimation
+ */
+public interface WindowInsetsAnimationControlListenerCompat {
+
+    /**
+     * Called when the animation is ready to be controlled. This may be delayed when the IME needs
+     * to redraw because of an {@link EditorInfo} change, or when the window is starting up.
+     *
+     * @param controller The controller to control the inset animation.
+     * @param types      The {@link Type}s it was able to gain control over. Note that this
+     *                   may be different than the types passed into
+     *                   {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation} in
+     *                   case the window wasn't able to gain the controls because it wasn't the
+     *                   IME target or not currently the window that's controlling the system bars.
+     * @see WindowInsetsAnimationControllerCompat#isReady
+     */
+    void onReady(@NonNull WindowInsetsAnimationControllerCompat controller, @InsetsType int types);
+
+    /**
+     * Called when the request for control over the insets has
+     * {@link WindowInsetsAnimationControllerCompat#finish finished}.
+     * <p>
+     * Once this callback is invoked, the supplied {@link WindowInsetsAnimationControllerCompat}
+     * is no longer {@link WindowInsetsAnimationControllerCompat#isReady() ready}.
+     * <p>
+     * Control will not be regained until a new request
+     * to {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation} is made.
+     *
+     * @param controller the controller which has finished.
+     * @see WindowInsetsAnimationControllerCompat#isFinished
+     */
+    void onFinished(@NonNull WindowInsetsAnimationControllerCompat controller);
+
+    /**
+     * Called when the request for control over the insets has been cancelled, either
+     * because the {@link android.os.CancellationSignal} associated with the
+     * {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation request} has been
+     * invoked, or the window has lost control over the insets (e.g. because it lost focus).
+     * <p>
+     * Once this callback is invoked, the supplied {@link WindowInsetsAnimationControllerCompat}
+     * is no longer {@link WindowInsetsAnimationControllerCompat#isReady() ready}.
+     * <p>
+     * Control will not be regained until a new request
+     * to {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation} is made.
+     *
+     * @param controller the controller which has been cancelled, or null if the request
+     *                   was cancelled before {@link #onReady} was invoked.
+     * @see WindowInsetsAnimationControllerCompat#isCancelled
+     */
+    void onCancelled(@Nullable WindowInsetsAnimationControllerCompat controller);
+}
diff --git a/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationControllerCompat.java b/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationControllerCompat.java
new file mode 100644
index 0000000..1458fcd
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/view/WindowInsetsAnimationControllerCompat.java
@@ -0,0 +1,364 @@
+/*
+ * 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.core.view;
+
+import android.os.Build;
+import android.view.View;
+import android.view.WindowInsetsAnimationController;
+
+import androidx.annotation.FloatRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.graphics.Insets;
+import androidx.core.view.WindowInsetsCompat.Type;
+import androidx.core.view.WindowInsetsCompat.Type.InsetsType;
+
+
+/**
+ * Controller for app-driven animation of system windows.
+ * <p>
+ * {@code WindowInsetsAnimationController} lets apps animate system windows such as
+ * the {@link android.inputmethodservice.InputMethodService IME}. The animation is
+ * synchronized, such that changes the system windows and the app's current frame
+ * are rendered at the same time.
+ * <p>
+ * Control is obtained through {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation}.
+ */
+public final class WindowInsetsAnimationControllerCompat {
+
+    private final Impl mImpl;
+
+    WindowInsetsAnimationControllerCompat() {
+        if (Build.VERSION.SDK_INT < 30) {
+            mImpl = new Impl();
+        } else {
+            throw new UnsupportedOperationException("On API 30+, the constructor taking a "
+                    + WindowInsetsAnimationController.class.getSimpleName() + " as parameter");
+        }
+    }
+
+    @RequiresApi(30)
+    WindowInsetsAnimationControllerCompat(
+            @NonNull WindowInsetsAnimationController controller) {
+        mImpl = new Impl30(controller);
+    }
+
+    /**
+     * Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden.
+     * <p>
+     * Note that these insets are always relative to the window, which is the same as being relative
+     * to {@link View#getRootView}
+     * <p>
+     * If there are any animation listeners registered, this value is the same as
+     * {@link WindowInsetsAnimationCompat.Bounds#getLowerBound()} that is being be passed into the
+     * root view of the hierarchy.
+     *
+     * @return Insets when the windows this animation is controlling are fully hidden.
+     * @see WindowInsetsAnimationCompat.Bounds#getLowerBound()
+     */
+    @NonNull
+    public Insets getHiddenStateInsets() {
+        return mImpl.getHiddenStateInsets();
+    }
+
+    /**
+     * Retrieves the {@link Insets} when the windows this animation is
+     * controlling are fully shown.
+     * <p>
+     * Note that these insets are always relative to the window, which is the same as being relative
+     * to {@link View#getRootView}
+     * <p>
+     * If there are any animation listeners registered, this value is the same as
+     * {@link WindowInsetsAnimationCompat.Bounds#getUpperBound()} that is being passed into the root
+     * view of hierarchy.
+     *
+     * @return Insets when the windows this animation is controlling are fully shown.
+     * @see WindowInsetsAnimationCompat.Bounds#getUpperBound()
+     */
+    @NonNull
+    public Insets getShownStateInsets() {
+        return mImpl.getShownStateInsets();
+    }
+
+    /**
+     * Retrieves the current insets.
+     * <p>
+     * Note that these insets are always relative to the window, which is the same as
+     * being relative
+     * to {@link View#getRootView}
+     *
+     * @return The current insets on the currently showing frame. These insets will change as the
+     * animation progresses to reflect the current insets provided by the controlled window.
+     */
+    @NonNull
+    public Insets getCurrentInsets() {
+        return mImpl.getCurrentInsets();
+    }
+
+    /**
+     * Returns the progress as previously set by {@code fraction} in {@link #setInsetsAndAlpha}
+     *
+     * @return the progress of the animation, where {@code 0} is fully hidden and {@code 1} is
+     * fully shown.
+     * <p>
+     * Note: this value represents raw overall progress of the animation
+     * i.e. the combined progress of insets and alpha.
+     * <p>
+     */
+    @FloatRange(from = 0f, to = 1f)
+    public float getCurrentFraction() {
+        return mImpl.getCurrentFraction();
+    }
+
+    /**
+     * Current alpha value of the window.
+     *
+     * @return float value between 0 and 1.
+     */
+    public float getCurrentAlpha() {
+        return mImpl.getCurrentAlpha();
+    }
+
+    /**
+     * @return The {@link Type}s this object is currently controlling.
+     */
+    @InsetsType
+    public int getTypes() {
+        return mImpl.getTypes();
+    }
+
+    /**
+     * Modifies the insets for the frame being drawn by indirectly moving the windows around in the
+     * system that are causing window insets.
+     * <p>
+     * Note that these insets are always relative to the window, which is the same as being relative
+     * to {@link View#getRootView}
+     * <p>
+     * Also note that this will <b>not</b> inform the view system of a full inset change via
+     * {@link View#dispatchApplyWindowInsets} in order to avoid a full layout pass during the
+     * animation. If you'd like to animate views during a window inset animation, register a
+     * {@link WindowInsetsAnimationCompat.Callback} by calling
+     *
+     * {@link ViewCompat#setWindowInsetsAnimationCallback(View,
+     * WindowInsetsAnimationCompat.Callback)}
+     * that will be notified about any insets change via
+     * {@link WindowInsetsAnimationCompat.Callback#onProgress} during the animation.
+     * <p>
+     * {@link View#dispatchApplyWindowInsets} will instead be called once the animation has
+     * finished, i.e. once {@link #finish} has been called.
+     * Note: If there are no insets, alpha animation is still applied.
+     *
+     * @param insets   The new insets to apply. Based on the requested insets, the system will
+     *                 calculate the positions of the windows in the system causing insets such that
+     *                 the resulting insets of that configuration will match the passed in
+     *                 parameter.
+     *                 Note that these insets are being clamped to the range from
+     *                 {@link #getHiddenStateInsets} to {@link #getShownStateInsets}.
+     *                 If you intend on changing alpha only, pass null or
+     *                 {@link #getCurrentInsets()}.
+     * @param alpha    The new alpha to apply to the inset side.
+     * @param fraction instantaneous animation progress. This value is dispatched to
+     *                 {@link WindowInsetsAnimationCompat.Callback}.
+     * @see WindowInsetsAnimationCompat.Callback
+     * @see ViewCompat#setWindowInsetsAnimationCallback(View, WindowInsetsAnimationCompat.Callback)
+     */
+
+    public void setInsetsAndAlpha(@Nullable Insets insets,
+            @FloatRange(from = 0f, to = 1f) float alpha,
+            @FloatRange(from = 0f, to = 1f) float fraction) {
+        mImpl.setInsetsAndAlpha(insets, alpha, fraction);
+    }
+
+    /**
+     * Finishes the animation, and leaves the windows shown or hidden.
+     * <p>
+     * After invoking {@link #finish}, this instance is no longer {@link #isReady ready}.
+     * <p>
+     * Note: Finishing an animation implicitly {@link #setInsetsAndAlpha sets insets and alpha}
+     * according to the requested end state without any further animation.
+     *
+     * @param shown if {@code true}, the windows will be shown after finishing the
+     *              animation. Otherwise they will be hidden.
+     */
+    public void finish(boolean shown) {
+        mImpl.finish(shown);
+    }
+
+    /**
+     * Returns whether this instance is ready to be used to control window insets.
+     * <p>
+     * Instances are ready when passed in {@link WindowInsetsAnimationControlListenerCompat#onReady}
+     * and stop being ready when it is either {@link #isFinished() finished} or
+     * {@link #isCancelled() cancelled}.
+     *
+     * @return {@code true} if the instance is ready, {@code false} otherwise.
+     */
+
+    public boolean isReady() {
+        return !isFinished() && !isCancelled();
+    }
+
+    /**
+     * Returns whether this instance has been finished by a call to {@link #finish}.
+     *
+     * @return {@code true} if the instance is finished, {@code false} otherwise.
+     * @see WindowInsetsAnimationControlListenerCompat#onFinished
+     */
+    public boolean isFinished() {
+        return mImpl.isFinished();
+    }
+
+    /**
+     * Returns whether this instance has been cancelled by the system, or by invoking the
+     * {@link android.os.CancellationSignal} passed into
+     * {@link WindowInsetsControllerCompat#controlWindowInsetsAnimation}.
+     *
+     * @return {@code true} if the instance is cancelled, {@code false} otherwise.
+     * @see WindowInsetsAnimationControlListenerCompat#onCancelled
+     */
+    public boolean isCancelled() {
+        return mImpl.isCancelled();
+    }
+
+    private static class Impl {
+        Impl() {
+            //privatex
+        }
+
+        @NonNull
+        public Insets getHiddenStateInsets() {
+            return Insets.NONE;
+        }
+
+        @NonNull
+        public Insets getShownStateInsets() {
+            return Insets.NONE;
+        }
+
+        @NonNull
+        public Insets getCurrentInsets() {
+            return Insets.NONE;
+        }
+
+        @FloatRange(from = 0f, to = 1f)
+        public float getCurrentFraction() {
+            return 0f;
+        }
+
+        public float getCurrentAlpha() {
+            return 0f;
+        }
+
+        @InsetsType
+        public int getTypes() {
+            return 0;
+        }
+
+        public void setInsetsAndAlpha(@Nullable Insets insets,
+                @FloatRange(from = 0f, to = 1f) float alpha,
+                @FloatRange(from = 0f, to = 1f) float fraction) {
+        }
+
+        void finish(boolean shown) {
+        }
+
+        public boolean isReady() {
+            return false;
+        }
+
+        boolean isFinished() {
+            return false;
+        }
+
+        boolean isCancelled() {
+            return true;
+        }
+    }
+
+    @RequiresApi(30)
+    private static class Impl30 extends Impl {
+
+        private final WindowInsetsAnimationController mController;
+
+        Impl30(@NonNull WindowInsetsAnimationController controller) {
+            mController = controller;
+        }
+
+        @NonNull
+        @Override
+        public Insets getHiddenStateInsets() {
+            return Insets.toCompatInsets(mController.getHiddenStateInsets());
+        }
+
+        @NonNull
+        @Override
+        public Insets getShownStateInsets() {
+            return Insets.toCompatInsets(mController.getShownStateInsets());
+        }
+
+        @NonNull
+        @Override
+        public Insets getCurrentInsets() {
+            return Insets.toCompatInsets(mController.getCurrentInsets());
+        }
+
+        @Override
+        public float getCurrentFraction() {
+            return mController.getCurrentFraction();
+        }
+
+        @Override
+        public float getCurrentAlpha() {
+            return mController.getCurrentAlpha();
+        }
+
+        @Override
+        public int getTypes() {
+            return mController.getTypes();
+        }
+
+        @Override
+        public void setInsetsAndAlpha(@Nullable Insets insets, float alpha, float fraction) {
+            mController.setInsetsAndAlpha(insets == null ? null : insets.toPlatformInsets(),
+                    alpha,
+                    fraction
+            );
+        }
+
+        @Override
+        void finish(boolean shown) {
+            mController.finish(shown);
+        }
+
+        @Override
+        public boolean isReady() {
+            return mController.isReady();
+        }
+
+        @Override
+        boolean isFinished() {
+            return mController.isFinished();
+        }
+
+        @Override
+        boolean isCancelled() {
+            return mController.isCancelled();
+        }
+    }
+}
+
diff --git a/core/core/src/main/java/androidx/core/view/WindowInsetsCompat.java b/core/core/src/main/java/androidx/core/view/WindowInsetsCompat.java
index 6e674e7..31a8a5b 100644
--- a/core/core/src/main/java/androidx/core/view/WindowInsetsCompat.java
+++ b/core/core/src/main/java/androidx/core/view/WindowInsetsCompat.java
@@ -884,7 +884,7 @@
         private Insets mSystemWindowInsets = null;
 
         private WindowInsetsCompat mRootWindowInsets;
-        private Insets mRootViewVisibleInsets;
+        Insets mRootViewVisibleInsets;
 
         Impl20(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) {
             super(host);
@@ -1168,6 +1168,13 @@
         private static void logReflectionError(Exception e) {
             Log.e(TAG, "Failed to get visible insets. (Reflection error). " + e.getMessage(), e);
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!super.equals(o)) return false;
+            Impl20 impl20 = (Impl20) o;
+            return Objects.equals(mRootViewVisibleInsets, impl20.mRootViewVisibleInsets);
+        }
     }
 
     @RequiresApi(21)
@@ -1241,7 +1248,8 @@
             if (!(o instanceof Impl28)) return false;
             Impl28 otherImpl28 = (Impl28) o;
             // On API 28+ we can rely on WindowInsets.equals()
-            return Objects.equals(mPlatformInsets, otherImpl28.mPlatformInsets);
+            return Objects.equals(mPlatformInsets, otherImpl28.mPlatformInsets)
+                    && Objects.equals(mRootViewVisibleInsets, otherImpl28.mRootViewVisibleInsets);
         }
 
         @Override
diff --git a/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java b/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
index 7c9ee1d..d904b05 100644
--- a/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
@@ -19,13 +19,16 @@
 import static android.os.Build.VERSION.SDK_INT;
 
 import android.content.Context;
-import android.graphics.Insets;
+import android.inputmethodservice.InputMethodService;
+import android.os.CancellationSignal;
 import android.view.View;
 import android.view.Window;
 import android.view.WindowInsets;
+import android.view.WindowInsetsAnimationControlListener;
 import android.view.WindowInsetsAnimationController;
 import android.view.WindowInsetsController;
 import android.view.WindowManager;
+import android.view.animation.Interpolator;
 import android.view.inputmethod.InputMethodManager;
 
 import androidx.annotation.IntDef;
@@ -33,10 +36,13 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.collection.SimpleArrayMap;
+import androidx.core.graphics.Insets;
 import androidx.core.view.WindowInsetsCompat.Type.InsetsType;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Provide simple controls of windows that generate insets.
@@ -48,36 +54,31 @@
 
     /**
      * The default option for {@link #setSystemBarsBehavior(int)}. System bars will be forcibly
-     * shown on any user interaction on the corresponding display if navigation bars are
-     * hidden by
-     * {@link #hide(int)} or
-     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
+     * shown on any user interaction on the corresponding display if navigation bars are hidden
+     * by {@link #hide(int)} or
+     * {@link WindowInsetsAnimationControllerCompat#setInsetsAndAlpha(Insets, float, float)}.
      */
     public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0;
 
     /**
-     * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain
-     * interactive when
-     * hiding navigation bars by calling {@link #hide(int)} or
-     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
-     *
-     * <p>When system bars are hidden in this mode, they can be revealed with system
-     * gestures, such
-     * as swiping from the edge of the screen where the bar is hidden from.</p>
+     * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain interactive
+     * when hiding navigation bars by calling {@link #hide(int)} or
+     * {@link WindowInsetsAnimationControllerCompat#setInsetsAndAlpha(Insets, float, float)}.
+     * <p>
+     * When system bars are hidden in this mode, they can be revealed with system
+     * gestures, such as swiping from the edge of the screen where the bar is hidden from.
      */
     public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1;
 
     /**
      * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain
-     * interactive when
-     * hiding navigation bars by calling {@link #hide(int)} or
-     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
-     *
-     * <p>When system bars are hidden in this mode, they can be revealed temporarily with system
+     * interactive when hiding navigation bars by calling {@link #hide(int)} or
+     * {@link WindowInsetsAnimationControllerCompat#setInsetsAndAlpha(Insets, float, float)}.
+     * <p>
+     * When system bars are hidden in this mode, they can be revealed temporarily with system
      * gestures, such as swiping from the edge of the screen where the bar is hidden from. These
      * transient system bars will overlay app’s content, may have some degree of
-     * transparency, and
-     * will automatically hide after a short timeout.</p>
+     * transparency, and will automatically hide after a short timeout.
      */
     public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2;
 
@@ -86,7 +87,7 @@
     @RequiresApi(30)
     private WindowInsetsControllerCompat(@NonNull WindowInsetsController insetsController) {
         if (SDK_INT >= 30) {
-            mImpl = new Impl30(insetsController);
+            mImpl = new Impl30(insetsController, this);
         } else {
             mImpl = new Impl();
         }
@@ -94,7 +95,7 @@
 
     public WindowInsetsControllerCompat(@NonNull Window window, @NonNull View view) {
         if (SDK_INT >= 30) {
-            mImpl = new Impl30(window);
+            mImpl = new Impl30(window, this);
         } else if (SDK_INT >= 26) {
             mImpl = new Impl26(window, view);
         } else if (SDK_INT >= 23) {
@@ -211,6 +212,49 @@
     }
 
     /**
+     * Lets the application control window inset animations in a frame-by-frame manner by
+     * modifying the position of the windows in the system causing insets directly using
+     * {@link WindowInsetsAnimationControllerCompat#setInsetsAndAlpha} in the controller provided
+     * by the given listener.
+     * <p>
+     * This method only works on API >= 30 since there is no way to control the window in the
+     * system on prior APIs.
+     *
+     * @param types              The {@link WindowInsetsCompat.Type}s the application has
+     *                           requested to control.
+     * @param durationMillis     Duration of animation in {@link TimeUnit#MILLISECONDS}, or -1 if
+     *                           the animation doesn't have a predetermined duration. This value
+     *                           will be passed to
+     *                           {@link WindowInsetsAnimationCompat#getDurationMillis()}
+     * @param interpolator       The interpolator used for this animation, or {@code null } if
+     *                           this animation doesn't follow an interpolation curve. This value
+     *                           will be passed to
+     *                           {@link WindowInsetsAnimationCompat#getInterpolator()} and used
+     *                           to calculate
+     *                           {@link WindowInsetsAnimationCompat#getInterpolatedFraction()}.
+     * @param cancellationSignal A cancellation signal that the caller can use to cancel the
+     *                           request to obtain control, or once they have control, to cancel
+     *                           the control.
+     * @param listener           The {@link WindowInsetsAnimationControlListener} that gets
+     *                           called when the windows are ready to be controlled, among other
+     *                           callbacks.
+     * @see WindowInsetsAnimationCompat#getFraction()
+     * @see WindowInsetsAnimationCompat#getInterpolatedFraction()
+     * @see WindowInsetsAnimationCompat#getInterpolator()
+     * @see WindowInsetsAnimationCompat#getDurationMillis()
+     */
+    public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+            @Nullable Interpolator interpolator,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull WindowInsetsAnimationControlListenerCompat listener) {
+        mImpl.controlWindowInsetsAnimation(types,
+                durationMillis,
+                interpolator,
+                cancellationSignal,
+                listener);
+    }
+
+    /**
      * Controls the behavior of system bars.
      *
      * @param behavior Determines how the bars behave when being hidden by the application.
@@ -231,6 +275,68 @@
         return mImpl.getSystemBarsBehavior();
     }
 
+    /**
+     * Adds a {@link WindowInsetsController.OnControllableInsetsChangedListener} to the window
+     * insets controller.
+     *
+     * @param listener The listener to add.
+     * @see WindowInsetsControllerCompat.OnControllableInsetsChangedListener
+     * @see #removeOnControllableInsetsChangedListener(
+     *WindowInsetsControllerCompat.OnControllableInsetsChangedListener)
+     */
+    public void addOnControllableInsetsChangedListener(
+            @NonNull WindowInsetsControllerCompat.OnControllableInsetsChangedListener listener) {
+        mImpl.addOnControllableInsetsChangedListener(listener);
+    }
+
+    /**
+     * Removes a {@link WindowInsetsController.OnControllableInsetsChangedListener} from the
+     * window insets controller.
+     *
+     * @param listener The listener to remove.
+     * @see WindowInsetsControllerCompat.OnControllableInsetsChangedListener
+     * @see #addOnControllableInsetsChangedListener(
+     *WindowInsetsControllerCompat.OnControllableInsetsChangedListener)
+     */
+    public void removeOnControllableInsetsChangedListener(
+            @NonNull WindowInsetsControllerCompat.OnControllableInsetsChangedListener
+                    listener) {
+        mImpl.removeOnControllableInsetsChangedListener(listener);
+    }
+
+    /**
+     * Listener to be notified when the set of controllable {@link WindowInsetsCompat.Type}
+     * controlled by a {@link WindowInsetsController} changes.
+     * <p>
+     * Once a {@link WindowInsetsCompat.Type} becomes controllable, the app will be able to
+     * control the window that is causing this type of insets by calling
+     * {@link #controlWindowInsetsAnimation}.
+     * <p>
+     * Note: When listening to cancellability of the {@link WindowInsets.Type#ime},
+     * {@link #controlWindowInsetsAnimation} may still fail in case the {@link InputMethodService}
+     * decides to cancel the show request. This could happen when there is a hardware keyboard
+     * attached.
+     *
+     * @see #addOnControllableInsetsChangedListener(
+     *WindowInsetsControllerCompat.OnControllableInsetsChangedListener)
+     * @see #removeOnControllableInsetsChangedListener(
+     *WindowInsetsControllerCompat.OnControllableInsetsChangedListener)
+     */
+    public interface OnControllableInsetsChangedListener {
+
+        /**
+         * Called when the set of controllable {@link WindowInsetsCompat.Type} changes.
+         *
+         * @param controller The controller for which the set of controllable
+         *                   {@link WindowInsetsCompat.Type}s
+         *                   are changing.
+         * @param typeMask   Bitwise behavior type-mask of the {@link WindowInsetsCompat.Type}s
+         *                   the controller is currently able to control.
+         */
+        void onControllableInsetsChanged(@NonNull WindowInsetsControllerCompat controller,
+                @InsetsType int typeMask);
+    }
+
     private static class Impl {
         Impl() {
             //privatex
@@ -242,6 +348,11 @@
         void hide(int types) {
         }
 
+        void controlWindowInsetsAnimation(int types, long durationMillis,
+                Interpolator interpolator, CancellationSignal cancellationSignal,
+                WindowInsetsAnimationControlListenerCompat listener) {
+        }
+
         void setSystemBarsBehavior(int behavior) {
         }
 
@@ -262,6 +373,15 @@
 
         public void setAppearanceLightNavigationBars(boolean isLight) {
         }
+
+        void addOnControllableInsetsChangedListener(
+                WindowInsetsControllerCompat.OnControllableInsetsChangedListener listener) {
+        }
+
+        void removeOnControllableInsetsChangedListener(
+                @NonNull WindowInsetsControllerCompat.OnControllableInsetsChangedListener
+                        listener) {
+        }
     }
 
     @RequiresApi(20)
@@ -384,6 +504,12 @@
         }
 
         @Override
+        void controlWindowInsetsAnimation(int types, long durationMillis,
+                Interpolator interpolator, CancellationSignal cancellationSignal,
+                WindowInsetsAnimationControlListenerCompat listener) {
+        }
+
+        @Override
         void setSystemBarsBehavior(int behavior) {
         }
 
@@ -391,6 +517,17 @@
         int getSystemBarsBehavior() {
             return 0;
         }
+
+        @Override
+        void addOnControllableInsetsChangedListener(
+                WindowInsetsControllerCompat.OnControllableInsetsChangedListener listener) {
+        }
+
+        @Override
+        void removeOnControllableInsetsChangedListener(
+                @NonNull WindowInsetsControllerCompat.OnControllableInsetsChangedListener
+                        listener) {
+        }
     }
 
     @RequiresApi(23)
@@ -446,14 +583,21 @@
     @RequiresApi(30)
     private static class Impl30 extends Impl {
 
-        private final WindowInsetsController mInsetsController;
+        final WindowInsetsControllerCompat mCompatController;
+        final WindowInsetsController mInsetsController;
+        private final SimpleArrayMap<
+                WindowInsetsControllerCompat.OnControllableInsetsChangedListener,
+                WindowInsetsController.OnControllableInsetsChangedListener>
+                mListeners = new SimpleArrayMap<>();
 
-        Impl30(Window window) {
-            mInsetsController = window.getInsetsController();
+        Impl30(@NonNull Window window, @NonNull WindowInsetsControllerCompat compatController) {
+            this(window.getInsetsController(), compatController);
         }
 
-        Impl30(WindowInsetsController insetsController) {
+        Impl30(@NonNull WindowInsetsController insetsController,
+                @NonNull WindowInsetsControllerCompat compatController) {
             mInsetsController = insetsController;
+            mCompatController = compatController;
         }
 
         @Override
@@ -504,6 +648,45 @@
             }
         }
 
+        @Override
+        void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+                @Nullable Interpolator interpolator,
+                @Nullable CancellationSignal cancellationSignal,
+                @NonNull final WindowInsetsAnimationControlListenerCompat listener) {
+
+            WindowInsetsAnimationControlListener fwListener =
+                    new WindowInsetsAnimationControlListener() {
+
+                        private WindowInsetsAnimationControllerCompat mCompatAnimController = null;
+
+                        @Override
+                        public void onReady(@NonNull WindowInsetsAnimationController controller,
+                                int types) {
+                            mCompatAnimController =
+                                    new WindowInsetsAnimationControllerCompat(controller);
+                            listener.onReady(mCompatAnimController, types);
+                        }
+
+                        @Override
+                        public void onFinished(
+                                @NonNull WindowInsetsAnimationController controller) {
+                            listener.onFinished(mCompatAnimController);
+                        }
+
+                        @Override
+                        public void onCancelled(
+                                @Nullable WindowInsetsAnimationController controller) {
+                            listener.onCancelled(controller == null ? null : mCompatAnimController);
+                        }
+                    };
+
+            mInsetsController.controlWindowInsetsAnimation(types,
+                    durationMillis,
+                    interpolator,
+                    cancellationSignal,
+                    fwListener);
+        }
+
         /**
          * Controls the behavior of system bars.
          *
@@ -526,5 +709,43 @@
         int getSystemBarsBehavior() {
             return mInsetsController.getSystemBarsBehavior();
         }
+
+        @Override
+        void addOnControllableInsetsChangedListener(
+                @NonNull final WindowInsetsControllerCompat.OnControllableInsetsChangedListener
+                        listener) {
+
+            if (mListeners.containsKey(listener)) {
+                // The listener has already been added.
+                return;
+            }
+            WindowInsetsController.OnControllableInsetsChangedListener
+                    fwListener =
+                    new WindowInsetsController.OnControllableInsetsChangedListener() {
+                        @Override
+                        public void onControllableInsetsChanged(
+                                @NonNull WindowInsetsController controller,
+                                int typeMask) {
+
+                            if (mInsetsController == controller) {
+                                listener.onControllableInsetsChanged(
+                                        mCompatController, typeMask);
+                            }
+                        }
+                    };
+            mListeners.put(listener, fwListener);
+            mInsetsController.addOnControllableInsetsChangedListener(fwListener);
+        }
+
+        @Override
+        void removeOnControllableInsetsChangedListener(
+                @NonNull WindowInsetsControllerCompat.OnControllableInsetsChangedListener
+                        listener) {
+            WindowInsetsController.OnControllableInsetsChangedListener
+                    fwListener = mListeners.remove(listener);
+            if (fwListener != null) {
+                mInsetsController.removeOnControllableInsetsChangedListener(fwListener);
+            }
+        }
     }
 }
diff --git a/core/core/src/main/java/androidx/core/widget/RichContentReceiverCompat.java b/core/core/src/main/java/androidx/core/widget/RichContentReceiverCompat.java
deleted file mode 100644
index 2a66954..0000000
--- a/core/core/src/main/java/androidx/core/widget/RichContentReceiverCompat.java
+++ /dev/null
@@ -1,223 +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.core.widget;
-
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.core.view.inputmethod.EditorInfoCompat;
-import androidx.core.view.inputmethod.InputConnectionCompat;
-import androidx.core.view.inputmethod.InputContentInfoCompat;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Set;
-
-/**
- * Callback for apps to implement handling for insertion of rich content. "Rich content" here refers
- * to both text and non-text content: plain text, styled text, HTML, images, videos, audio files,
- * etc.
- *
- * <p>This callback can be attached to different types of UI components. For editable
- * {@link android.widget.TextView} components, implementations should typically extend from
- * {@link TextViewRichContentReceiverCompat}.
- *
- * <p>Example implementation:<br>
- * <pre class="prettyprint">
- *   public class MyRichContentReceiver extends TextViewRichContentReceiverCompat {
- *
- *       private static final Set&lt;String&gt; SUPPORTED_MIME_TYPES = Collections.unmodifiableSet(
- *           Set.of("text/*", "image/gif", "image/png", "image/jpg"));
- *
- *       &#64;NonNull
- *       &#64;Override
- *       public Set&lt;String&gt; getSupportedMimeTypes() {
- *           return SUPPORTED_MIME_TYPES;
- *       }
- *
- *       &#64;Override
- *       public boolean onReceive(@NonNull TextView textView, @NonNull ClipData clip,
- *               int source, int flags) {
- *         if (clip.getDescription().hasMimeType("image/*")) {
- *             return receiveImage(textView, clip);
- *         }
- *         return super.onReceive(textView, clip, source);
- *       }
- *
- *       private boolean receiveImage(@NonNull TextView textView, @NonNull ClipData clip) {
- *           // ... app-specific logic to handle the content URI in the clip ...
- *       }
- *   }
- * </pre>
- *
- * @param <T> The type of {@link View} with which this receiver can be associated.
- */
-public abstract class RichContentReceiverCompat<T extends View> {
-    private static final String TAG = "RichContentReceiver";
-
-    /**
-     * Specifies the UI through which content is being inserted.
-     */
-    @IntDef(value = {SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD})
-    @Retention(RetentionPolicy.SOURCE)
-    @interface Source {}
-
-    /**
-     * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
-     * "Paste as plain text" action in the insertion/selection menu).
-     */
-    public static final int SOURCE_CLIPBOARD = 0;
-
-    /**
-     * Specifies that the operation was triggered from the soft keyboard (also known as input method
-     * editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard for more
-     * info.
-     */
-    public static final int SOURCE_INPUT_METHOD = 1;
-
-    /**
-     * Flags to configure the insertion behavior.
-     */
-    @IntDef(flag = true, value = {FLAG_CONVERT_TO_PLAIN_TEXT})
-    @Retention(RetentionPolicy.SOURCE)
-    @interface Flags {}
-
-    /**
-     * Flag for {@link #onReceive} requesting that the content should be converted to plain text
-     * prior to inserting.
-     */
-    public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
-
-    /**
-     * Insert the given clip.
-     *
-     * <p>For a UI component where this callback is set, this function will be invoked in the
-     * following scenarios:
-     * <ol>
-     *     <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the
-     *     insertion/selection menu)
-     *     <li>Content insertion from the keyboard ({@link InputConnection#commitContent})
-     * </ol>
-     *
-     * <p>For text, if the view has a selection, the selection should be overwritten by the
-     * clip; if there's no selection, this method should insert the content at the current
-     * cursor position.
-     *
-     * <p>For rich content (e.g. an image), this function may insert the content inline, or it may
-     * add the content as an attachment (could potentially go into a completely separate view).
-     *
-     * <p>This function may be invoked with a clip whose MIME type is not in the list of supported
-     * types returned by {@link #getSupportedMimeTypes()}. This provides the opportunity to
-     * implement custom fallback logic if desired.
-     *
-     * @param view   The view where the content insertion was requested.
-     * @param clip   The clip to insert.
-     * @param source The trigger of the operation.
-     * @param flags  Optional flags to configure the insertion behavior. Use 0 for default
-     *               behavior. See {@code FLAG_} constants on this class for other options.
-     * @return Returns true if the clip was inserted.
-     */
-    public abstract boolean onReceive(@NonNull T view, @NonNull ClipData clip, @Source int source,
-            @Flags int flags);
-
-    /**
-     * Returns the MIME types that can be handled by this callback.
-     *
-     * <p>Different platform features (e.g. pasting from the clipboard, inserting stickers from the
-     * keyboard, etc) may use this function to conditionally alter their behavior. For example, the
-     * keyboard may choose to hide its UI for inserting GIFs if the input field that has focus has
-     * a {@link RichContentReceiverCompat} set and the MIME types returned from this function
-     * don't include "image/gif".
-     *
-     * @return An immutable set with the MIME types supported by this callback. The returned
-     * MIME types may contain wildcards such as "text/*", "image/*", etc.
-     */
-    @NonNull
-    public abstract Set<String> getSupportedMimeTypes();
-
-    /**
-     * Returns true if the MIME type of the given clip is {@link #getSupportedMimeTypes() supported}
-     * by this receiver.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public final boolean supports(@NonNull ClipDescription description) {
-        for (String supportedMimeType : getSupportedMimeTypes()) {
-            if (description.hasMimeType(supportedMimeType)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Populates {@code outAttrs.contentMimeTypes} with the supported MIME types of this receiver.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public final void populateEditorInfoContentMimeTypes(@Nullable InputConnection ic,
-            @Nullable EditorInfo outAttrs) {
-        if (ic == null || outAttrs == null) {
-            return;
-        }
-        String[] mimeTypes = getSupportedMimeTypes().toArray(new String[0]);
-        EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes);
-    }
-
-    /**
-     * Creates an {@link InputConnectionCompat.OnCommitContentListener} that uses this receiver
-     * to insert content. The object returned by this function should be passed to
-     * {@link InputConnectionCompat#createWrapper} when creating the {@link InputConnection} in
-     * {@link View#onCreateInputConnection}.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    @NonNull
-    public final InputConnectionCompat.OnCommitContentListener buildOnCommitContentListener(
-            @NonNull final T view) {
-        return new InputConnectionCompat.OnCommitContentListener() {
-            @Override
-            public boolean onCommitContent(InputContentInfoCompat content, int flags,
-                    Bundle opts) {
-                ClipDescription description = content.getDescription();
-                if ((flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
-                    try {
-                        content.requestPermission();
-                    } catch (Exception e) {
-                        Log.w(TAG, "Can't insert from IME; requestPermission() failed: " + e);
-                        return false; // Can't insert the content if we don't have permission
-                    }
-                }
-                ClipData clip = new ClipData(description,
-                        new ClipData.Item(content.getContentUri()));
-                return onReceive(view, clip, SOURCE_INPUT_METHOD, 0);
-            }
-        };
-    }
-}
diff --git a/core/core/src/main/java/androidx/core/widget/TextViewOnReceiveContentListener.java b/core/core/src/main/java/androidx/core/widget/TextViewOnReceiveContentListener.java
new file mode 100644
index 0000000..93e3eb1
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/widget/TextViewOnReceiveContentListener.java
@@ -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.core.widget;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.core.view.ContentInfoCompat.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static androidx.core.view.ContentInfoCompat.SOURCE_DRAG_AND_DROP;
+import static androidx.core.view.ContentInfoCompat.SOURCE_INPUT_METHOD;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.os.Build;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.core.view.ContentInfoCompat;
+import androidx.core.view.ContentInfoCompat.Flags;
+import androidx.core.view.ContentInfoCompat.Source;
+import androidx.core.view.OnReceiveContentListener;
+
+/**
+ * Default implementation inserting content into editable {@link TextView} components. This class
+ * handles insertion of text (plain text, styled text, HTML, etc) but not images or other content.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP_PREFIX)
+public final class TextViewOnReceiveContentListener implements OnReceiveContentListener {
+    private static final String LOG_TAG = "ReceiveContent";
+
+    @Nullable
+    @Override
+    public ContentInfoCompat onReceiveContent(@NonNull View view,
+            @NonNull ContentInfoCompat payload) {
+        if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
+            Log.d(LOG_TAG, "onReceive: " + payload);
+        }
+        final @Source int source = payload.getSource();
+        if (source == SOURCE_INPUT_METHOD) {
+            // InputConnection.commitContent() should only be used for non-text input which is not
+            // supported by the default implementation.
+            return payload;
+        }
+        if (source == SOURCE_DRAG_AND_DROP) {
+            onReceiveForDragAndDrop((TextView) view, payload);
+            return null;
+        }
+
+        // The code here follows the platform logic in TextView:
+        // https://cs.android.com/android/_/android/platform/frameworks/base/+/9fefb65aa9e7beae9ca8306b925b9fbfaeffecc9:core/java/android/widget/TextView.java;l=12644
+        // In particular, multiple items within the given ClipData will trigger separate calls to
+        // replace/insert. This is to preserve the platform behavior with respect to TextWatcher
+        // notifications fired from SpannableStringBuilder when replace/insert is called.
+        final ClipData clip = payload.getClip();
+        final @Flags int flags = payload.getFlags();
+        final TextView textView = (TextView) view;
+        final Editable editable = (Editable) textView.getText();
+        final Context context = textView.getContext();
+        boolean didFirst = false;
+        for (int i = 0; i < clip.getItemCount(); i++) {
+            CharSequence itemText = coerceToText(context, clip.getItemAt(i), flags);
+            if (itemText != null) {
+                if (!didFirst) {
+                    replaceSelection(editable, itemText);
+                    didFirst = true;
+                } else {
+                    editable.insert(Selection.getSelectionEnd(editable), "\n");
+                    editable.insert(Selection.getSelectionEnd(editable), itemText);
+                }
+            }
+        }
+        return null;
+    }
+
+    private static void onReceiveForDragAndDrop(@NonNull TextView view,
+            @NonNull ContentInfoCompat payload) {
+        final CharSequence text = coerceToText(payload.getClip(), view.getContext(),
+                payload.getFlags());
+        replaceSelection((Editable) view.getText(), text);
+    }
+
+    @NonNull
+    private static CharSequence coerceToText(@NonNull ClipData clip, @NonNull Context context,
+            @Flags int flags) {
+        SpannableStringBuilder ssb = new SpannableStringBuilder();
+        for (int i = 0; i < clip.getItemCount(); i++) {
+            CharSequence itemText = coerceToText(context, clip.getItemAt(i), flags);
+            if (itemText != null) {
+                ssb.append(itemText);
+            }
+        }
+        return ssb;
+    }
+
+    private static CharSequence coerceToText(@NonNull Context context, @NonNull ClipData.Item item,
+            @Flags int flags) {
+        if (Build.VERSION.SDK_INT >= 16) {
+            return Api16Impl.coerce(context, item, flags);
+        } else {
+            return ApiImpl.coerce(context, item, flags);
+        }
+    }
+
+    private static void replaceSelection(@NonNull Editable editable,
+            @NonNull CharSequence replacement) {
+        final int selStart = Selection.getSelectionStart(editable);
+        final int selEnd = Selection.getSelectionEnd(editable);
+        final int start = Math.max(0, Math.min(selStart, selEnd));
+        final int end = Math.max(0, Math.max(selStart, selEnd));
+        Selection.setSelection(editable, end);
+        editable.replace(start, end, replacement);
+    }
+
+    @RequiresApi(16) // For ClipData.Item.coerceToStyledText()
+    private static final class Api16Impl {
+        private Api16Impl() {}
+
+        static CharSequence coerce(@NonNull Context context, @NonNull ClipData.Item item,
+                @Flags int flags) {
+            if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
+                CharSequence text = item.coerceToText(context);
+                return (text instanceof Spanned) ? text.toString() : text;
+            } else {
+                return item.coerceToStyledText(context);
+            }
+        }
+    }
+
+    private static final class ApiImpl {
+        private ApiImpl() {}
+
+        static CharSequence coerce(@NonNull Context context, @NonNull ClipData.Item item,
+                @Flags int flags) {
+            CharSequence text = item.coerceToText(context);
+            if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0 && text instanceof Spanned) {
+                text = text.toString();
+            }
+            return text;
+        }
+    }
+}
diff --git a/core/core/src/main/java/androidx/core/widget/TextViewRichContentReceiverCompat.java b/core/core/src/main/java/androidx/core/widget/TextViewRichContentReceiverCompat.java
deleted file mode 100644
index 31c51b1..0000000
--- a/core/core/src/main/java/androidx/core/widget/TextViewRichContentReceiverCompat.java
+++ /dev/null
@@ -1,104 +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.core.widget;
-
-import android.content.ClipData;
-import android.content.Context;
-import android.os.Build;
-import android.text.Editable;
-import android.text.Selection;
-import android.text.Spanned;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * Base implementation of {@link RichContentReceiverCompat} for editable {@link TextView}
- * components.
- *
- * <p>This class handles insertion of text (plain text, styled text, HTML, etc) but not images or
- * other rich content. It should be used as a base class when implementing a custom
- * {@link RichContentReceiverCompat}, to provide consistent behavior for insertion of text while
- * implementing custom behavior for insertion of other content (images, etc).
- *
- * <p>See {@link RichContentReceiverCompat} for an example of how to implement a custom receiver.
- */
-public abstract class TextViewRichContentReceiverCompat extends
-        RichContentReceiverCompat<TextView> {
-
-    private static final Set<String> MIME_TYPES_ALL_TEXT = Collections.singleton("text/*");
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @NonNull
-    public Set<String> getSupportedMimeTypes() {
-        return MIME_TYPES_ALL_TEXT;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean onReceive(@NonNull TextView textView, @NonNull ClipData clip,
-            @Source int source, @Flags int flags) {
-        if (source == SOURCE_INPUT_METHOD && !supports(clip.getDescription())) {
-            return false;
-        }
-
-        // The code here follows the platform logic in TextView:
-        // https://cs.android.com/android/_/android/platform/frameworks/base/+/9fefb65aa9e7beae9ca8306b925b9fbfaeffecc9:core/java/android/widget/TextView.java;l=12644
-        // In particular, multiple items within the given ClipData will trigger separate calls to
-        // replace/insert. This is to preserve the platform behavior with respect to TextWatcher
-        // notifications fired from SpannableStringBuilder when replace/insert is called.
-        final Editable editable = (Editable) textView.getText();
-        final Context context = textView.getContext();
-        boolean didFirst = false;
-        for (int i = 0; i < clip.getItemCount(); i++) {
-            CharSequence paste;
-            if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
-                paste = clip.getItemAt(i).coerceToText(context);
-                paste = (paste instanceof Spanned) ? paste.toString() : paste;
-            } else {
-                if (Build.VERSION.SDK_INT >= 16) {
-                    paste = clip.getItemAt(i).coerceToStyledText(context);
-                } else {
-                    paste = clip.getItemAt(i).coerceToText(context);
-                }
-            }
-            if (paste != null) {
-                if (!didFirst) {
-                    final int selStart = Selection.getSelectionStart(editable);
-                    final int selEnd = Selection.getSelectionEnd(editable);
-                    final int start = Math.max(0, Math.min(selStart, selEnd));
-                    final int end = Math.max(0, Math.max(selStart, selEnd));
-                    Selection.setSelection(editable, end);
-                    editable.replace(start, end, paste);
-                    didFirst = true;
-                } else {
-                    editable.insert(Selection.getSelectionEnd(editable), "\n");
-                    editable.insert(Selection.getSelectionEnd(editable), paste);
-                }
-            }
-        }
-        return didFirst;
-    }
-}
diff --git a/core/core/src/main/res/values/ids.xml b/core/core/src/main/res/values/ids.xml
index 5147433..344c64c 100644
--- a/core/core/src/main/res/values/ids.xml
+++ b/core/core/src/main/res/values/ids.xml
@@ -30,6 +30,10 @@
     <item name="tag_accessibility_clickable_spans" type="id"/>
     <item name="tag_accessibility_actions" type="id"/>
     <item name="tag_state_description" type="id"/>
+    <item name="tag_on_receive_content_listener" type="id"/>
+    <item name="tag_on_receive_content_mime_types" type="id"/>
+    <item name="tag_window_insets_animation_callback" type="id"/>
+    <item name="tag_on_apply_window_listener" type="id"/>
     <item name="accessibility_custom_action_0" type="id"/>
     <item name="accessibility_custom_action_1" type="id"/>
     <item name="accessibility_custom_action_2" type="id"/>
diff --git a/customview/customview/api/current.txt b/customview/customview/api/current.txt
index 2834ec2..b7566cb 100644
--- a/customview/customview/api/current.txt
+++ b/customview/customview/api/current.txt
@@ -38,6 +38,7 @@
     method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
     method public final boolean requestKeyboardFocusForVirtualView(int);
     method public final boolean sendEventForVirtualView(int, int);
+    method public final void setBoundsInScreenFromBoundsInParent(androidx.core.view.accessibility.AccessibilityNodeInfoCompat, android.graphics.Rect);
     field public static final int HOST_ID = -1; // 0xffffffff
     field public static final int INVALID_ID = -2147483648; // 0x80000000
   }
diff --git a/customview/customview/api/public_plus_experimental_current.txt b/customview/customview/api/public_plus_experimental_current.txt
index 2834ec2..b7566cb 100644
--- a/customview/customview/api/public_plus_experimental_current.txt
+++ b/customview/customview/api/public_plus_experimental_current.txt
@@ -38,6 +38,7 @@
     method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
     method public final boolean requestKeyboardFocusForVirtualView(int);
     method public final boolean sendEventForVirtualView(int, int);
+    method public final void setBoundsInScreenFromBoundsInParent(androidx.core.view.accessibility.AccessibilityNodeInfoCompat, android.graphics.Rect);
     field public static final int HOST_ID = -1; // 0xffffffff
     field public static final int INVALID_ID = -2147483648; // 0x80000000
   }
diff --git a/customview/customview/api/restricted_current.txt b/customview/customview/api/restricted_current.txt
index 2834ec2..b7566cb 100644
--- a/customview/customview/api/restricted_current.txt
+++ b/customview/customview/api/restricted_current.txt
@@ -38,6 +38,7 @@
     method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
     method public final boolean requestKeyboardFocusForVirtualView(int);
     method public final boolean sendEventForVirtualView(int, int);
+    method public final void setBoundsInScreenFromBoundsInParent(androidx.core.view.accessibility.AccessibilityNodeInfoCompat, android.graphics.Rect);
     field public static final int HOST_ID = -1; // 0xffffffff
     field public static final int INVALID_ID = -2147483648; // 0x80000000
   }
diff --git a/customview/customview/src/androidTest/java/androidx/customview/widget/ExploreByTouchHelperTest.java b/customview/customview/src/androidTest/java/androidx/customview/widget/ExploreByTouchHelperTest.java
index 1e11425..ebaa94a 100644
--- a/customview/customview/src/androidTest/java/androidx/customview/widget/ExploreByTouchHelperTest.java
+++ b/customview/customview/src/androidTest/java/androidx/customview/widget/ExploreByTouchHelperTest.java
@@ -17,11 +17,9 @@
 package androidx.customview.widget;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.os.Bundle;
 import android.view.KeyEvent;
 import android.view.View;
@@ -61,70 +59,94 @@
 
     @Test
     @UiThreadTest
-    public void testBoundsInScreen() {
-        final ExploreByTouchHelper helper = new ParentBoundsHelper(mHost);
+    public void testAssignBoundsInParent() {
+        final TwoNestedViewHelper boundsInParentOnlyHelper = new ParentBoundsHelper(mHost);
+        testBounds(boundsInParentOnlyHelper);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testAssignBoundsInScreen() {
+        final TwoNestedViewHelper boundsInScreenOnlyHelper = new ScreenBoundsHelper(mHost);
+        testBounds(boundsInScreenOnlyHelper);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testAssignBoundsInScreenAndParent() {
+        final TwoNestedViewHelper boundsInScreenAndParentHelper =
+                new ParentAndScreenBoundsHelper(mHost);
+        testBounds(boundsInScreenAndParentHelper);
+    }
+
+    private void testBounds(TwoNestedViewHelper helper) {
         ViewCompat.setAccessibilityDelegate(mHost, helper);
-
-        final AccessibilityNodeInfoCompat node =
-                helper.getAccessibilityNodeProvider(mHost).createAccessibilityNodeInfo(1);
-        assertNotNull(node);
-
-        final Rect hostBounds = new Rect();
-        mHost.getLocalVisibleRect(hostBounds);
-        assertFalse("Host has not been laid out", hostBounds.isEmpty());
-
-        final Rect nodeBoundsInParent = new Rect();
-        node.getBoundsInParent(nodeBoundsInParent);
-        assertEquals("Wrong bounds in parent", hostBounds, nodeBoundsInParent);
-
-        final Rect hostBoundsOnScreen = getBoundsOnScreen(mHost);
-        final Rect nodeBoundsInScreen = new Rect();
-        node.getBoundsInScreen(nodeBoundsInScreen);
-        assertEquals("Wrong bounds in screen", hostBoundsOnScreen, nodeBoundsInScreen);
-
-        final int scrollX = 100;
-        final int scrollY = 50;
-        mHost.scrollTo(scrollX, scrollY);
-
-        // Generate a node for the new position.
-        final AccessibilityNodeInfoCompat scrolledNode =
-                helper.getAccessibilityNodeProvider(mHost).createAccessibilityNodeInfo(1);
-        assertNotNull(scrolledNode);
-
-        // Bounds in parent should not be affected by visibility.
-        final Rect scrolledNodeBoundsInParent = new Rect();
-        scrolledNode.getBoundsInParent(scrolledNodeBoundsInParent);
-        assertEquals("Wrong bounds in parent after scrolling",
-                hostBounds, scrolledNodeBoundsInParent);
-
-        final Rect expectedBoundsInScreen = new Rect(hostBoundsOnScreen);
-        expectedBoundsInScreen.offset(-scrollX, -scrollY);
-        expectedBoundsInScreen.intersect(hostBoundsOnScreen);
-        scrolledNode.getBoundsInScreen(nodeBoundsInScreen);
-        assertEquals("Wrong bounds in screen after scrolling",
-                expectedBoundsInScreen, nodeBoundsInScreen);
-
+        testBounds(helper, 0);
+        testBounds(helper, 1);
+        mHost.scrollTo(100, 50);
+        testBounds(helper, 0);
+        testBounds(helper, 1);
+        mHost.scrollTo(0, 0);
         ViewCompat.setAccessibilityDelegate(mHost, null);
     }
+
+    private void testBounds(TwoNestedViewHelper helper, int virtualViewId) {
+        AccessibilityNodeInfoCompat node =
+                helper.getAccessibilityNodeProvider(mHost).createAccessibilityNodeInfo(
+                        virtualViewId);
+        assertNotNull(node);
+
+        VirtualItem item = helper.mVirtualItems[virtualViewId];
+        final Rect nodeBoundsInParent = new Rect();
+        node.getBoundsInParent(nodeBoundsInParent);
+        assertEquals("Wrong bounds in parent", item.mBoundsInParent, nodeBoundsInParent);
+
+        final Rect expectedNodeBoundsInScreen = getBoundsOnScreen(helper, virtualViewId,
+                item.mParentId);
+        final Rect nodeBoundsInScreen = new Rect();
+        node.getBoundsInScreen(nodeBoundsInScreen);
+        assertEquals("Wrong bounds in screen", expectedNodeBoundsInScreen,
+                nodeBoundsInScreen);
+
+        node.recycle();
+    }
+
+    private Rect getBoundsOnScreen(TwoNestedViewHelper helper, int virtualViewId,
+            int virtualParentId) {
+        final Rect boundsOnScreen = new Rect();
+        boundsOnScreen.set(helper.mVirtualItems[virtualViewId].mBoundsInParent);
+        if (virtualParentId != ExploreByTouchHelper.HOST_ID) {
+            boundsOnScreen.offset(helper.mVirtualItems[virtualParentId].mBoundsInParent.left,
+                    helper.mVirtualItems[virtualParentId].mBoundsInParent.top);
+        }
+        final int[] tempLocation = new int[2];
+        mHost.getLocationOnScreen(tempLocation);
+        boundsOnScreen.offset(tempLocation[0] - mHost.getScrollX(),
+                tempLocation[1] - mHost.getScrollY());
+        final Rect tempVisibleRect = new Rect();
+        mHost.getLocalVisibleRect(tempVisibleRect);
+        tempVisibleRect.offset(tempLocation[0] - mHost.getScrollX(),
+                tempLocation[1] - mHost.getScrollY());
+        boundsOnScreen.intersect(tempVisibleRect);
+        return boundsOnScreen;
+    }
+
     @Test
     @UiThreadTest
     public void testMoveFocusToNextVirtualId() {
-        final ExploreByTouchHelper helper = new FocusTouchHelper(mHost);
-
+        final ExploreByTouchHelper helper = new TwoNestedViewHelper(mHost);
         ViewCompat.setAccessibilityDelegate(mHost, helper);
 
+        boolean moveFocusToId0 = helper.dispatchKeyEvent(
+                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB));
+        assertEquals(0, helper.getKeyboardFocusedVirtualViewId());
+        assertEquals(true, moveFocusToId0);
+
         boolean moveFocusToId1 = helper.dispatchKeyEvent(
                 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB));
         assertEquals(1, helper.getKeyboardFocusedVirtualViewId());
         assertEquals(true, moveFocusToId1);
 
-        // moveFocus should move focus to the node with id 5
-        boolean moveFocusToId5 = helper.dispatchKeyEvent(
-                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB));
-        assertEquals(5, helper.getKeyboardFocusedVirtualViewId());
-        assertEquals(true, moveFocusToId5);
-
-        // moveFocus should not return true if the node has id INVALID_ID.
         boolean moveFocusToInvalidId = helper.dispatchKeyEvent(
                 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB));
         assertEquals(ExploreByTouchHelper.INVALID_ID, helper.getKeyboardFocusedVirtualViewId());
@@ -133,95 +155,151 @@
         ViewCompat.setAccessibilityDelegate(mHost, null);
     }
 
-    private static Rect getBoundsOnScreen(View v) {
-        final int[] tempLocation = new int[2];
-        final Rect hostBoundsOnScreen = new Rect(0, 0, v.getWidth(), v.getHeight());
-        v.getLocationOnScreen(tempLocation);
-        hostBoundsOnScreen.offset(tempLocation[0], tempLocation[1]);
-        return hostBoundsOnScreen;
+    @Test
+    @UiThreadTest
+    public void testMoveFocusDirection() {
+        final ExploreByTouchHelper helper = new TwoNestedViewHelper(mHost);
+        ViewCompat.setAccessibilityDelegate(mHost, helper);
+        helper.requestKeyboardFocusForVirtualView(0);
+
+        boolean moveFocusUp = helper.dispatchKeyEvent(
+                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP));
+        assertEquals(ExploreByTouchHelper.INVALID_ID, helper.getKeyboardFocusedVirtualViewId());
+        assertEquals(false, moveFocusUp);
+
+        boolean moveFocusDown = helper.dispatchKeyEvent(
+                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN));
+        assertEquals(0, helper.getKeyboardFocusedVirtualViewId());
+        assertEquals(true, moveFocusDown);
+
+        ViewCompat.setAccessibilityDelegate(mHost, null);
     }
 
     /**
-     * An extension of ExploreByTouchHelper that contains a single virtual view
-     * whose bounds match the host view.
+     * An extension of ExploreByTouchHelper that contains 2 nested virtual view
+     * and specify {@link AccessibilityNodeInfoCompat#setBoundsInParent}.
      */
-    private static class ParentBoundsHelper extends ExploreByTouchHelper {
-        private final View mHost;
+    private static class ParentBoundsHelper extends TwoNestedViewHelper {
 
         ParentBoundsHelper(View host) {
             super(host);
-
-            mHost = host;
         }
 
         @Override
-        protected int getVirtualViewAt(float x, float y) {
-            return 1;
-        }
-
-        @Override
-        protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
-            virtualViewIds.add(1);
-        }
-
-        @Override
-        protected void onPopulateNodeForVirtualView(int virtualViewId,
-                @NonNull AccessibilityNodeInfoCompat node) {
-            if (virtualViewId == 1) {
-                node.setContentDescription("test");
-
-                final Rect hostBounds = new Rect(0, 0, mHost.getWidth(), mHost.getHeight());
-                node.setBoundsInParent(hostBounds);
-            }
-        }
-
-        @Override
-        protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
-                Bundle arguments) {
-            return false;
+        protected void onPopulateNodeForVirtualView(
+                int virtualViewId, @NonNull AccessibilityNodeInfoCompat node) {
+            populateNodeForVirtualView(/* setBoundsFromParent= */true,
+                    /* setBoundsFromScreen= */ false, virtualViewId, node);
         }
     }
 
     /**
-     * An extension of ExploreByTouchHelper that contains two virtual views to test moving focus.
+     * An extension of ExploreByTouchHelper that contains 2 nested virtual view
+     * and specify {@link AccessibilityNodeInfoCompat#setBoundsInScreen} by calling
+     * {@link ExploreByTouchHelper#setBoundsInScreenFromBoundsInParent}.
      */
-    private static class FocusTouchHelper extends ExploreByTouchHelper {
-        private final View mHost;
+    private static class ScreenBoundsHelper extends TwoNestedViewHelper {
 
-        FocusTouchHelper(View host) {
+        ScreenBoundsHelper(View host) {
+            super(host);
+        }
+
+        @Override
+        protected void onPopulateNodeForVirtualView(
+                int virtualViewId, @NonNull AccessibilityNodeInfoCompat node) {
+            populateNodeForVirtualView(/* setBoundsFromParent= */false,
+                    /* setBoundsFromScreen= */ true, virtualViewId, node);
+        }
+    }
+
+    /**
+     * An extension of ExploreByTouchHelper that contains 2 nested virtual view
+     * and specify {@link AccessibilityNodeInfoCompat#setBoundsInParent}
+     * and {@link AccessibilityNodeInfoCompat#setBoundsInScreen} by calling
+     * {@link ExploreByTouchHelper#setBoundsInScreenFromBoundsInParent}.
+     */
+    private static class ParentAndScreenBoundsHelper extends TwoNestedViewHelper {
+
+        ParentAndScreenBoundsHelper(View host) {
+            super(host);
+        }
+
+        @Override
+        protected void onPopulateNodeForVirtualView(
+                int virtualViewId, @NonNull AccessibilityNodeInfoCompat node) {
+            populateNodeForVirtualView(/* setBoundsFromParent= */true,
+                    /* setBoundsFromScreen= */ true, virtualViewId, node);
+        }
+    }
+
+    private static class VirtualItem {
+        private int mParentId;
+        private Rect mBoundsInParent;
+        private String mText;
+
+        VirtualItem(int parentId, String text, Rect boundsInParent) {
+            this.mParentId = parentId;
+            this.mBoundsInParent = boundsInParent;
+            this.mText = text;
+        }
+    }
+
+    /**
+     * An extension of ExploreByTouchHelper that contains 2 nested virtual views.
+     * Host view contains 1 child "bottom" and "bottom" contains one child
+     * "nested-bottom-right".
+     */
+    private static class TwoNestedViewHelper extends ExploreByTouchHelper {
+        private final View mHost;
+        protected VirtualItem[] mVirtualItems = new VirtualItem[2];
+
+        TwoNestedViewHelper(View host) {
             super(host);
             mHost = host;
+            mVirtualItems[0] = new VirtualItem(ExploreByTouchHelper.HOST_ID, "bottom",
+                    new Rect(0, mHost.getHeight() / 2,
+                            mHost.getWidth(), mHost.getHeight()));
+            mVirtualItems[1] = new VirtualItem(0, "nested-bottom-right",
+                    new Rect(mHost.getWidth() / 2, 0,
+                            mHost.getWidth(), mHost.getHeight() / 2));
         }
 
         @Override
         protected int getVirtualViewAt(float x, float y) {
-            RectF topHalf = new RectF();
-            topHalf.set(0, 0, mHost.getWidth(), mHost.getHeight() / 2);
-            if (topHalf.contains(x, y)) {
+            if (x < mHost.getWidth() / 2 && y > mHost.getHeight() / 2) {
+                return 0;
+            } else if (x > mHost.getWidth() / 2 && y > mHost.getHeight() / 2) {
                 return 1;
             }
-            return 5;
+            return -1;
         }
 
         @Override
         protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
+            virtualViewIds.add(0);
             virtualViewIds.add(1);
-            virtualViewIds.add(5);
         }
 
         @Override
-        protected void onPopulateNodeForVirtualView(int virtualViewId,
+        protected void onPopulateNodeForVirtualView(
+                int virtualViewId, @NonNull AccessibilityNodeInfoCompat node) {
+            populateNodeForVirtualView(/* setBoundsFromParent= */false,
+                    /* setBoundsFromScreen= */ true, virtualViewId, node);
+        }
+
+        protected void populateNodeForVirtualView(boolean setBoundsFromParent,
+                boolean setBoundsFromScreen, int virtualViewId,
                 @NonNull AccessibilityNodeInfoCompat node) {
-            if (virtualViewId == 1) {
-                node.setContentDescription("test 1");
-                final Rect hostBounds = new Rect(0, 0, mHost.getWidth(), mHost.getHeight() / 2);
-                node.setBoundsInParent(hostBounds);
-            }
-            if (virtualViewId == 5) {
-                node.setContentDescription("test 5");
-                final Rect hostBounds =
-                        new Rect(0, mHost.getHeight() / 2, mHost.getWidth(), mHost.getHeight());
-                node.setBoundsInParent(hostBounds);
+            if (virtualViewId <= mVirtualItems.length) {
+                int index = virtualViewId;
+                node.setContentDescription(mVirtualItems[index].mText);
+                node.setParent(mHost, mVirtualItems[index].mParentId);
+                if (setBoundsFromParent) {
+                    node.setBoundsInParent(mVirtualItems[index].mBoundsInParent);
+                }
+                if (setBoundsFromScreen) {
+                    setBoundsInScreenFromBoundsInParent(node, mVirtualItems[index].mBoundsInParent);
+                }
             }
         }
 
@@ -230,6 +308,5 @@
                 Bundle arguments) {
             return false;
         }
-
     }
 }
diff --git a/customview/customview/src/main/java/androidx/customview/widget/ExploreByTouchHelper.java b/customview/customview/src/main/java/androidx/customview/widget/ExploreByTouchHelper.java
index ae399dd..adc7915 100644
--- a/customview/customview/src/main/java/androidx/customview/widget/ExploreByTouchHelper.java
+++ b/customview/customview/src/main/java/androidx/customview/widget/ExploreByTouchHelper.java
@@ -97,7 +97,7 @@
     private static final String DEFAULT_CLASS_NAME = "android.view.View";
 
     /** Default bounds used to determine if the client didn't set any. */
-    private static final Rect INVALID_PARENT_BOUNDS = new Rect(
+    private static final Rect INVALID_BOUNDS = new Rect(
             Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
 
     // Temporary, reusable data structures.
@@ -324,9 +324,9 @@
      * @param virtualViewId the identifier of the virtual view
      * @param outBounds the rect to populate with virtual view bounds
      */
-    private void getBoundsInParent(int virtualViewId, Rect outBounds) {
+    private void getBoundsInScreen(int virtualViewId, Rect outBounds) {
         final AccessibilityNodeInfoCompat node = obtainAccessibilityNodeInfo(virtualViewId);
-        node.getBoundsInParent(outBounds);
+        node.getBoundsInScreen(outBounds);
     }
 
     /**
@@ -336,7 +336,7 @@
             new FocusStrategy.BoundsAdapter<AccessibilityNodeInfoCompat>() {
                 @Override
                 public void obtainBounds(AccessibilityNodeInfoCompat node, Rect outBounds) {
-                    node.getBoundsInParent(outBounds);
+                    node.getBoundsInScreen(outBounds);
                 }
             };
 
@@ -392,7 +392,7 @@
                 final Rect selectedRect = new Rect();
                 if (mKeyboardFocusedVirtualViewId != INVALID_ID) {
                     // Focus is moving from a virtual view within the host.
-                    getBoundsInParent(mKeyboardFocusedVirtualViewId, selectedRect);
+                    getBoundsInScreen(mKeyboardFocusedVirtualViewId, selectedRect);
                 } else if (previouslyFocusedRect != null) {
                     // Focus is moving from a real view outside the host.
                     selectedRect.set(previouslyFocusedRect);
@@ -772,16 +772,6 @@
      * <li>{@link AccessibilityNodeInfoCompat#setParent(View)}
      * <li>{@link AccessibilityNodeInfoCompat#setSource(View, int)}
      * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
-     * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
-     * </ul>
-     * <p>
-     * Uses the bounds of the parent view and the parent-relative bounding
-     * rectangle specified by
-     * {@link AccessibilityNodeInfoCompat#getBoundsInParent} to automatically
-     * update the following properties:
-     * <ul>
-     * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
-     * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent}
      * </ul>
      *
      * @param virtualViewId the virtual view id for item for which to construct
@@ -797,8 +787,8 @@
         node.setFocusable(true);
         node.setClassName(DEFAULT_CLASS_NAME);
 
-        node.setBoundsInParent(INVALID_PARENT_BOUNDS);
-        node.setBoundsInScreen(INVALID_PARENT_BOUNDS);
+        node.setBoundsInParent(INVALID_BOUNDS);
+        node.setBoundsInScreen(INVALID_BOUNDS);
         node.setParent(mHost);
 
         // Allow the client to populate the node.
@@ -811,8 +801,10 @@
         }
 
         node.getBoundsInParent(mTempParentRect);
-        if (mTempParentRect.equals(INVALID_PARENT_BOUNDS)) {
-            throw new RuntimeException("Callbacks must set parent bounds in "
+        node.getBoundsInScreen(mTempScreenRect);
+        if (mTempParentRect.equals(INVALID_BOUNDS) && mTempScreenRect.equals(
+                INVALID_BOUNDS)) {
+            throw new RuntimeException("Callbacks must set parent bounds or screen bounds in "
                     + "populateNodeForVirtualViewId()");
         }
 
@@ -850,32 +842,9 @@
 
         mHost.getLocationOnScreen(mTempGlobalRect);
 
-        // If not explicitly specified, calculate screen-relative bounds and
-        // offset for scroll position based on bounds in parent.
-        node.getBoundsInScreen(mTempScreenRect);
-        if (mTempScreenRect.equals(INVALID_PARENT_BOUNDS)) {
-            node.getBoundsInParent(mTempScreenRect);
-
-            // If there is a parent node, adjust bounds based on the parent node.
-            if (node.mParentVirtualDescendantId != HOST_ID) {
-                AccessibilityNodeInfoCompat parentNode = AccessibilityNodeInfoCompat.obtain();
-                // Walk up the node tree to adjust the screen rect.
-                for (int virtualDescendantId = node.mParentVirtualDescendantId;
-                        virtualDescendantId != HOST_ID;
-                        virtualDescendantId = parentNode.mParentVirtualDescendantId) {
-                    // Reset the values in the parent node we'll be using.
-                    parentNode.setParent(mHost, HOST_ID);
-                    parentNode.setBoundsInParent(INVALID_PARENT_BOUNDS);
-                    // Adjust the bounds for the parent node.
-                    onPopulateNodeForVirtualView(virtualDescendantId, parentNode);
-                    parentNode.getBoundsInParent(mTempParentRect);
-                    mTempScreenRect.offset(mTempParentRect.left, mTempParentRect.top);
-                }
-                parentNode.recycle();
-            }
-            // Adjust the rect for the host view's location.
-            mTempScreenRect.offset(mTempGlobalRect[0] - mHost.getScrollX(),
-                    mTempGlobalRect[1] - mHost.getScrollY());
+        if (mTempScreenRect.equals(INVALID_BOUNDS)) {
+            setBoundsInScreenFromBoundsInParent(node, mTempParentRect);
+            node.getBoundsInScreen(mTempScreenRect);
         }
 
         if (mHost.getLocalVisibleRect(mTempVisibleRect)) {
@@ -884,7 +853,6 @@
             final boolean intersects = mTempScreenRect.intersect(mTempVisibleRect);
             if (intersects) {
                 node.setBoundsInScreen(mTempScreenRect);
-
                 if (isVisibleToUser(mTempScreenRect)) {
                     node.setVisibleToUser(true);
                 }
@@ -924,15 +892,15 @@
 
     /**
      * Computes whether the specified {@link Rect} intersects with the visible
-     * portion of its parent {@link View}. Modifies {@code localRect} to contain
+     * portion of its parent {@link View}. Modifies {@code screenRect} to contain
      * only the visible portion.
      *
-     * @param localRect a rectangle in local (parent) coordinates
+     * @param screenRect a rectangle in screen coordinates
      * @return whether the specified {@link Rect} is visible on the screen
      */
-    private boolean isVisibleToUser(Rect localRect) {
+    private boolean isVisibleToUser(Rect screenRect) {
         // Missing or empty bounds mean this view is not visible.
-        if ((localRect == null) || localRect.isEmpty()) {
+        if ((screenRect == null) || screenRect.isEmpty()) {
             return false;
         }
 
@@ -1064,6 +1032,46 @@
     }
 
     /**
+     * Calculates and assigns screen-relative bounds based on bounds in parent. Instead
+     * of calling the deprecated {@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)}, it
+     * provides a convenient method to calculate and assign
+     * {@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)} based on {@code boundsInParent}.
+     *
+     * @param node The node to populate
+     * @param boundsInParent The node bounds in the viewParent's coordinates.
+     */
+    public final void setBoundsInScreenFromBoundsInParent(@NonNull AccessibilityNodeInfoCompat node,
+            @NonNull Rect boundsInParent) {
+        node.setBoundsInParent(boundsInParent);
+        Rect screenRect = new Rect();
+        screenRect.set(boundsInParent);
+
+        // If there is a parent node, adjust bounds based on the parent node.
+        if (node.mParentVirtualDescendantId != HOST_ID) {
+            AccessibilityNodeInfoCompat parentNode = AccessibilityNodeInfoCompat.obtain();
+            Rect tempParentRect = new Rect();
+            // Walk up the node tree to adjust the screen rect.
+            for (int virtualDescendantId = node.mParentVirtualDescendantId;
+                    virtualDescendantId != HOST_ID;
+                    virtualDescendantId = parentNode.mParentVirtualDescendantId) {
+                // Reset the values in the parent node we'll be using.
+                parentNode.setParent(mHost, HOST_ID);
+                parentNode.setBoundsInParent(INVALID_BOUNDS);
+                // Adjust the bounds for the parent node.
+                onPopulateNodeForVirtualView(virtualDescendantId, parentNode);
+                parentNode.getBoundsInParent(tempParentRect);
+                screenRect.offset(tempParentRect.left, tempParentRect.top);
+            }
+            parentNode.recycle();
+        }
+        // Adjust the rect for the host view's location.
+        mHost.getLocationOnScreen(mTempGlobalRect);
+        screenRect.offset(mTempGlobalRect[0] - mHost.getScrollX(),
+                mTempGlobalRect[1] - mHost.getScrollY());
+        node.setBoundsInScreen(screenRect);
+    }
+
+    /**
      * Provides a mapping between view-relative coordinates and logical
      * items.
      *
@@ -1144,8 +1152,10 @@
      * <li>event text, see
      * {@link AccessibilityNodeInfoCompat#setText(CharSequence)} or
      * {@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)}
-     * <li>bounds in parent coordinates, see
-     * {@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)}
+     * <li>bounds in screen coordinates, see
+     * {@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)} and
+     * {@link ExploreByTouchHelper#setBoundsInScreenFromBoundsInParent
+     * (AccessibilityNodeInfoCompat, Rect)}
      * </ul>
      * <p>
      * The helper class automatically populates the following fields with
@@ -1176,8 +1186,6 @@
      * {@link AccessibilityNodeInfoCompat#setAccessibilityFocused(boolean)}
      * <li>keyboard focus, computed based on internal helper state, see
      * {@link AccessibilityNodeInfoCompat#setFocused(boolean)}
-     * <li>bounds in screen coordinates, computed based on host view bounds,
-     * see {@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
      * </ul>
      * <p>
      * Additionally, the helper class automatically handles keyboard focus and
diff --git a/datastore/datastore-preferences-core/api/current.txt b/datastore/datastore-preferences-core/api/current.txt
index f5f3008..1b3e2d0 100644
--- a/datastore/datastore-preferences-core/api/current.txt
+++ b/datastore/datastore-preferences-core/api/current.txt
@@ -3,8 +3,14 @@
 
   public final class MutablePreferences extends androidx.datastore.preferences.core.Preferences {
     method public java.util.Map<androidx.datastore.preferences.core.Preferences.Key<?>,java.lang.Object> asMap();
+    method public void clear();
     method public operator <T> boolean contains(androidx.datastore.preferences.core.Preferences.Key<T> key);
     method public operator <T> T? get(androidx.datastore.preferences.core.Preferences.Key<T> key);
+    method public operator void minusAssign(androidx.datastore.preferences.core.Preferences.Key<?> key);
+    method public operator void plusAssign(androidx.datastore.preferences.core.Preferences prefs);
+    method public operator void plusAssign(androidx.datastore.preferences.core.Preferences.Pair<?> pair);
+    method public void putAll(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
+    method public <T> T! remove(androidx.datastore.preferences.core.Preferences.Key<T> key);
     method public operator <T> void set(androidx.datastore.preferences.core.Preferences.Key<T> key, T? value);
   }
 
@@ -20,32 +26,37 @@
     method public abstract java.util.Map<androidx.datastore.preferences.core.Preferences.Key<?>,java.lang.Object> asMap();
     method public abstract operator <T> boolean contains(androidx.datastore.preferences.core.Preferences.Key<T> key);
     method public abstract operator <T> T? get(androidx.datastore.preferences.core.Preferences.Key<T> key);
+    method public final androidx.datastore.preferences.core.MutablePreferences toMutablePreferences();
+    method public final androidx.datastore.preferences.core.Preferences toPreferences();
   }
 
   public static final class Preferences.Key<T> {
     method public String getName();
+    method public infix androidx.datastore.preferences.core.Preferences.Pair<T> to(T? value);
     property public final String name;
   }
 
   public static final class Preferences.Pair<T> {
   }
 
+  public final class PreferencesFactory {
+    method public static androidx.datastore.preferences.core.Preferences create(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
+    method public static androidx.datastore.preferences.core.Preferences createEmpty();
+    method public static androidx.datastore.preferences.core.MutablePreferences createMutable(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
+  }
+
+  public final class PreferencesKeys {
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Boolean> booleanKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Double> doubleKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Float> floatKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Integer> intKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Long> longKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.String> stringKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.util.Set<java.lang.String>> stringSetKey(String name);
+  }
+
   public final class PreferencesKt {
-    method public static void clear(androidx.datastore.preferences.core.MutablePreferences);
     method public static suspend Object? edit(androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>, kotlin.jvm.functions.Function2<? super androidx.datastore.preferences.core.MutablePreferences,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> transform, kotlin.coroutines.Continuation<? super androidx.datastore.preferences.core.Preferences> p);
-    method public static androidx.datastore.preferences.core.Preferences emptyPreferences();
-    method public static operator void minusAssign(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Key<?> key);
-    method public static androidx.datastore.preferences.core.MutablePreferences mutablePreferencesOf(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
-    method public static operator void plusAssign(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences prefs);
-    method public static operator void plusAssign(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Pair<?> pair);
-    method public static inline <reified T> androidx.datastore.preferences.core.Preferences.Key<T>! preferencesKey(String name);
-    method public static androidx.datastore.preferences.core.Preferences preferencesOf(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
-    method public static inline <reified T> androidx.datastore.preferences.core.Preferences.Key<java.util.Set<? extends T>>! preferencesSetKey(String name);
-    method public static void putAll(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
-    method public static <T> T! remove(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Key<T> key);
-    method public static infix <T> androidx.datastore.preferences.core.Preferences.Pair<T> to(androidx.datastore.preferences.core.Preferences.Key<T>, T? value);
-    method public static androidx.datastore.preferences.core.MutablePreferences toMutablePreferences(androidx.datastore.preferences.core.Preferences);
-    method public static androidx.datastore.preferences.core.Preferences toPreferences(androidx.datastore.preferences.core.Preferences);
   }
 
 }
diff --git a/datastore/datastore-preferences-core/api/public_plus_experimental_current.txt b/datastore/datastore-preferences-core/api/public_plus_experimental_current.txt
index f5f3008..1b3e2d0 100644
--- a/datastore/datastore-preferences-core/api/public_plus_experimental_current.txt
+++ b/datastore/datastore-preferences-core/api/public_plus_experimental_current.txt
@@ -3,8 +3,14 @@
 
   public final class MutablePreferences extends androidx.datastore.preferences.core.Preferences {
     method public java.util.Map<androidx.datastore.preferences.core.Preferences.Key<?>,java.lang.Object> asMap();
+    method public void clear();
     method public operator <T> boolean contains(androidx.datastore.preferences.core.Preferences.Key<T> key);
     method public operator <T> T? get(androidx.datastore.preferences.core.Preferences.Key<T> key);
+    method public operator void minusAssign(androidx.datastore.preferences.core.Preferences.Key<?> key);
+    method public operator void plusAssign(androidx.datastore.preferences.core.Preferences prefs);
+    method public operator void plusAssign(androidx.datastore.preferences.core.Preferences.Pair<?> pair);
+    method public void putAll(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
+    method public <T> T! remove(androidx.datastore.preferences.core.Preferences.Key<T> key);
     method public operator <T> void set(androidx.datastore.preferences.core.Preferences.Key<T> key, T? value);
   }
 
@@ -20,32 +26,37 @@
     method public abstract java.util.Map<androidx.datastore.preferences.core.Preferences.Key<?>,java.lang.Object> asMap();
     method public abstract operator <T> boolean contains(androidx.datastore.preferences.core.Preferences.Key<T> key);
     method public abstract operator <T> T? get(androidx.datastore.preferences.core.Preferences.Key<T> key);
+    method public final androidx.datastore.preferences.core.MutablePreferences toMutablePreferences();
+    method public final androidx.datastore.preferences.core.Preferences toPreferences();
   }
 
   public static final class Preferences.Key<T> {
     method public String getName();
+    method public infix androidx.datastore.preferences.core.Preferences.Pair<T> to(T? value);
     property public final String name;
   }
 
   public static final class Preferences.Pair<T> {
   }
 
+  public final class PreferencesFactory {
+    method public static androidx.datastore.preferences.core.Preferences create(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
+    method public static androidx.datastore.preferences.core.Preferences createEmpty();
+    method public static androidx.datastore.preferences.core.MutablePreferences createMutable(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
+  }
+
+  public final class PreferencesKeys {
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Boolean> booleanKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Double> doubleKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Float> floatKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Integer> intKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Long> longKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.String> stringKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.util.Set<java.lang.String>> stringSetKey(String name);
+  }
+
   public final class PreferencesKt {
-    method public static void clear(androidx.datastore.preferences.core.MutablePreferences);
     method public static suspend Object? edit(androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>, kotlin.jvm.functions.Function2<? super androidx.datastore.preferences.core.MutablePreferences,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> transform, kotlin.coroutines.Continuation<? super androidx.datastore.preferences.core.Preferences> p);
-    method public static androidx.datastore.preferences.core.Preferences emptyPreferences();
-    method public static operator void minusAssign(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Key<?> key);
-    method public static androidx.datastore.preferences.core.MutablePreferences mutablePreferencesOf(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
-    method public static operator void plusAssign(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences prefs);
-    method public static operator void plusAssign(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Pair<?> pair);
-    method public static inline <reified T> androidx.datastore.preferences.core.Preferences.Key<T>! preferencesKey(String name);
-    method public static androidx.datastore.preferences.core.Preferences preferencesOf(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
-    method public static inline <reified T> androidx.datastore.preferences.core.Preferences.Key<java.util.Set<? extends T>>! preferencesSetKey(String name);
-    method public static void putAll(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
-    method public static <T> T! remove(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Key<T> key);
-    method public static infix <T> androidx.datastore.preferences.core.Preferences.Pair<T> to(androidx.datastore.preferences.core.Preferences.Key<T>, T? value);
-    method public static androidx.datastore.preferences.core.MutablePreferences toMutablePreferences(androidx.datastore.preferences.core.Preferences);
-    method public static androidx.datastore.preferences.core.Preferences toPreferences(androidx.datastore.preferences.core.Preferences);
   }
 
 }
diff --git a/datastore/datastore-preferences-core/api/restricted_current.txt b/datastore/datastore-preferences-core/api/restricted_current.txt
index 71f6e03..c8be158 100644
--- a/datastore/datastore-preferences-core/api/restricted_current.txt
+++ b/datastore/datastore-preferences-core/api/restricted_current.txt
@@ -3,8 +3,14 @@
 
   public final class MutablePreferences extends androidx.datastore.preferences.core.Preferences {
     method public java.util.Map<androidx.datastore.preferences.core.Preferences.Key<?>,java.lang.Object> asMap();
+    method public void clear();
     method public operator <T> boolean contains(androidx.datastore.preferences.core.Preferences.Key<T> key);
     method public operator <T> T? get(androidx.datastore.preferences.core.Preferences.Key<T> key);
+    method public operator void minusAssign(androidx.datastore.preferences.core.Preferences.Key<?> key);
+    method public operator void plusAssign(androidx.datastore.preferences.core.Preferences prefs);
+    method public operator void plusAssign(androidx.datastore.preferences.core.Preferences.Pair<?> pair);
+    method public void putAll(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
+    method public <T> T! remove(androidx.datastore.preferences.core.Preferences.Key<T> key);
     method public operator <T> void set(androidx.datastore.preferences.core.Preferences.Key<T> key, T? value);
   }
 
@@ -20,33 +26,38 @@
     method public abstract java.util.Map<androidx.datastore.preferences.core.Preferences.Key<?>,java.lang.Object> asMap();
     method public abstract operator <T> boolean contains(androidx.datastore.preferences.core.Preferences.Key<T> key);
     method public abstract operator <T> T? get(androidx.datastore.preferences.core.Preferences.Key<T> key);
+    method public final androidx.datastore.preferences.core.MutablePreferences toMutablePreferences();
+    method public final androidx.datastore.preferences.core.Preferences toPreferences();
   }
 
   public static final class Preferences.Key<T> {
     ctor @kotlin.PublishedApi internal Preferences.Key(String name);
     method public String getName();
+    method public infix androidx.datastore.preferences.core.Preferences.Pair<T> to(T? value);
     property public final String name;
   }
 
   public static final class Preferences.Pair<T> {
   }
 
+  public final class PreferencesFactory {
+    method public static androidx.datastore.preferences.core.Preferences create(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
+    method public static androidx.datastore.preferences.core.Preferences createEmpty();
+    method public static androidx.datastore.preferences.core.MutablePreferences createMutable(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
+  }
+
+  public final class PreferencesKeys {
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Boolean> booleanKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Double> doubleKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Float> floatKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Integer> intKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.Long> longKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.lang.String> stringKey(String name);
+    method public static androidx.datastore.preferences.core.Preferences.Key<java.util.Set<java.lang.String>> stringSetKey(String name);
+  }
+
   public final class PreferencesKt {
-    method public static void clear(androidx.datastore.preferences.core.MutablePreferences);
     method public static suspend Object? edit(androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>, kotlin.jvm.functions.Function2<? super androidx.datastore.preferences.core.MutablePreferences,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> transform, kotlin.coroutines.Continuation<? super androidx.datastore.preferences.core.Preferences> p);
-    method public static androidx.datastore.preferences.core.Preferences emptyPreferences();
-    method public static operator void minusAssign(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Key<?> key);
-    method public static androidx.datastore.preferences.core.MutablePreferences mutablePreferencesOf(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
-    method public static operator void plusAssign(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences prefs);
-    method public static operator void plusAssign(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Pair<?> pair);
-    method public static inline <reified T> androidx.datastore.preferences.core.Preferences.Key<T>! preferencesKey(String name);
-    method public static androidx.datastore.preferences.core.Preferences preferencesOf(androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
-    method public static inline <reified T> androidx.datastore.preferences.core.Preferences.Key<java.util.Set<? extends T>>! preferencesSetKey(String name);
-    method public static void putAll(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Pair<?>... pairs);
-    method public static <T> T! remove(androidx.datastore.preferences.core.MutablePreferences, androidx.datastore.preferences.core.Preferences.Key<T> key);
-    method public static infix <T> androidx.datastore.preferences.core.Preferences.Pair<T> to(androidx.datastore.preferences.core.Preferences.Key<T>, T? value);
-    method public static androidx.datastore.preferences.core.MutablePreferences toMutablePreferences(androidx.datastore.preferences.core.Preferences);
-    method public static androidx.datastore.preferences.core.Preferences toPreferences(androidx.datastore.preferences.core.Preferences);
   }
 
 }
diff --git a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/Preferences.kt b/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/Preferences.kt
index 957088c..0448adf 100644
--- a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/Preferences.kt
+++ b/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/Preferences.kt
@@ -16,107 +16,10 @@
 package androidx.datastore.preferences.core
 
 import androidx.datastore.core.DataStore
-import java.lang.IllegalArgumentException
 import java.util.Collections
 import java.util.concurrent.atomic.AtomicBoolean
 
 /**
- * Get a preference Key of type T. Type T must be one of: Int, Long, Boolean, Float, Double, String.
- * Use the [preferencesSetKey] function to create a preference key for Set<String>. No
- * other types are supported.
- *
- * You should not have multiple keys with the same name (for use with the same Preferences).
- * Using overlapping keys with different types will result in ClassCastExceptions.
- *
- * @return the Preference.Key object for your preference
- * @throws IllegalArgumentException if an unsupported type is used
- */
-public inline fun <reified T : Any> preferencesKey(name: String): Preferences.Key<T> {
-    return when (T::class) {
-        Int::class -> {
-            Preferences.Key<T>(name)
-        }
-        String::class -> {
-            Preferences.Key<T>(name)
-        }
-        Boolean::class -> {
-            Preferences.Key<T>(name)
-        }
-        Float::class -> {
-            Preferences.Key<T>(name)
-        }
-        Long::class -> {
-            Preferences.Key<T>(name)
-        }
-        Double::class -> {
-            Preferences.Key<T>(name)
-        }
-        Set::class -> {
-            throw IllegalArgumentException("Use `preferencesSetKey` to create keys for Sets.")
-        }
-        else -> {
-            throw IllegalArgumentException("Type not supported: ${T::class.java}")
-        }
-    }
-}
-
-/**
- * Get a preference key for Set<T>. The only type currently supported for T is String.
- *
- * Note: sets returned by DataStore are unmodifiable.
- * @throws IllegalArgumentException if an unsupported type is used
- * @return the Preference.Key object for use with Preferences
- */
-public inline fun <reified T : Any> preferencesSetKey(name: String): Preferences.Key<Set<T>> {
-    if (T::class == String::class) {
-        return Preferences.Key(name)
-    } else {
-        throw IllegalArgumentException("Only String sets are currently supported.")
-    }
-}
-
-/**
- * Get a new empty Preferences.
- *
- * @return a new Preferences instance with no preferences set
- */
-public fun emptyPreferences(): Preferences = MutablePreferences(startFrozen = true)
-
-/**
- * Construct a Preferences object with a list of Preferences.Pair<T>. Comparable to mapOf().
- *
- * Example usage:
- *
- * val counterKey = preferencesKey<Int>("counter")
- * val preferences = preferencesOf(counterKey to 100)
- *
- * @param pairs
- */
-public fun preferencesOf(vararg pairs: Preferences.Pair<*>): Preferences =
-    mutablePreferencesOf(*pairs)
-
-/**
- * Construct a MutablePreferences object with a list of Preferences.Pair<T>. Comparable to mapOf().
- *
- * Example usage:
- *
- * val counterKey = preferencesKey<Int>("counter")
- * val preferences = preferencesOf(counterKey to 100)
- *
- * @param pairs
- */
-public fun mutablePreferencesOf(vararg pairs: Preferences.Pair<*>): MutablePreferences =
-    MutablePreferences(startFrozen = false).apply { putAll(*pairs) }
-
-/**
- * Infix function to create a Preferences.Pair.
- * This is used to support [preferencesOf] and [MutablePreferences.putAll]
- * @param value is the value this preferences key should point to.
- */
-public infix fun <T> Preferences.Key<T>.to(value: T): Preferences.Pair<T> =
-    Preferences.Pair(this, value)
-
-/**
  * Preferences and MutablePreferences are a lot like a generic Map and MutableMap keyed by the
  * Preferences.Key class. These are intended for use with DataStore. Construct a
  * DataStore<Preferences> instance using [PreferenceDataStoreFactory.create].
@@ -133,6 +36,13 @@
     public class Key<T>
     @PublishedApi // necessary to use this in the public inline function preferencesKey().
     internal constructor(public val name: String) {
+        /**
+         * Infix function to create a Preferences.Pair.
+         * This is used to support [preferencesOf] and [MutablePreferences.putAll]
+         * @param value is the value this preferences key should point to.
+         */
+        public infix fun to(value: T): Preferences.Pair<T> = Preferences.Pair(this, value)
+
         override fun equals(other: Any?): Boolean =
             if (other is Key<*>) {
                 name == other.name
@@ -152,7 +62,11 @@
      */
     public class Pair<T> internal constructor(internal val key: Key<T>, internal val value: T)
 
-    /* Checks whether Preferences contains a key. */
+    /**
+     * Returns true if this Preferences contains the specified key.
+     *
+     * @param key the key to check for
+     */
     public abstract operator fun <T> contains(key: Key<T>): Boolean
 
     /**
@@ -178,6 +92,30 @@
      * @return a map containing all the preferences in this Preferences
      */
     public abstract fun asMap(): Map<Key<*>, Any>
+
+    /**
+     * Gets a mutable copy of Preferences which contains all the preferences in this Preferences.
+     * This can be used to update your preferences without building a new Preferences object from
+     * scratch in [DataStore.updateData].
+     *
+     * This is similar to [Map.toMutableMap].
+     *
+     * @return a MutablePreferences with all the preferences from this Preferences
+     */
+    public fun toMutablePreferences(): MutablePreferences {
+        return MutablePreferences(asMap().toMutableMap(), startFrozen = false)
+    }
+
+    /**
+     * Gets a read-only copy of Preferences which contains all the preferences in this Preferences.
+     *
+     * This is similar to [Map.toMap].
+     *
+     * @return a copy of this Preferences
+     */
+    public fun toPreferences(): Preferences {
+        return MutablePreferences(asMap().toMutableMap(), startFrozen = true)
+    }
 }
 
 /**
@@ -223,7 +161,7 @@
      * Set a key value pair in MutablePreferences.
      *
      * Example usage:
-     * val COUNTER_KEY = preferencesKey<Int>("counter")
+     * val COUNTER_KEY = intPreferencesKey("counter")
      *
      * // Once edit completes successfully, preferenceStore will contain the incremented counter.
      * preferenceStore.edit { prefs: MutablePreferences ->
@@ -252,6 +190,77 @@
         }
     }
 
+    /**
+     * Appends or replaces all pairs from [prefs] to this MutablePreferences. Keys in [prefs]
+     * will overwrite keys in this Preferences.
+     *
+     * Example usage:
+     * mutablePrefs += preferencesOf(COUNTER_KEY to 100, NAME to "abcdef")
+     *
+     * @param prefs Preferences to append to this MutablePreferences
+     */
+    public operator fun plusAssign(prefs: Preferences) {
+        checkNotFrozen()
+        preferencesMap += prefs.asMap()
+    }
+
+    /**
+     * Appends or replaces all [pair] to this MutablePreferences.
+     *
+     * Example usage:
+     * mutablePrefs += COUNTER_KEY to 100
+     *
+     * @param pair the Preference.Pair to add to this MutablePreferences
+     */
+    public operator fun plusAssign(pair: Preferences.Pair<*>) {
+        checkNotFrozen()
+        putAll(pair)
+    }
+
+    /**
+     * Removes the preference with the given key from this MutablePreferences. If this
+     * Preferences does not contain the key, this is a no-op.
+     *
+     * Example usage:
+     * mutablePrefs -= COUNTER_KEY
+     *
+     * @param key the key to remove from this MutablePreferences
+     */
+    public operator fun minusAssign(key: Preferences.Key<*>) {
+        checkNotFrozen()
+        remove(key)
+    }
+
+    /**
+     * Appends or replaces all [pairs] to this MutablePreferences.
+     *
+     * @param pairs the pairs to append to this MutablePreferences
+     */
+    public fun putAll(vararg pairs: Preferences.Pair<*>) {
+        checkNotFrozen()
+        pairs.forEach {
+            setUnchecked(it.key, it.value)
+        }
+    }
+
+    /**
+     * Remove a preferences from this MutablePreferences.
+     *
+     * @param key the key to remove this MutablePreferences
+     * @return the original value of this preference key.
+     */
+    @Suppress("UNCHECKED_CAST")
+    public fun <T> remove(key: Preferences.Key<T>): T {
+        checkNotFrozen()
+        return preferencesMap.remove(key) as T
+    }
+
+    /* Removes all preferences from this MutablePreferences. */
+    public fun clear() {
+        checkNotFrozen()
+        preferencesMap.clear()
+    }
+
     // Equals and hash code for use by DataStore
     override fun equals(other: Any?): Boolean {
         if (other is MutablePreferences) {
@@ -278,101 +287,6 @@
 }
 
 /**
- * Gets a mutable copy of Preferences which contains all the preferences in this Preferences.
- * This can be used to update your preferences without building a new Preferences object from
- * scratch in [DataStore.updateData].
- *
- * This is similar to [Map.toMutableMap].
- *
- * @return a MutablePreferences with all the preferences from this Preferences
- */
-public fun Preferences.toMutablePreferences(): MutablePreferences {
-    return MutablePreferences(asMap().toMutableMap(), startFrozen = false)
-}
-
-/**
- * Gets a read-only copy of Preferences which contains all the preferences in this Preferences.
- *
- * This is similar to [Map.toMap].
- *
- * @return a copy of this Preferences
- */
-public fun Preferences.toPreferences(): Preferences {
-    return MutablePreferences(asMap().toMutableMap(), startFrozen = true)
-}
-
-/**
- * Appends or replaces all pairs from [prefs] to this MutablePreferences. Keys in [prefs]
- * will overwrite keys in this Preferences.
- *
- * Example usage:
- * mutablePrefs += preferencesOf(COUNTER_KEY to 100, NAME to "abcdef")
- *
- * @param prefs Preferences to append to this MutablePreferences
- */
-public operator fun MutablePreferences.plusAssign(prefs: Preferences) {
-    checkNotFrozen()
-    preferencesMap += prefs.asMap()
-}
-
-/**
- * Appends or replaces all [pair] to this MutablePreferences.
- *
- * Example usage:
- * mutablePrefs += COUNTER_KEY to 100
- *
- * @param pair the Preference.Pair to add to this MutablePreferences
- */
-public operator fun MutablePreferences.plusAssign(pair: Preferences.Pair<*>) {
-    checkNotFrozen()
-    putAll(pair)
-}
-
-/**
- * Removes the preference with the given key from this MutablePreferences. If this
- * Preferences does not contain the key, this is a no-op.
- *
- * Example usage:
- * mutablePrefs -= COUNTER_KEY
- *
- * @param key the key to remove from this MutablePreferences
- */
-public operator fun MutablePreferences.minusAssign(key: Preferences.Key<*>) {
-    checkNotFrozen()
-    remove(key)
-}
-
-/**
- * Appends or replaces all [pairs] to this MutablePreferences.
- *
- * @param pairs the pairs to append to this MutablePreferences
- */
-public fun MutablePreferences.putAll(vararg pairs: Preferences.Pair<*>) {
-    checkNotFrozen()
-    pairs.forEach {
-        setUnchecked(it.key, it.value)
-    }
-}
-
-/**
- * Remove a preferences from this MutablePreferences.
- *
- * @param key the key to remove this MutablePreferences
- * @return the original value of this preference key.
- */
-@Suppress("UNCHECKED_CAST")
-public fun <T> MutablePreferences.remove(key: Preferences.Key<T>): T {
-    checkNotFrozen()
-    return preferencesMap.remove(key) as T
-}
-
-/* Removes all preferences from this MutablePreferences. */
-public fun MutablePreferences.clear() {
-    checkNotFrozen()
-    preferencesMap.clear()
-}
-
-/**
  * Edit the value in DataStore transactionally in an atomic read-modify-write operation. All
  * operations are serialized.
  *
@@ -391,7 +305,7 @@
  * See [DataStore.updateData].
  *
  * Example usage:
- * val COUNTER_KEY = preferencesKey<Int>("my_counter")
+ * val COUNTER_KEY = intPreferencesKey("my_counter")
  *
  * dataStore.edit { prefs ->
  *   prefs\[COUNTER_KEY\] = prefs\[COUNTER_KEY\] :? 0 + 1
diff --git a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesFactory.kt b/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesFactory.kt
new file mode 100644
index 0000000..ca97f77
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesFactory.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+@file:JvmName("PreferencesFactory")
+
+package androidx.datastore.preferences.core
+
+/**
+ * Get a new empty Preferences.
+ *
+ * @return a new Preferences instance with no preferences set
+ */
+@JvmName("createEmpty")
+public fun emptyPreferences(): Preferences = MutablePreferences(startFrozen = true)
+
+/**
+ * Construct a Preferences object with a list of Preferences.Pair<T>. Comparable to mapOf().
+ *
+ * Example usage:
+ *
+ * val counterKey = intPreferencesKey("counter")
+ * val preferences = preferencesOf(counterKey to 100)
+ *
+ * @param pairs the key value pairs with which to construct the preferences
+ */
+@JvmName("create")
+public fun preferencesOf(vararg pairs: Preferences.Pair<*>): Preferences =
+    mutablePreferencesOf(*pairs)
+
+/**
+ * Construct a MutablePreferences object with a list of Preferences.Pair<T>. Comparable to mapOf().
+ *
+ * Example usage:
+ *
+ * val counterKey = intPreferencesKey("counter")
+ * val preferences = preferencesOf(counterKey to 100)
+ *
+ * @param pairs the key value pairs with which to construct the preferences
+ */
+@JvmName("createMutable")
+public fun mutablePreferencesOf(vararg pairs: Preferences.Pair<*>): MutablePreferences =
+    MutablePreferences(startFrozen = false).apply { putAll(*pairs) }
diff --git a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesKeys.kt b/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesKeys.kt
new file mode 100644
index 0000000..fde37e8
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesKeys.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+@file:JvmName("PreferencesKeys")
+
+package androidx.datastore.preferences.core
+
+/**
+ * Get a key for an Int preference. You should not have multiple keys with the same name (for use
+ * with the same Preferences). Using overlapping keys with different types can result in
+ * ClassCastException.
+ *
+ * @param name the name of the preference
+ * @return the Preferences.Key<Int> for [name]
+ */
+@JvmName("intKey")
+public fun intPreferencesKey(name: String): Preferences.Key<Int> = Preferences.Key(name)
+
+/**
+ * Get a key for a Double preference. You should not have multiple keys with the same name (for use
+ * with the same Preferences). Using overlapping keys with different types can result in
+ * ClassCastException.
+ *
+ * @param name the name of the preference
+ * @return the Preferences.Key<Double> for [name]
+ */
+@JvmName("doubleKey")
+public fun doublePreferencesKey(name: String): Preferences.Key<Double> = Preferences.Key(name)
+
+/**
+ * Get a key for a String preference. You should not have multiple keys with the same name (for use
+ * with the same Preferences). Using overlapping keys with different types can result in
+ * ClassCastException.
+ *
+ * @param name the name of the preference
+ * @return the Preferences.Key<String> for [name]
+ */
+@JvmName("stringKey")
+public fun stringPreferencesKey(name: String): Preferences.Key<String> = Preferences.Key(name)
+
+/**
+ * Get a key for a Boolean preference. You should not have multiple keys with the same name (for use
+ * with the same Preferences). Using overlapping keys with different types can result in
+ * ClassCastException.
+ *
+ * @param name the name of the preference
+ * @return the Preferences.Key<Boolean> for [name]
+ */
+@JvmName("booleanKey")
+public fun booleanPreferencesKey(name: String): Preferences.Key<Boolean> = Preferences.Key(name)
+
+/**
+ * Get a key for a Float preference. You should not have multiple keys with the same name (for use
+ * with the same Preferences). Using overlapping keys with different types can result in
+ * ClassCastException.
+ *
+ * @param name the name of the preference
+ * @return the Preferences.Key<Float> for [name]
+ */
+@JvmName("floatKey")
+public fun floatPreferencesKey(name: String): Preferences.Key<Float> = Preferences.Key(name)
+
+/**
+ * Get a key for an Long preference. You should not have multiple keys with the same name (for use
+ * with the same Preferences). Using overlapping keys with different types can result in
+ * ClassCastException.
+ *
+ * @param name the name of the preference
+ * @return the Preferences.Key<Long> for [name]
+ */
+@JvmName("longKey")
+public fun longPreferencesKey(name: String): Preferences.Key<Long> = Preferences.Key(name)
+
+/**
+ * Get a key for a String Set preference. You should not have multiple keys with the same name (for
+ * use with the same Preferences). Using overlapping keys with different types can result in
+ * ClassCastException.
+ *
+ * Note: sets returned by DataStore are unmodifiable and will throw exceptions if mutated.
+ *
+ * @param name the name of the preference
+ * @return the Preferences.Key<Set<String>> for [name]
+ */
+@JvmName("stringSetKey")
+public fun stringSetPreferencesKey(name: String): Preferences.Key<Set<String>> =
+    Preferences.Key(name)
diff --git a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesSerializer.kt b/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesSerializer.kt
index 2500055..00429d6 100644
--- a/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesSerializer.kt
+++ b/datastore/datastore-preferences-core/src/main/java/androidx/datastore/preferences/core/PreferencesSerializer.kt
@@ -91,14 +91,16 @@
         mutablePreferences: MutablePreferences
     ) {
         return when (value.valueCase) {
-            Value.ValueCase.BOOLEAN -> mutablePreferences[preferencesKey(name)] = value.boolean
-            Value.ValueCase.FLOAT -> mutablePreferences[preferencesKey(name)] = value.float
-            Value.ValueCase.DOUBLE -> mutablePreferences[preferencesKey(name)] = value.double
-            Value.ValueCase.INTEGER -> mutablePreferences[preferencesKey(name)] = value.integer
-            Value.ValueCase.LONG -> mutablePreferences[preferencesKey(name)] = value.long
-            Value.ValueCase.STRING -> mutablePreferences[preferencesKey(name)] = value.string
+            Value.ValueCase.BOOLEAN ->
+                mutablePreferences[booleanPreferencesKey(name)] =
+                    value.boolean
+            Value.ValueCase.FLOAT -> mutablePreferences[floatPreferencesKey(name)] = value.float
+            Value.ValueCase.DOUBLE -> mutablePreferences[doublePreferencesKey(name)] = value.double
+            Value.ValueCase.INTEGER -> mutablePreferences[intPreferencesKey(name)] = value.integer
+            Value.ValueCase.LONG -> mutablePreferences[longPreferencesKey(name)] = value.long
+            Value.ValueCase.STRING -> mutablePreferences[stringPreferencesKey(name)] = value.string
             Value.ValueCase.STRING_SET ->
-                mutablePreferences[preferencesSetKey<String>(name)] =
+                mutablePreferences[stringSetPreferencesKey(name)] =
                     value.stringSet.stringsList.toSet()
             Value.ValueCase.VALUE_NOT_SET ->
                 throw CorruptionException("Value not set.")
diff --git a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferenceDataStoreFactoryTest.kt b/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferenceDataStoreFactoryTest.kt
index 7961019..a3e3fe7 100644
--- a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferenceDataStoreFactoryTest.kt
+++ b/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferenceDataStoreFactoryTest.kt
@@ -42,8 +42,8 @@
     private lateinit var testFile: File
     private lateinit var dataStoreScope: TestCoroutineScope
 
-    val stringKey = preferencesKey<String>("key")
-    val booleanKey = preferencesKey<Boolean>("key")
+    val stringKey = stringPreferencesKey("key")
+    val booleanKey = booleanPreferencesKey("key")
 
     @Before
     fun setUp() {
diff --git a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesFromJavaTest.java b/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesFromJavaTest.java
new file mode 100644
index 0000000..539b1d9
--- /dev/null
+++ b/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesFromJavaTest.java
@@ -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.datastore.preferences.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+
+public class PreferencesFromJavaTest {
+
+    @Test
+    public void testStoreAndGetInteger() {
+        Preferences.Key<Integer> integerKey = PreferencesKeys.intKey("integer_key");
+
+        MutablePreferences mutablePreferences = PreferencesFactory.createMutable();
+        mutablePreferences.set(integerKey, 123);
+
+        assertEquals(1, mutablePreferences.asMap().size());
+        assertEquals(Integer.valueOf(123), mutablePreferences.get(integerKey));
+    }
+
+    @Test
+    public void testAllKeyTypes() {
+        Preferences.Key<Integer> integerKey = PreferencesKeys.intKey("integer_key");
+        Preferences.Key<Long> longKey = PreferencesKeys.longKey("long_key");
+        Preferences.Key<Float> floatKey = PreferencesKeys.floatKey("float_key");
+        Preferences.Key<Double> doubleKey = PreferencesKeys.doubleKey("double_key");
+        Preferences.Key<String> stringKey = PreferencesKeys.stringKey("string_key");
+        Preferences.Key<Boolean> booleanKey = PreferencesKeys.booleanKey("boolean_key");
+        Preferences.Key<Set<String>> stringSetKey = PreferencesKeys.stringSetKey(
+                "string_set_key");
+
+        MutablePreferences mutablePreferences = PreferencesFactory.createMutable();
+        mutablePreferences.set(integerKey, 123);
+        mutablePreferences.set(longKey, 1234567890123L);
+        mutablePreferences.set(floatKey, 1.23f);
+        mutablePreferences.set(doubleKey, 1.23d);
+        mutablePreferences.set(stringKey, "123");
+        mutablePreferences.set(booleanKey, true);
+        mutablePreferences.set(stringSetKey,
+                new HashSet<>(Arrays.asList("1", "2", "3")));
+
+        assertEquals(Integer.valueOf(123),
+                mutablePreferences.get(integerKey));
+        assertEquals(Long.valueOf(1234567890123L),
+                mutablePreferences.get(longKey));
+        assertEquals(Float.valueOf(1.23f),
+                mutablePreferences.get(floatKey));
+        assertEquals(Double.valueOf(1.23d),
+                mutablePreferences.get(doubleKey));
+        assertEquals("123",
+                mutablePreferences.get(stringKey));
+
+        assertEquals(new HashSet<>(Arrays.asList("1", "2", "3")),
+                mutablePreferences.get(stringSetKey));
+    }
+
+    @Test
+    public void testNonExistentKeyIsNull() {
+        MutablePreferences mutablePreferences = PreferencesFactory.createMutable();
+        Preferences.Key<Integer> integerKey = PreferencesKeys.intKey("integer_key");
+
+        assertNull(mutablePreferences.get(integerKey));
+    }
+
+    @Test
+    public void testMutablePreferencesOfConstructor() {
+        Preferences.Key<Integer> integerKey = PreferencesKeys.intKey("integer_key");
+
+        MutablePreferences mutablePreferences =
+                PreferencesFactory.createMutable(integerKey.to(123));
+        assertEquals(Integer.valueOf(123), mutablePreferences.get(integerKey));
+    }
+
+    @Test
+    public void testCreateEmpty() {
+        assertEquals(PreferencesFactory.createMutable(), PreferencesFactory.createEmpty());
+        assertEquals(PreferencesFactory.create(), PreferencesFactory.createEmpty());
+        assertEquals(0, PreferencesFactory.createEmpty().asMap().size());
+    }
+}
diff --git a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesSerializerTest.kt b/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesSerializerTest.kt
index 60c10a3..a4b2dbf 100644
--- a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesSerializerTest.kt
+++ b/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesSerializerTest.kt
@@ -43,7 +43,7 @@
 
     @Test
     fun testWriteAndReadString() {
-        val stringKey = preferencesKey<String>("string_key")
+        val stringKey = stringPreferencesKey("string_key")
 
         val prefs = preferencesOf(
             stringKey to "string1"
@@ -63,7 +63,7 @@
     @Test
     fun testWriteAndReadStringSet() {
         val stringSetKey =
-            preferencesSetKey<String>("string_set_key")
+            stringSetPreferencesKey("string_set_key")
 
         val prefs = preferencesOf(
             stringSetKey to setOf("string1", "string2", "string3")
@@ -82,7 +82,7 @@
 
     @Test
     fun testWriteAndReadLong() {
-        val longKey = preferencesKey<Long>("long_key")
+        val longKey = longPreferencesKey("long_key")
 
         val prefs = preferencesOf(
             longKey to (1 shr 50)
@@ -101,7 +101,7 @@
 
     @Test
     fun testWriteAndReadInt() {
-        val intKey = preferencesKey<Int>("int_key")
+        val intKey = intPreferencesKey("int_key")
 
         val prefs = preferencesOf(
             intKey to 3
@@ -120,7 +120,7 @@
 
     @Test
     fun testWriteAndReadBoolean() {
-        val booleanKey = preferencesKey<Boolean>("boolean_key")
+        val booleanKey = booleanPreferencesKey("boolean_key")
 
         val prefs = preferencesOf(
             booleanKey to true
@@ -139,7 +139,7 @@
 
     @Test
     fun testWriteAndReadFloat() {
-        val floatKey = preferencesKey<Float>("float_key")
+        val floatKey = floatPreferencesKey("float_key")
 
         val prefs = preferencesOf(
             floatKey to 3.0f
@@ -158,8 +158,8 @@
 
     @Test
     fun testWriteAndReadDouble() {
-        val maxDouble = preferencesKey<Double>("max_double_key")
-        val minDouble = preferencesKey<Double>("min_double_key")
+        val maxDouble = doublePreferencesKey("max_double_key")
+        val minDouble = doublePreferencesKey("min_double_key")
 
         val prefs = preferencesOf(
             maxDouble to Double.MAX_VALUE,
diff --git a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesTest.kt b/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesTest.kt
index 7171372..147fdde 100644
--- a/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesTest.kt
+++ b/datastore/datastore-preferences-core/src/test/java/androidx/datastore/preferences/core/PreferencesTest.kt
@@ -19,7 +19,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import java.lang.IllegalArgumentException
 import java.lang.UnsupportedOperationException
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
@@ -32,7 +31,7 @@
 
     @Test
     fun testBoolean() {
-        val booleanKey = preferencesKey<Boolean>("boolean_key")
+        val booleanKey = booleanPreferencesKey("boolean_key")
 
         val prefs = preferencesOf(booleanKey to true)
 
@@ -42,14 +41,14 @@
 
     @Test
     fun testBooleanNotSet() {
-        val booleanKey = preferencesKey<Boolean>("boolean_key")
+        val booleanKey = booleanPreferencesKey("boolean_key")
 
         assertNull(emptyPreferences()[booleanKey])
     }
 
     @Test
     fun testFloat() {
-        val floatKey = preferencesKey<Float>("float_key")
+        val floatKey = floatPreferencesKey("float_key")
 
         val prefs = preferencesOf(floatKey to 1.1f)
 
@@ -59,13 +58,13 @@
 
     @Test
     fun testFloatNotSet() {
-        val floatKey = preferencesKey<Float>("float_key")
+        val floatKey = floatPreferencesKey("float_key")
         assertNull(emptyPreferences()[floatKey])
     }
 
     @Test
     fun testDouble() {
-        val doubleKey = preferencesKey<Double>("double_key")
+        val doubleKey = doublePreferencesKey("double_key")
 
         val prefs = preferencesOf(doubleKey to Double.MAX_VALUE)
 
@@ -75,13 +74,13 @@
 
     @Test
     fun testDoubleNotSet() {
-        val doubleKey = preferencesKey<Double>("double_key")
+        val doubleKey = doublePreferencesKey("double_key")
         assertNull(emptyPreferences()[doubleKey])
     }
 
     @Test
     fun testInt() {
-        val intKey = preferencesKey<Int>("int_key")
+        val intKey = intPreferencesKey("int_key")
 
         val prefs = preferencesOf(intKey to 1)
 
@@ -91,13 +90,13 @@
 
     @Test
     fun testIntNotSet() {
-        val intKey = preferencesKey<Int>("int_key")
+        val intKey = intPreferencesKey("int_key")
         assertNull(emptyPreferences()[intKey])
     }
 
     @Test
     fun testLong() {
-        val longKey = preferencesKey<Long>("long_key")
+        val longKey = longPreferencesKey("long_key")
 
         val bigLong = 1L shr 50; // 2^50 > Int.MAX_VALUE
 
@@ -109,14 +108,14 @@
 
     @Test
     fun testLongNotSet() {
-        val longKey = preferencesKey<Long>("long_key")
+        val longKey = longPreferencesKey("long_key")
 
         assertNull(emptyPreferences()[longKey])
     }
 
     @Test
     fun testString() {
-        val stringKey = preferencesKey<String>("string_key")
+        val stringKey = stringPreferencesKey("string_key")
 
         val prefs = preferencesOf(stringKey to "string123")
 
@@ -126,7 +125,7 @@
 
     @Test
     fun testStringNotSet() {
-        val stringKey = preferencesKey<String>("string_key")
+        val stringKey = stringPreferencesKey("string_key")
 
         assertNull(emptyPreferences()[stringKey])
     }
@@ -134,7 +133,7 @@
     @Test
     fun testStringSet() {
         val stringSetKey =
-            preferencesSetKey<String>("string_set_key")
+            stringSetPreferencesKey("string_set_key")
 
         val prefs = preferencesOf(
             stringSetKey to setOf(
@@ -153,7 +152,7 @@
     @Test
     fun testStringSetNotSet() {
         val stringSetKey =
-            preferencesSetKey<String>("string_set_key")
+            stringSetPreferencesKey("string_set_key")
 
         assertNull(emptyPreferences()[stringSetKey])
     }
@@ -161,7 +160,7 @@
     @Test
     fun testModifyingStringSetDoesntModifyInternalState() {
         val stringSetKey =
-            preferencesSetKey<String>("string_set_key")
+            stringSetPreferencesKey("string_set_key")
 
         val stringSet = mutableSetOf("1", "2", "3")
 
@@ -186,10 +185,10 @@
     @Test
     @Suppress("UNUSED_VARIABLE")
     fun testWrongTypeThrowsClassCastException() {
-        val stringKey = preferencesKey<String>("string_key")
+        val stringKey = stringPreferencesKey("string_key")
         val intKey =
-            preferencesKey<Int>("string_key") // long key of the same name as stringKey!
-        val longKey = preferencesKey<Long>("string_key")
+            intPreferencesKey("string_key") // long key of the same name as stringKey!
+        val longKey = longPreferencesKey("string_key")
 
         val prefs = preferencesOf(intKey to 123456)
 
@@ -211,9 +210,9 @@
 
     @Test
     fun testGetAll() {
-        val intKey = preferencesKey<Int>("int_key")
+        val intKey = intPreferencesKey("int_key")
         val stringSetKey =
-            preferencesSetKey<String>("string_set_key")
+            stringSetPreferencesKey("string_set_key")
 
         val prefs = preferencesOf(
             intKey to 123,
@@ -230,9 +229,9 @@
     @Test
     @Suppress("UNCHECKED_CAST")
     fun testGetAllCantMutateInternalState() {
-        val intKey = preferencesKey<Int>("int_key")
+        val intKey = intPreferencesKey("int_key")
         val stringSetKey =
-            preferencesSetKey<String>("string_set_key")
+            stringSetPreferencesKey("string_set_key")
 
         val prefs = preferencesOf(
             intKey to 123,
@@ -253,7 +252,7 @@
 
     @Test
     fun testMutablePreferencesClear() {
-        val intKey = preferencesKey<Int>("int_key")
+        val intKey = intPreferencesKey("int_key")
 
         val prefsWithInt = preferencesOf(intKey to 123)
 
@@ -264,7 +263,7 @@
 
     @Test
     fun testMutablePreferencesRemove() {
-        val intKey = preferencesKey<Int>("int_key")
+        val intKey = intPreferencesKey("int_key")
 
         val prefsWithInt = preferencesOf(intKey to 123)
 
@@ -288,7 +287,7 @@
 
     @Test
     fun testEqualsDifferentInstances() {
-        val intKey1 = preferencesKey<Int>("int_key1")
+        val intKey1 = intPreferencesKey("int_key1")
 
         val prefs1 = preferencesOf(intKey1 to 123)
         val prefs2 = preferencesOf(intKey1 to 123)
@@ -298,8 +297,8 @@
 
     @Test
     fun testNotEqualsDifferentKeys() {
-        val intKey1 = preferencesKey<Int>("int_key1")
-        val intKey2 = preferencesKey<Int>("int_key2")
+        val intKey1 = intPreferencesKey("int_key1")
+        val intKey2 = intPreferencesKey("int_key2")
 
         val prefs1 = preferencesOf(intKey1 to 123)
         val prefs2 = preferencesOf(intKey2 to 123)
@@ -309,7 +308,7 @@
 
     @Test
     fun testNotEqualsDifferentValues() {
-        val intKey1 = preferencesKey<Int>("int_key1")
+        val intKey1 = intPreferencesKey("int_key1")
 
         val prefs1 = preferencesOf(intKey1 to 123)
         val prefs2 = preferencesOf(intKey1 to 999)
@@ -320,7 +319,7 @@
     @Test
     fun testNotEqualsDifferentStringSets() {
         val stringSetKey =
-            preferencesSetKey<String>("string_set")
+            stringSetPreferencesKey("string_set")
 
         val prefs1 = preferencesOf(stringSetKey to setOf("1"))
         val prefs2 = preferencesOf(stringSetKey to setOf())
@@ -329,57 +328,9 @@
     }
 
     @Test
-    fun testCreateUnsupportedKeyType_failsWithIllegalStateException() {
-        assertFailsWith<IllegalArgumentException> {
-            preferencesKey<Set<String>>(
-                "test"
-            )
-        }
-        assertFailsWith<IllegalArgumentException> {
-            preferencesKey<Set<*>>(
-                "test"
-            )
-        }
-        assertFailsWith<IllegalArgumentException> {
-            preferencesKey<Preferences>(
-                "test"
-            )
-        }
-        assertFailsWith<IllegalArgumentException> {
-            preferencesKey<Any>(
-                "test"
-            )
-        }
-    }
-
-    @Test
-    fun testCreateUnsupportedSetKeyType_failsWithIllegalStateException() {
-        assertFailsWith<IllegalArgumentException> {
-            preferencesSetKey<Set<String>>(
-                "test"
-            )
-        }
-        assertFailsWith<IllegalArgumentException> {
-            preferencesSetKey<Set<*>>(
-                "test"
-            )
-        }
-        assertFailsWith<IllegalArgumentException> {
-            preferencesSetKey<Double>(
-                "test"
-            )
-        }
-        assertFailsWith<IllegalArgumentException> {
-            preferencesSetKey<Any>(
-                "test"
-            )
-        }
-    }
-
-    @Test
     fun testToPreferences_retainsAllKeys() {
-        val intKey1 = preferencesKey<Int>("int_key1")
-        val intKey2 = preferencesKey<Int>("int_key2")
+        val intKey1 = intPreferencesKey("int_key1")
+        val intKey2 = intPreferencesKey("int_key2")
         val prefs = preferencesOf(intKey1 to 1, intKey2 to 2)
         val toPrefs = prefs.toPreferences()
         assertEquals(2, toPrefs.asMap().size)
@@ -396,8 +347,8 @@
 
     @Test
     fun testToMutablePreferences_retainsAllKeys() {
-        val intKey1 = preferencesKey<Int>("int_key1")
-        val intKey2 = preferencesKey<Int>("int_key2")
+        val intKey1 = intPreferencesKey("int_key1")
+        val intKey2 = intPreferencesKey("int_key2")
         val prefs = preferencesOf(intKey1 to 1, intKey2 to 2)
         val toPrefs = prefs.toMutablePreferences()
         assertEquals(2, toPrefs.asMap().size)
@@ -414,8 +365,8 @@
 
     @Test
     fun testToMutablePreferences_doesntMutateOriginal() {
-        val intKey1 = preferencesKey<Int>("int_key1")
-        val intKey2 = preferencesKey<Int>("int_key2")
+        val intKey1 = intPreferencesKey("int_key1")
+        val intKey2 = intPreferencesKey("int_key2")
         val prefs =
             mutablePreferencesOf(intKey1 to 1, intKey2 to 2)
         val toPrefs = prefs.toMutablePreferences()
@@ -431,13 +382,13 @@
 
     @Test
     fun testToString() {
-        val intKey = preferencesKey<Int>("int_key")
-        val booleanKey = preferencesKey<Boolean>("boolean_key")
-        val floatKey = preferencesKey<Float>("float_key")
-        val stringKey = preferencesKey<String>("string_key")
+        val intKey = intPreferencesKey("int_key")
+        val booleanKey = booleanPreferencesKey("boolean_key")
+        val floatKey = floatPreferencesKey("float_key")
+        val stringKey = stringPreferencesKey("string_key")
         val stringSetKey =
-            preferencesSetKey<String>("string_set_key")
-        val longKey = preferencesKey<Long>("long_key")
+            stringSetPreferencesKey("string_set_key")
+        val longKey = longPreferencesKey("long_key")
 
         val prefs = preferencesOf(
             intKey to 123,
diff --git a/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/PreferenceDataStoreFactoryTest.kt b/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/PreferenceDataStoreFactoryTest.kt
index 26f06482..1d5f383 100644
--- a/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/PreferenceDataStoreFactoryTest.kt
+++ b/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/PreferenceDataStoreFactoryTest.kt
@@ -22,17 +22,10 @@
 import androidx.datastore.preferences.core.MutablePreferences
 import androidx.datastore.preferences.core.PreferenceDataStoreFactory
 import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.core.clear
-import androidx.datastore.preferences.core.edit
-import androidx.datastore.preferences.core.minusAssign
-import androidx.datastore.preferences.core.plusAssign
-import androidx.datastore.preferences.core.preferencesKey
+import androidx.datastore.preferences.core.booleanPreferencesKey
 import androidx.datastore.preferences.core.preferencesOf
-import androidx.datastore.preferences.core.putAll
-import androidx.datastore.preferences.core.remove
-import androidx.datastore.preferences.core.to
-import androidx.datastore.preferences.core.toMutablePreferences
-import androidx.datastore.preferences.core.toPreferences
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
 import androidx.test.core.app.ApplicationProvider
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.ObsoleteCoroutinesApi
@@ -59,8 +52,8 @@
     private lateinit var dataStoreScope: TestCoroutineScope
     private lateinit var context: Context
 
-    val stringKey = preferencesKey<String>("key")
-    val booleanKey = preferencesKey<Boolean>("key")
+    val stringKey = stringPreferencesKey("key")
+    val booleanKey = booleanPreferencesKey("key")
 
     @Before
     fun setUp() {
diff --git a/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/SharedPreferencesToPreferencesTest.kt b/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/SharedPreferencesToPreferencesTest.kt
index 7a5018f..f24d608 100644
--- a/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/SharedPreferencesToPreferencesTest.kt
+++ b/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/SharedPreferencesToPreferencesTest.kt
@@ -22,7 +22,11 @@
 import androidx.datastore.core.DataStore
 import androidx.datastore.preferences.core.PreferenceDataStoreFactory
 import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.core.preferencesKey
+import androidx.datastore.preferences.core.booleanPreferencesKey
+import androidx.datastore.preferences.core.floatPreferencesKey
+import androidx.datastore.preferences.core.intPreferencesKey
+import androidx.datastore.preferences.core.longPreferencesKey
+import androidx.datastore.preferences.core.stringPreferencesKey
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.filters.MediumTest
 import kotlinx.coroutines.flow.first
@@ -63,7 +67,7 @@
 
     @Test
     fun existingKey_isMigrated() = runBlockingTest {
-        val stringKey = preferencesKey<String>("string_key")
+        val stringKey = stringPreferencesKey("string_key")
         val stringValue = "string value"
 
         assertTrue { sharedPrefs.edit().putString(stringKey.name, stringValue).commit() }
@@ -83,7 +87,7 @@
 
     @Test
     fun existingKey_isRemovedFromSharedPrefs() = runBlockingTest {
-        val stringKey = preferencesKey<String>("string_key")
+        val stringKey = stringPreferencesKey("string_key")
         val stringValue = "string value"
 
         assertTrue { sharedPrefs.edit().putString(stringKey.name, stringValue).commit() }
@@ -103,7 +107,7 @@
 
     @Test
     fun supportsStringKey() = runBlockingTest {
-        val stringKey = preferencesKey<String>("string_key")
+        val stringKey = stringPreferencesKey("string_key")
         val stringValue = "string_value"
 
         assertTrue { sharedPrefs.edit().putString(stringKey.name, stringValue).commit() }
@@ -121,7 +125,7 @@
 
     @Test
     fun supportsIntegerKey() = runBlockingTest {
-        val integerKey = preferencesKey<Int>("integer_key")
+        val integerKey = intPreferencesKey("integer_key")
         val integerValue = 123
 
         assertTrue { sharedPrefs.edit().putInt(integerKey.name, integerValue).commit() }
@@ -139,7 +143,7 @@
 
     @Test
     fun supportsFloatKey() = runBlockingTest {
-        val floatKey = preferencesKey<Float>("float_key")
+        val floatKey = floatPreferencesKey("float_key")
         val floatValue = 123.0f
 
         assertTrue { sharedPrefs.edit().putFloat(floatKey.name, floatValue).commit() }
@@ -157,7 +161,7 @@
 
     @Test
     fun supportsBooleanKey() = runBlockingTest {
-        val booleanKey = preferencesKey<Boolean>("boolean_key")
+        val booleanKey = booleanPreferencesKey("boolean_key")
         val booleanValue = true
 
         assertTrue { sharedPrefs.edit().putBoolean(booleanKey.name, booleanValue).commit() }
@@ -175,7 +179,7 @@
 
     @Test
     fun supportsLongKey() = runBlockingTest {
-        val longKey = preferencesKey<Long>("long_key")
+        val longKey = longPreferencesKey("long_key")
         val longValue = 1L shr 50
 
         assertTrue { sharedPrefs.edit().putLong(longKey.name, longValue).commit() }
@@ -194,7 +198,7 @@
     @Test
     fun supportsStringSetKey() = runBlockingTest {
         val stringSetKey =
-            androidx.datastore.preferences.core.preferencesSetKey<String>("stringSet_key")
+            androidx.datastore.preferences.core.stringSetPreferencesKey("stringSet_key")
         val stringSetValue = setOf("a", "b", "c")
 
         assertTrue { sharedPrefs.edit().putStringSet(stringSetKey.name, stringSetValue).commit() }
@@ -213,7 +217,7 @@
     @Test
     fun migratedStringSetNotMutable() = runBlockingTest {
         val stringSetKey =
-            androidx.datastore.preferences.core.preferencesSetKey<String>("stringSet_key")
+            androidx.datastore.preferences.core.stringSetPreferencesKey("stringSet_key")
         val stringSetValue = setOf("a", "b", "c")
 
         assertTrue { sharedPrefs.edit().putStringSet(stringSetKey.name, stringSetValue).commit() }
@@ -238,7 +242,7 @@
 
     @Test
     fun sharedPreferencesFileDeletedIfPrefsEmpty() = runBlockingTest {
-        val integerKey = preferencesKey<Int>("integer_key")
+        val integerKey = intPreferencesKey("integer_key")
 
         assertTrue { sharedPrefs.edit().putInt(integerKey.name, 123).commit() }
 
@@ -257,7 +261,7 @@
 
     @Test
     fun sharedPreferencesFileNotDeletedIfDisabled() = runBlockingTest {
-        val integerKey = preferencesKey<Int>("integer_key")
+        val integerKey = intPreferencesKey("integer_key")
 
         assertTrue { sharedPrefs.edit().putInt(integerKey.name, 123).commit() }
 
@@ -278,8 +282,8 @@
 
     @Test
     fun sharedPreferencesFileNotDeletedIfPrefsNotEmpty() = runBlockingTest {
-        val integerKey1 = preferencesKey<Int>("integer_key1")
-        val integerKey2 = preferencesKey<Int>("integer_key2")
+        val integerKey1 = intPreferencesKey("integer_key1")
+        val integerKey2 = intPreferencesKey("integer_key2")
 
         assertTrue {
             sharedPrefs.edit().putInt(integerKey1.name, 123).putInt(integerKey2.name, 123).commit()
@@ -300,7 +304,7 @@
 
     @Test
     fun sharedPreferencesBackupFileDeleted() = runBlockingTest {
-        val integerKey = preferencesKey<Int>("integer_key")
+        val integerKey = intPreferencesKey("integer_key")
 
         // Write to shared preferences then create the backup file
         val sharedPrefsFile = getSharedPrefsFile(context, sharedPrefsName)
@@ -324,8 +328,8 @@
 
     @Test
     fun canSpecifyMultipleKeys() = runBlockingTest {
-        val stringKey = preferencesKey<String>("string_key")
-        val integerKey = preferencesKey<Int>("integer_key")
+        val stringKey = stringPreferencesKey("string_key")
+        val integerKey = intPreferencesKey("integer_key")
         val keyNotMigrated = "dont_migrate_this_key"
 
         val stringValue = "string_value"
@@ -359,7 +363,7 @@
 
     @Test
     fun missingSpecifiedKeyIsNotMigrated() = runBlockingTest {
-        val missingKey = preferencesKey<Int>("missing_key")
+        val missingKey = intPreferencesKey("missing_key")
 
         val migration = SharedPreferencesMigration(
             context = context,
@@ -376,8 +380,8 @@
 
     @Test
     fun runsIfAnySpecifiedKeyExists() = runBlockingTest {
-        val integerKey = preferencesKey<Int>("integer_key")
-        val missingKey = preferencesKey<Int>("missing_key")
+        val integerKey = intPreferencesKey("integer_key")
+        val missingKey = intPreferencesKey("missing_key")
 
         val integerValue = 123
 
diff --git a/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/SharedPreferencesMigration.kt b/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/SharedPreferencesMigration.kt
index ac471ae..b91abe3 100644
--- a/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/SharedPreferencesMigration.kt
+++ b/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/SharedPreferencesMigration.kt
@@ -20,8 +20,12 @@
 import androidx.datastore.migrations.SharedPreferencesView
 import androidx.datastore.migrations.SharedPreferencesMigration
 import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.core.toMutablePreferences
-import androidx.datastore.preferences.core.toPreferences
+import androidx.datastore.preferences.core.booleanPreferencesKey
+import androidx.datastore.preferences.core.floatPreferencesKey
+import androidx.datastore.preferences.core.intPreferencesKey
+import androidx.datastore.preferences.core.longPreferencesKey
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.core.stringSetPreferencesKey
 
 /**
  * Creates a SharedPreferencesMigration for DataStore<Preferences>.
@@ -72,36 +76,24 @@
             for ((key, value) in filteredSharedPreferences) {
                 when (value) {
                     is Boolean -> mutablePreferences[
-                        androidx.datastore.preferences.core.preferencesKey(
-                            key
-                        )
+                        booleanPreferencesKey(key)
                     ] = value
                     is Float -> mutablePreferences[
-                        androidx.datastore.preferences.core.preferencesKey(
-                            key
-                        )
+                        floatPreferencesKey(key)
                     ] = value
                     is Int -> mutablePreferences[
-                        androidx.datastore.preferences.core.preferencesKey(
-                            key
-                        )
+                        intPreferencesKey(key)
                     ] = value
                     is Long -> mutablePreferences[
-                        androidx.datastore.preferences.core.preferencesKey(
-                            key
-                        )
+                        longPreferencesKey(key)
                     ] = value
                     is String -> mutablePreferences[
-                        androidx.datastore.preferences.core.preferencesKey(
-                            key
-                        )
+                        stringPreferencesKey(key)
                     ] = value
                     is Set<*> -> {
                         @Suppress("UNCHECKED_CAST")
                         mutablePreferences[
-                            androidx.datastore.preferences.core.preferencesSetKey<String>(
-                                key
-                            )
+                            stringSetPreferencesKey(key)
                         ] = value as Set<String>
                     }
                 }
diff --git a/datastore/datastore-rxjava2/api/current.txt b/datastore/datastore-rxjava2/api/current.txt
index 42e32ea..b52a1fc 100644
--- a/datastore/datastore-rxjava2/api/current.txt
+++ b/datastore/datastore-rxjava2/api/current.txt
@@ -1,20 +1,37 @@
 // Signature format: 4.0
 package androidx.datastore.rxjava2 {
 
+  public interface RxDataMigration<T> {
+    method public io.reactivex.Completable cleanUp();
+    method public io.reactivex.Single<T!> migrate(T?);
+    method public io.reactivex.Single<java.lang.Boolean!> shouldMigrate(T?);
+  }
+
   public final class RxDataStore {
     method @kotlinx.coroutines.ExperimentalCoroutinesApi public static <T> io.reactivex.Flowable<T> data(androidx.datastore.core.DataStore<T>);
     method @kotlinx.coroutines.ExperimentalCoroutinesApi public static <T> io.reactivex.Single<T> updateDataAsync(androidx.datastore.core.DataStore<T>, io.reactivex.functions.Function<T,io.reactivex.Single<T>> transform);
   }
 
   public final class RxDataStoreBuilder<T> {
-    ctor public RxDataStoreBuilder();
+    ctor public RxDataStoreBuilder(java.util.concurrent.Callable<java.io.File> produceFile, androidx.datastore.core.Serializer<T> serializer);
+    ctor public RxDataStoreBuilder(android.content.Context context, String fileName, androidx.datastore.core.Serializer<T> serializer);
     method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> addDataMigration(androidx.datastore.core.DataMigration<T> dataMigration);
+    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> addRxDataMigration(androidx.datastore.rxjava2.RxDataMigration<T> rxDataMigration);
     method public androidx.datastore.core.DataStore<T> build();
     method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setCorruptionHandler(androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<T> corruptionHandler);
-    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setFileName(android.content.Context context, String fileName);
-    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setFileProducer(java.util.concurrent.Callable<java.io.File> produceFile);
     method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setIoScheduler(io.reactivex.Scheduler ioScheduler);
-    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setSerializer(androidx.datastore.core.Serializer<T> serializer);
+  }
+
+  public interface RxSharedPreferencesMigration<T> {
+    method public io.reactivex.Single<T> migrate(androidx.datastore.migrations.SharedPreferencesView sharedPreferencesView, T? currentData);
+    method public default io.reactivex.Single<java.lang.Boolean> shouldMigrate(T? currentData);
+  }
+
+  public final class RxSharedPreferencesMigrationBuilder<T> {
+    ctor public RxSharedPreferencesMigrationBuilder(android.content.Context context, String sharedPreferencesName, androidx.datastore.rxjava2.RxSharedPreferencesMigration<T> rxSharedPreferencesMigration);
+    method public androidx.datastore.core.DataMigration<T> build();
+    method public androidx.datastore.rxjava2.RxSharedPreferencesMigrationBuilder<T> setDeleteEmptyPreferences(boolean deleteEmptyPreferences);
+    method public androidx.datastore.rxjava2.RxSharedPreferencesMigrationBuilder<T> setKeysToMigrate(java.lang.String... keys);
   }
 
 }
diff --git a/datastore/datastore-rxjava2/api/public_plus_experimental_current.txt b/datastore/datastore-rxjava2/api/public_plus_experimental_current.txt
index 42e32ea..b52a1fc 100644
--- a/datastore/datastore-rxjava2/api/public_plus_experimental_current.txt
+++ b/datastore/datastore-rxjava2/api/public_plus_experimental_current.txt
@@ -1,20 +1,37 @@
 // Signature format: 4.0
 package androidx.datastore.rxjava2 {
 
+  public interface RxDataMigration<T> {
+    method public io.reactivex.Completable cleanUp();
+    method public io.reactivex.Single<T!> migrate(T?);
+    method public io.reactivex.Single<java.lang.Boolean!> shouldMigrate(T?);
+  }
+
   public final class RxDataStore {
     method @kotlinx.coroutines.ExperimentalCoroutinesApi public static <T> io.reactivex.Flowable<T> data(androidx.datastore.core.DataStore<T>);
     method @kotlinx.coroutines.ExperimentalCoroutinesApi public static <T> io.reactivex.Single<T> updateDataAsync(androidx.datastore.core.DataStore<T>, io.reactivex.functions.Function<T,io.reactivex.Single<T>> transform);
   }
 
   public final class RxDataStoreBuilder<T> {
-    ctor public RxDataStoreBuilder();
+    ctor public RxDataStoreBuilder(java.util.concurrent.Callable<java.io.File> produceFile, androidx.datastore.core.Serializer<T> serializer);
+    ctor public RxDataStoreBuilder(android.content.Context context, String fileName, androidx.datastore.core.Serializer<T> serializer);
     method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> addDataMigration(androidx.datastore.core.DataMigration<T> dataMigration);
+    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> addRxDataMigration(androidx.datastore.rxjava2.RxDataMigration<T> rxDataMigration);
     method public androidx.datastore.core.DataStore<T> build();
     method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setCorruptionHandler(androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<T> corruptionHandler);
-    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setFileName(android.content.Context context, String fileName);
-    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setFileProducer(java.util.concurrent.Callable<java.io.File> produceFile);
     method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setIoScheduler(io.reactivex.Scheduler ioScheduler);
-    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setSerializer(androidx.datastore.core.Serializer<T> serializer);
+  }
+
+  public interface RxSharedPreferencesMigration<T> {
+    method public io.reactivex.Single<T> migrate(androidx.datastore.migrations.SharedPreferencesView sharedPreferencesView, T? currentData);
+    method public default io.reactivex.Single<java.lang.Boolean> shouldMigrate(T? currentData);
+  }
+
+  public final class RxSharedPreferencesMigrationBuilder<T> {
+    ctor public RxSharedPreferencesMigrationBuilder(android.content.Context context, String sharedPreferencesName, androidx.datastore.rxjava2.RxSharedPreferencesMigration<T> rxSharedPreferencesMigration);
+    method public androidx.datastore.core.DataMigration<T> build();
+    method public androidx.datastore.rxjava2.RxSharedPreferencesMigrationBuilder<T> setDeleteEmptyPreferences(boolean deleteEmptyPreferences);
+    method public androidx.datastore.rxjava2.RxSharedPreferencesMigrationBuilder<T> setKeysToMigrate(java.lang.String... keys);
   }
 
 }
diff --git a/datastore/datastore-rxjava2/api/restricted_current.txt b/datastore/datastore-rxjava2/api/restricted_current.txt
index 42e32ea..b52a1fc 100644
--- a/datastore/datastore-rxjava2/api/restricted_current.txt
+++ b/datastore/datastore-rxjava2/api/restricted_current.txt
@@ -1,20 +1,37 @@
 // Signature format: 4.0
 package androidx.datastore.rxjava2 {
 
+  public interface RxDataMigration<T> {
+    method public io.reactivex.Completable cleanUp();
+    method public io.reactivex.Single<T!> migrate(T?);
+    method public io.reactivex.Single<java.lang.Boolean!> shouldMigrate(T?);
+  }
+
   public final class RxDataStore {
     method @kotlinx.coroutines.ExperimentalCoroutinesApi public static <T> io.reactivex.Flowable<T> data(androidx.datastore.core.DataStore<T>);
     method @kotlinx.coroutines.ExperimentalCoroutinesApi public static <T> io.reactivex.Single<T> updateDataAsync(androidx.datastore.core.DataStore<T>, io.reactivex.functions.Function<T,io.reactivex.Single<T>> transform);
   }
 
   public final class RxDataStoreBuilder<T> {
-    ctor public RxDataStoreBuilder();
+    ctor public RxDataStoreBuilder(java.util.concurrent.Callable<java.io.File> produceFile, androidx.datastore.core.Serializer<T> serializer);
+    ctor public RxDataStoreBuilder(android.content.Context context, String fileName, androidx.datastore.core.Serializer<T> serializer);
     method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> addDataMigration(androidx.datastore.core.DataMigration<T> dataMigration);
+    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> addRxDataMigration(androidx.datastore.rxjava2.RxDataMigration<T> rxDataMigration);
     method public androidx.datastore.core.DataStore<T> build();
     method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setCorruptionHandler(androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<T> corruptionHandler);
-    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setFileName(android.content.Context context, String fileName);
-    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setFileProducer(java.util.concurrent.Callable<java.io.File> produceFile);
     method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setIoScheduler(io.reactivex.Scheduler ioScheduler);
-    method public androidx.datastore.rxjava2.RxDataStoreBuilder<T> setSerializer(androidx.datastore.core.Serializer<T> serializer);
+  }
+
+  public interface RxSharedPreferencesMigration<T> {
+    method public io.reactivex.Single<T> migrate(androidx.datastore.migrations.SharedPreferencesView sharedPreferencesView, T? currentData);
+    method public default io.reactivex.Single<java.lang.Boolean> shouldMigrate(T? currentData);
+  }
+
+  public final class RxSharedPreferencesMigrationBuilder<T> {
+    ctor public RxSharedPreferencesMigrationBuilder(android.content.Context context, String sharedPreferencesName, androidx.datastore.rxjava2.RxSharedPreferencesMigration<T> rxSharedPreferencesMigration);
+    method public androidx.datastore.core.DataMigration<T> build();
+    method public androidx.datastore.rxjava2.RxSharedPreferencesMigrationBuilder<T> setDeleteEmptyPreferences(boolean deleteEmptyPreferences);
+    method public androidx.datastore.rxjava2.RxSharedPreferencesMigrationBuilder<T> setKeysToMigrate(java.lang.String... keys);
   }
 
 }
diff --git a/datastore/datastore-rxjava2/build.gradle b/datastore/datastore-rxjava2/build.gradle
index 8bc10fe..83003c2 100644
--- a/datastore/datastore-rxjava2/build.gradle
+++ b/datastore/datastore-rxjava2/build.gradle
@@ -27,18 +27,12 @@
 }
 
 android {
-    buildTypes{
-        debug {
-            multiDexEnabled = true
-        }
-    }
-
     sourceSets {
         test.java.srcDirs += 'src/test-common/java'
+        androidTest.java.srcDirs += 'src/test-common/java'
     }
 }
 
-
 dependencies {
     api(KOTLIN_STDLIB)
     api(KOTLIN_COROUTINES_CORE)
@@ -53,6 +47,11 @@
     testImplementation(KOTLIN_COROUTINES_TEST)
     testImplementation(TRUTH)
     testImplementation(project(":internal-testutils-truth"))
+
+    androidTestImplementation(JUNIT)
+    androidTestImplementation(project(":internal-testutils-truth"))
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(ANDROIDX_TEST_CORE)
 }
 
 androidx {
@@ -63,10 +62,3 @@
     description = "Android DataStore Core - contains wrappers for using DataStore using RxJava2"
     legacyDisableKotlinStrictApiMode = true
 }
-
-// Allow usage of Kotlin's @OptIn.
-tasks.withType(KotlinCompile).configureEach {
-    kotlinOptions {
-        freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn"]
-    }
-}
\ No newline at end of file
diff --git a/car/app/app/src/androidTest/AndroidManifest.xml b/datastore/datastore-rxjava2/src/androidTest/AndroidManifest.xml
similarity index 94%
rename from car/app/app/src/androidTest/AndroidManifest.xml
rename to datastore/datastore-rxjava2/src/androidTest/AndroidManifest.xml
index 3bc2684..bf9d5c2 100644
--- a/car/app/app/src/androidTest/AndroidManifest.xml
+++ b/datastore/datastore-rxjava2/src/androidTest/AndroidManifest.xml
@@ -15,5 +15,6 @@
   limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.car.app">
+    package="androidx.datastore.rxjava2">
+
 </manifest>
diff --git a/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/AssertThrows.kt b/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/AssertThrows.kt
new file mode 100644
index 0000000..cc1f3493
--- /dev/null
+++ b/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/AssertThrows.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.datastore.rxjava2
+
+@Suppress("UNCHECKED_CAST")
+internal fun <T : Throwable?> assertThrows(
+    expectedType: Class<T>,
+    runnable: Runnable
+): T {
+    try {
+        runnable.run()
+    } catch (t: Throwable) {
+        if (!expectedType.isInstance(t)) {
+            throw RuntimeException(t)
+        }
+        return t as T
+    }
+    throw AssertionError(
+        String.format(
+            "Expected %s wasn't thrown",
+            expectedType.simpleName
+        )
+    )
+}
diff --git a/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/RxDataStoreBuilderTest.java b/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/RxDataStoreBuilderTest.java
new file mode 100644
index 0000000..2d3a0ae
--- /dev/null
+++ b/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/RxDataStoreBuilderTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.datastore.rxjava2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.datastore.core.DataStore;
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import io.reactivex.Completable;
+import io.reactivex.Scheduler;
+import io.reactivex.Single;
+import io.reactivex.schedulers.Schedulers;
+
+public class RxDataStoreBuilderTest {
+    @Rule
+    public TemporaryFolder tempFolder = new TemporaryFolder();
+
+    private static Single<Byte> incrementByte(Byte byteIn) {
+        return Single.just(++byteIn);
+    }
+
+    @Test
+    public void testConstructWithProduceFile() throws Exception {
+        File file = tempFolder.newFile();
+        DataStore<Byte> dataStore =
+                new RxDataStoreBuilder<Byte>(() -> file, new TestingSerializer())
+                        .build();
+        Single<Byte> incrementByte = RxDataStore.updateDataAsync(dataStore,
+                RxDataStoreBuilderTest::incrementByte);
+        assertThat(incrementByte.blockingGet()).isEqualTo(1);
+        // Construct it again and confirm that the data is still there:
+        dataStore =
+                new RxDataStoreBuilder<Byte>(() -> file, new TestingSerializer())
+                        .build();
+        assertThat(RxDataStore.data(dataStore).blockingFirst()).isEqualTo(1);
+    }
+
+    @Test
+    public void testConstructWithContextAndName() throws Exception {
+        Context context = ApplicationProvider.getApplicationContext();
+        String name = "my_data_store";
+        DataStore<Byte> dataStore =
+                new RxDataStoreBuilder<Byte>(context, name, new TestingSerializer())
+                        .build();
+        Single<Byte> set1 = RxDataStore.updateDataAsync(dataStore, input -> Single.just((byte) 1));
+        assertThat(set1.blockingGet()).isEqualTo(1);
+        // Construct it again and confirm that the data is still there:
+        dataStore =
+                new RxDataStoreBuilder<Byte>(context, name, new TestingSerializer())
+                        .build();
+        assertThat(RxDataStore.data(dataStore).blockingFirst()).isEqualTo(1);
+        // Construct it again with the expected file path and confirm that the data is there:
+        dataStore =
+                new RxDataStoreBuilder<Byte>(() -> new File(context.getFilesDir().getPath()
+                        + "/datastore/" + name), new TestingSerializer()
+                )
+                        .build();
+        assertThat(RxDataStore.data(dataStore).blockingFirst()).isEqualTo(1);
+    }
+
+    @Test
+    public void testMigrationsAreInstalledAndRun() throws Exception {
+        RxDataMigration<Byte> plusOneMigration = new RxDataMigration<Byte>() {
+            @NonNull
+            @Override
+            public Single<Boolean> shouldMigrate(@NonNull Byte currentData) {
+                return Single.just(true);
+            }
+
+            @NonNull
+            @Override
+            public Single<Byte> migrate(@NonNull Byte currentData) {
+                return incrementByte(currentData);
+            }
+
+            @NonNull
+            @Override
+            public Completable cleanUp() {
+                return Completable.complete();
+            }
+        };
+
+        DataStore<Byte> dataStore = new RxDataStoreBuilder<Byte>(
+                () -> tempFolder.newFile(), new TestingSerializer())
+                .addRxDataMigration(plusOneMigration)
+                .build();
+
+        assertThat(RxDataStore.data(dataStore).blockingFirst()).isEqualTo(1);
+    }
+
+    @Test
+    public void testSpecifiedSchedulerIsUser() throws Exception {
+        Scheduler singleThreadedScheduler =
+                Schedulers.from(Executors.newSingleThreadExecutor(new ThreadFactory() {
+                    @Override
+                    public Thread newThread(Runnable r) {
+                        return new Thread(r, "TestingThread");
+                    }
+                }));
+
+
+        DataStore<Byte> dataStore = new RxDataStoreBuilder<Byte>(() -> tempFolder.newFile(),
+                new TestingSerializer())
+                .setIoScheduler(singleThreadedScheduler)
+                .build();
+        Single<Byte> update = RxDataStore.updateDataAsync(dataStore, input -> {
+            Thread currentThread = Thread.currentThread();
+            assertThat(currentThread.getName()).isEqualTo("TestingThread");
+            return Single.just(input);
+        });
+        assertThat(update.blockingGet()).isEqualTo((byte) 0);
+        Single<Byte> subsequentUpdate = RxDataStore.updateDataAsync(dataStore, input -> {
+            Thread currentThread = Thread.currentThread();
+            assertThat(currentThread.getName()).isEqualTo("TestingThread");
+            return Single.just(input);
+        });
+        assertThat(subsequentUpdate.blockingGet()).isEqualTo((byte) 0);
+    }
+
+    @Test
+    public void testCorruptionHandlerIsUser() {
+        TestingSerializer testingSerializer = new TestingSerializer();
+        testingSerializer.setFailReadWithCorruptionException(true);
+        ReplaceFileCorruptionHandler<Byte> replaceFileCorruptionHandler =
+                new ReplaceFileCorruptionHandler<Byte>(exception -> (byte) 99);
+
+
+        DataStore<Byte> dataStore = new RxDataStoreBuilder<Byte>(
+                () -> tempFolder.newFile(),
+                testingSerializer)
+                .setCorruptionHandler(replaceFileCorruptionHandler)
+                .build();
+        assertThat(RxDataStore.data(dataStore).blockingFirst()).isEqualTo(99);
+    }
+}
diff --git a/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/RxSharedPreferencesMigrationTest.java b/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/RxSharedPreferencesMigrationTest.java
new file mode 100644
index 0000000..c8e0b79
--- /dev/null
+++ b/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/RxSharedPreferencesMigrationTest.java
@@ -0,0 +1,207 @@
+/*
+ * 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.datastore.rxjava2;
+
+import static androidx.testutils.AssertionsKt.assertThrows;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.datastore.core.DataMigration;
+import androidx.datastore.core.DataStore;
+import androidx.datastore.migrations.SharedPreferencesView;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+
+import io.reactivex.Single;
+
+public class RxSharedPreferencesMigrationTest {
+    @Rule
+    public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+    private final String mSharedPrefsName = "shared_prefs_name";
+
+
+    private Context mContext;
+    private SharedPreferences mSharedPrefs;
+    private File mDatastoreFile;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = ApplicationProvider.getApplicationContext();
+        mSharedPrefs = mContext.getSharedPreferences(mSharedPrefsName, Context.MODE_PRIVATE);
+        mDatastoreFile = temporaryFolder.newFile("test_file.preferences_pb");
+
+        assertThat(mSharedPrefs.edit().clear().commit()).isTrue();
+    }
+
+    @Test
+    public void testShouldMigrateSkipsMigration() {
+        RxSharedPreferencesMigration<Byte> skippedMigration =
+                new RxSharedPreferencesMigration<Byte>() {
+                    @NotNull
+                    @Override
+                    public Single<Boolean> shouldMigrate(Byte currentData) {
+                        return Single.just(false);
+                    }
+
+                    @NotNull
+                    @Override
+                    public Single<Byte> migrate(
+                            @NotNull SharedPreferencesView sharedPreferencesView,
+                            Byte currentData) {
+                        return Single.error(
+                                new IllegalStateException("We shouldn't reach this point!"));
+                    }
+                };
+
+
+        DataMigration<Byte> spMigration =
+                getSpMigrationBuilder(skippedMigration).build();
+
+        DataStore<Byte> dataStoreWithMigrations = getDataStoreWithMigration(spMigration);
+
+        assertThat(RxDataStore.data(dataStoreWithMigrations).blockingFirst()).isEqualTo(0);
+    }
+
+    @Test
+    public void testSharedPrefsViewContainsSpecifiedKeys() {
+        String includedKey = "key1";
+        int includedVal = 99;
+        String notMigratedKey = "key2";
+
+        assertThat(mSharedPrefs.edit().putInt(includedKey, includedVal).putInt(notMigratedKey,
+                123).commit()).isTrue();
+
+        DataMigration<Byte> dataMigration =
+                getSpMigrationBuilder(
+                        new DefaultMigration() {
+                            @NotNull
+                            @Override
+                            public Single<Byte> migrate(
+                                    @NotNull SharedPreferencesView sharedPreferencesView,
+                                    Byte currentData) {
+                                assertThat(sharedPreferencesView.contains(includedKey)).isTrue();
+                                assertThat(sharedPreferencesView.getAll().size()).isEqualTo(1);
+                                assertThrows(IllegalStateException.class,
+                                        () -> sharedPreferencesView.getInt(notMigratedKey, -1));
+
+                                return Single.just((byte) 50);
+                            }
+                        }
+                ).setKeysToMigrate(includedKey).build();
+
+        DataStore<Byte> byteStore = getDataStoreWithMigration(dataMigration);
+
+        assertThat(RxDataStore.data(byteStore).blockingFirst()).isEqualTo(50);
+
+        assertThat(mSharedPrefs.contains(includedKey)).isFalse();
+        assertThat(mSharedPrefs.contains(notMigratedKey)).isTrue();
+    }
+
+
+    @Test
+    public void testSharedPrefsViewWithAllKeysSpecified() {
+        String includedKey = "key1";
+        String includedKey2 = "key2";
+        int value = 99;
+
+        assertThat(mSharedPrefs.edit().putInt(includedKey, value).putInt(includedKey2,
+                value).commit()).isTrue();
+
+        DataMigration<Byte> dataMigration =
+                getSpMigrationBuilder(
+                        new DefaultMigration() {
+                            @NotNull
+                            @Override
+                            public Single<Byte> migrate(
+                                    @NotNull SharedPreferencesView sharedPreferencesView,
+                                    Byte currentData) {
+                                assertThat(sharedPreferencesView.contains(includedKey)).isTrue();
+                                assertThat(sharedPreferencesView.contains(includedKey2)).isTrue();
+                                assertThat(sharedPreferencesView.getAll().size()).isEqualTo(2);
+
+                                return Single.just((byte) 50);
+                            }
+                        }
+                ).build();
+
+        DataStore<Byte> byteStore = getDataStoreWithMigration(dataMigration);
+
+        assertThat(RxDataStore.data(byteStore).blockingFirst()).isEqualTo(50);
+
+        assertThat(mSharedPrefs.contains(includedKey)).isFalse();
+        assertThat(mSharedPrefs.contains(includedKey2)).isFalse();
+    }
+
+    @Test
+    public void testDeletesEmptySharedPreferences() {
+        String key = "key";
+        String value = "value";
+        assertThat(mSharedPrefs.edit().putString(key, value).commit()).isTrue();
+
+        DataMigration<Byte> dataMigration =
+                getSpMigrationBuilder(new DefaultMigration()).setDeleteEmptyPreferences(
+                        true).build();
+        DataStore<Byte> byteStore = getDataStoreWithMigration(dataMigration);
+        assertThat(RxDataStore.data(byteStore).blockingFirst()).isEqualTo(0);
+
+        // Check that the shared preferences files are deleted
+        File prefsDir = new File(mContext.getApplicationInfo().dataDir, "shared_prefs");
+        File prefsFile = new File(prefsDir, mSharedPrefsName + ".xml");
+        File backupPrefsFile = new File(prefsFile.getPath() + ".bak");
+        assertThat(prefsFile.exists()).isFalse();
+        assertThat(backupPrefsFile.exists()).isFalse();
+    }
+
+    private RxSharedPreferencesMigrationBuilder<Byte> getSpMigrationBuilder(
+            RxSharedPreferencesMigration<Byte> rxSharedPreferencesMigration) {
+        return new RxSharedPreferencesMigrationBuilder<Byte>(mContext, mSharedPrefsName,
+                rxSharedPreferencesMigration);
+    }
+
+    private DataStore<Byte> getDataStoreWithMigration(DataMigration<Byte> dataMigration) {
+        return new RxDataStoreBuilder<Byte>(() -> mDatastoreFile, new TestingSerializer())
+                .addDataMigration(dataMigration).build();
+    }
+
+
+    private static class DefaultMigration implements RxSharedPreferencesMigration<Byte> {
+
+        @NotNull
+        @Override
+        public Single<Boolean> shouldMigrate(Byte currentData) {
+            return Single.just(true);
+        }
+
+        @NotNull
+        @Override
+        public Single<Byte> migrate(@NotNull SharedPreferencesView sharedPreferencesView,
+                Byte currentData) {
+            return Single.just(currentData);
+        }
+    }
+}
diff --git a/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataMigration.java b/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataMigration.java
new file mode 100644
index 0000000..79df6a1
--- /dev/null
+++ b/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataMigration.java
@@ -0,0 +1,79 @@
+/*
+ * 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.datastore.rxjava2;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import io.reactivex.Completable;
+import io.reactivex.Single;
+
+/**
+ * Interface for migrations to DataStore. Methods on this migration ([shouldMigrate], [migrate]
+ * and [cleanUp]) may be called multiple times, so their implementations must be idempotent.
+ * These methods may be called multiple times if DataStore encounters issues when writing the
+ * newly migrated data to disk or if any migration installed in the same DataStore throws an
+ * Exception.
+ *
+ * If you're migrating from SharedPreferences see [SharedPreferencesMigration].
+ *
+ * @param <T> the exception type
+ */
+public interface RxDataMigration<T> {
+
+    /**
+     * Return whether this migration needs to be performed. If this returns false, no migration or
+     * cleanup will occur. Apps should do the cheapest possible check to determine if this migration
+     * should run, since this will be called every time the DataStore is initialized. This method
+     * may be run multiple times when any failure is encountered.
+     *
+     * Note that this will always be called before each call to [migrate].
+     *
+     * @param currentData the current data (which might already populated from previous runs of this
+     *                    or other migrations). Only Nullable if the type used with DataStore is
+     *                    Nullable.
+     */
+    @NonNull
+    Single<Boolean> shouldMigrate(@Nullable T currentData);
+
+    /**
+     * Perform the migration. Implementations should be idempotent since this may be called
+     * multiple times. If migrate fails, DataStore will not commit any data to disk, cleanUp will
+     * not be called, and the exception will be propagated back to the DataStore call that
+     * triggered the migration. Future calls to DataStore will result in DataMigrations being
+     * attempted again. This method may be run multiple times when any failure is encountered.
+     *
+     * Note that this will always be called before a call to [cleanUp].
+     *
+     * @param currentData the current data (it might be populated from other migrations or from
+     *                    manual changes before this migration was added to the app). Only
+     *                    Nullable if the type used with DataStore is Nullable.
+     * @return The migrated data.
+     */
+    @NonNull
+    Single<T> migrate(@Nullable T currentData);
+
+    /**
+     * Clean up any old state/data that was migrated into the DataStore. This will not be called
+     * if the migration fails. If cleanUp throws an exception, the exception will be propagated
+     * back to the DataStore call that triggered the migration and future calls to DataStore will
+     * result in DataMigrations being attempted again. This method may be run multiple times when
+     * any failure is encountered.
+     */
+    @NonNull
+    Completable cleanUp();
+}
diff --git a/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataStoreBuilder.kt b/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataStoreBuilder.kt
index 1685286..070d0a1c 100644
--- a/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataStoreBuilder.kt
+++ b/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataStoreBuilder.kt
@@ -28,6 +28,7 @@
 import io.reactivex.schedulers.Schedulers
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.rx2.asCoroutineDispatcher
+import kotlinx.coroutines.rx2.await
 import java.io.File
 import java.util.concurrent.Callable
 
@@ -35,15 +36,49 @@
  * RxSharedPreferencesMigrationBuilder class for a DataStore that works on a single process.
  */
 @SuppressLint("TopLevelBuilder")
-public class RxDataStoreBuilder<T>() {
+public class RxDataStoreBuilder<T> {
 
-    // Either produceFile or context & name must be set, but not both.
+    /**
+     * Create a RxDataStoreBuilder with the callable which returns the File that DataStore acts on.
+     * The user is responsible for ensuring that there is never more than one DataStore acting on
+     * a file at a time.
+     *
+     * @param produceFile Function which returns the file that the new DataStore will act on. The
+     * function must return the same path every time. No two instances of DataStore should act on
+     * the same file at the same time.
+     * @param serializer the serializer for the type that this DataStore acts on.
+     */
+    public constructor(produceFile: Callable<File>, serializer: Serializer<T>) {
+        this.produceFile = produceFile
+        this.serializer = serializer
+    }
+
+    /**
+     * Create a RxDataStoreBuilder with the Context and name from which to derive the DataStore
+     * file. The file is generated by See [Context.createDataStore] for more info. The user is
+     * responsible for ensuring that there is never more than one DataStore acting on a file at a
+     * time.
+     *
+     * @param context the context from which we retrieve files directory.
+     * @param fileName the filename relative to Context.filesDir that DataStore acts on. The File is
+     * obtained by calling File(context.filesDir, fileName). No two instances of DataStore should
+     * act on the same file at the same time.
+     * @param serializer the serializer for the type that this DataStore acts on.
+     */
+    public constructor(context: Context, fileName: String, serializer: Serializer<T>) {
+        this.context = context
+        this.name = fileName
+        this.serializer = serializer
+    }
+
+    // Either produceFile or context & name must be set, but not both. This is enforced by the
+    // two constructors.
     private var produceFile: Callable<File>? = null
 
     private var context: Context? = null
     private var name: String? = null
 
-    // Required
+    // Required. This is enforced by the constructors.
     private var serializer: Serializer<T>? = null
 
     // Optional
@@ -52,61 +87,6 @@
     private val dataMigrations: MutableList<DataMigration<T>> = mutableListOf()
 
     /**
-     * Set the callable which returns the File that DataStore acts on. The user is responsible for
-     * ensuring that there is never more than one DataStore acting on a file at a time.
-     *
-     * It is required to call either this method or [setFileName] before calling [build].
-     *
-     *
-     * @param produceFile Function which returns the file that the new DataStore will act on. The
-     * function must return the same path every time. No two instances of DataStore should act on
-     * the same file at the same time.
-     * @throws IllegalStateException if context and name are already set
-     * @return this
-     */
-    @Suppress("MissingGetterMatchingBuilder")
-    public fun setFileProducer(produceFile: Callable<File>): RxDataStoreBuilder<T> = apply {
-        check(context == null) { "Only call setFileProducer or setContextAndName" }
-        check(name == null) { "Only call setFileProducer or setContextAndName" }
-        this.produceFile = produceFile
-    }
-
-    /**
-     * Set the Context and name from which to derive the DataStore file. The file is generated by
-     * See [Context.createDataStore] for more info. The user is responsible for ensuring that
-     * there is never more than one DataStore acting on a file at a time.
-     *
-     * It is required to call either this method or [setFileProducer] before calling [build].
-     *
-     * @param context the context from which we retrieve files directory.
-     * @param fileName the filename relative to Context.filesDir that DataStore acts on. The File is
-     * obtained by calling File(context.filesDir, fileName). No two instances of DataStore should
-     * act on the same file at the same time.
-     * @throws IllegalStateException if produceFile is already set
-     * @return this
-     */
-    @Suppress("MissingGetterMatchingBuilder")
-    public fun setFileName(context: Context, fileName: String): RxDataStoreBuilder<T> =
-        apply {
-            check(produceFile == null) { "Only call setFileProducer or setContextAndName" }
-            this.context = context
-            this.name = fileName
-        }
-
-    /**
-     * Set the serializer that this DataStore acts on.
-     *
-     * This parameter is required.
-     *
-     * @param serializer the serializer for the type that this DataStore acts on.
-     * @return this
-     */
-    @Suppress("MissingGetterMatchingBuilder")
-    public fun setSerializer(serializer: Serializer<T>): RxDataStoreBuilder<T> = apply {
-        this.serializer = serializer
-    }
-
-    /**
      * Set the Scheduler on which to perform IO and transform operations. This is converted into
      * a CoroutineDispatcher before being added to DataStore.
      *
@@ -132,6 +112,18 @@
         RxDataStoreBuilder<T> = apply { this.corruptionHandler = corruptionHandler }
 
     /**
+     * Add an RxDataMigration to the DataStore. Migrations are run in the order they are added.
+     *
+     * @param rxDataMigration the migration to add.
+     * @return this
+     */
+    @Suppress("MissingGetterMatchingBuilder")
+    public fun addRxDataMigration(rxDataMigration: RxDataMigration<T>): RxDataStoreBuilder<T> =
+        apply {
+            this.dataMigrations.add(DataMigrationFromRxDataMigration(rxDataMigration))
+        }
+
+    /**
      * Add a DataMigration to the Datastore. Migrations are run in the order they are added.
      *
      * @param dataMigration the migration to add
@@ -145,15 +137,9 @@
     /**
      * Build the DataStore.
      *
-     * @throws IllegalStateException if serializer is not set or if neither produceFile not
-     * context and name are set.
      * @return the DataStore with the provided parameters
      */
     public fun build(): DataStore<T> {
-        check(serializer != null) {
-            "Serializer must be set."
-        }
-
         val scope = CoroutineScope(ioScheduler.asCoroutineDispatcher())
 
         return if (produceFile != null) {
@@ -175,7 +161,24 @@
                 migrations = dataMigrations
             )
         } else {
-            throw IllegalStateException("Either produceFile or context and name must be set.")
+            error(
+                "Either produceFile or context and name must be set. This should never happen."
+            )
         }
     }
 }
+
+internal class DataMigrationFromRxDataMigration<T>(private val migration: RxDataMigration<T>) :
+    DataMigration<T> {
+    override suspend fun shouldMigrate(currentData: T): Boolean {
+        return migration.shouldMigrate(currentData).await()
+    }
+
+    override suspend fun migrate(currentData: T): T {
+        return migration.migrate(currentData).await()
+    }
+
+    override suspend fun cleanUp() {
+        migration.cleanUp().await()
+    }
+}
diff --git a/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxSharedPreferencesMigration.kt b/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxSharedPreferencesMigration.kt
new file mode 100644
index 0000000..e84e377
--- /dev/null
+++ b/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxSharedPreferencesMigration.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.datastore.rxjava2
+
+import android.annotation.SuppressLint
+import android.content.Context
+import androidx.datastore.core.DataMigration
+import androidx.datastore.migrations.SharedPreferencesMigration
+import androidx.datastore.migrations.SharedPreferencesView
+import io.reactivex.Single
+import kotlinx.coroutines.rx2.await
+
+/**
+ * Client implemented migration interface.
+ **/
+public interface RxSharedPreferencesMigration<T> {
+    /**
+     * Whether or not the migration should be run. This can be used to skip a read from the
+     * SharedPreferences.
+     *
+     * @param currentData the most recently persisted data
+     * @return a Single indicating whether or not the migration should be run.
+     */
+    public fun shouldMigrate(currentData: T): Single<Boolean> {
+        return Single.just(true)
+    }
+
+    /**
+     * Maps SharedPreferences into T. Implementations should be idempotent
+     * since this may be called multiple times. See [DataMigration.migrate] for more
+     * information. The method accepts a SharedPreferencesView which is the view of the
+     * SharedPreferences to migrate from (limited to [keysToMigrate] and a T which represent
+     * the current data. The function must return the migrated data.
+     *
+     * @param sharedPreferencesView the current state of the SharedPreferences
+     * @param currentData the most recently persisted data
+     * @return a Single of the updated data
+     */
+    public fun migrate(sharedPreferencesView: SharedPreferencesView, currentData: T): Single<T>
+}
+
+/**
+ * RxSharedPreferencesMigrationBuilder for the RxSharedPreferencesMigration.
+ */
+@SuppressLint("TopLevelBuilder")
+public class RxSharedPreferencesMigrationBuilder<T>
+/**
+ * Construct a RxSharedPreferencesMigrationBuilder.
+ *
+ * @param context the Context used for getting the SharedPreferences.
+ * @param sharedPreferencesName the name of the SharedPreference from which to migrate.
+ * @param rxSharedPreferencesMigration the user implemented migration for this SharedPreference.
+ */
+constructor(
+    private val context: Context,
+    private val sharedPreferencesName: String,
+    private val rxSharedPreferencesMigration: RxSharedPreferencesMigration<T>
+) {
+
+    /** Optional */
+    private var deleteEmptyPreference: Boolean = true
+    private var keysToMigrate: Set<String>? = null
+
+    /**
+     * Set the list of keys to migrate. The keys will be mapped to datastore.Preferences with
+     * their same values. If the key is already present in the new Preferences, the key
+     * will not be migrated again. If the key is not present in the SharedPreferences it
+     * will not be migrated.
+     *
+     * This method is optional and if keysToMigrate is not set, all keys will be migrated from the
+     * existing SharedPreferences.
+     *
+     * @param keys the keys to migrate
+     * @return this
+     */
+    @Suppress("MissingGetterMatchingBuilder")
+    public fun setKeysToMigrate(vararg keys: String):
+        RxSharedPreferencesMigrationBuilder<T> = apply {
+            keysToMigrate = setOf(*keys)
+        }
+
+    /**
+     * If enabled and the SharedPreferences are empty (i.e. no remaining
+     * keys) after this migration runs, the leftover SharedPreferences file is deleted. Note that
+     * this cleanup runs only if the migration itself runs, i.e., if the keys were never in
+     * SharedPreferences to begin with then the (potentially) empty SharedPreferences
+     * won't be cleaned up by this option. This functionality is best effort - if there
+     * is an issue deleting the SharedPreferences file it will be silently ignored.
+     *
+     * This method is optional and defaults to true.
+     *
+     * @param deleteEmptyPreferences whether or not to delete the empty shared preferences file
+     * @return this
+     */
+    @Suppress("MissingGetterMatchingBuilder")
+    public fun setDeleteEmptyPreferences(deleteEmptyPreferences: Boolean):
+        RxSharedPreferencesMigrationBuilder<T> = apply {
+            this.deleteEmptyPreference = deleteEmptyPreferences
+        }
+
+    public fun build(): DataMigration<T> {
+        return SharedPreferencesMigration(
+            context = context,
+            sharedPreferencesName = sharedPreferencesName,
+            migrate = { spView, curData ->
+                rxSharedPreferencesMigration.migrate(spView, curData).await()
+            },
+            keysToMigrate = keysToMigrate,
+            deleteEmptyPreferences = deleteEmptyPreference,
+            shouldRunMigration = { curData ->
+                rxSharedPreferencesMigration.shouldMigrate(curData).await()
+            }
+        )
+    }
+}
diff --git a/datastore/datastore-sampleapp/src/main/java/com/example/datastoresampleapp/PreferencesDataStoreActivity.kt b/datastore/datastore-sampleapp/src/main/java/com/example/datastoresampleapp/PreferencesDataStoreActivity.kt
index 6d64419..1e506a1 100644
--- a/datastore/datastore-sampleapp/src/main/java/com/example/datastoresampleapp/PreferencesDataStoreActivity.kt
+++ b/datastore/datastore-sampleapp/src/main/java/com/example/datastoresampleapp/PreferencesDataStoreActivity.kt
@@ -28,7 +28,7 @@
 import androidx.datastore.preferences.createDataStore
 import androidx.datastore.preferences.core.edit
 import androidx.datastore.preferences.core.emptyPreferences
-import androidx.datastore.preferences.core.preferencesKey
+import androidx.datastore.preferences.core.intPreferencesKey
 import androidx.lifecycle.lifecycleScope
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.collect
@@ -42,7 +42,7 @@
     private val TAG = "PreferencesActivity"
 
     private val PREFERENCE_STORE_FILE_NAME = "datastore_test_app"
-    private val COUNTER_KEY = preferencesKey<Int>("counter")
+    private val COUNTER_KEY = intPreferencesKey("counter")
 
     private val preferenceStore: DataStore<Preferences> by lazy {
         applicationContext.createDataStore(PREFERENCE_STORE_FILE_NAME)
diff --git a/development/aosp.sh b/development/aosp.sh
index 83e6a9b..32c4c12 100755
--- a/development/aosp.sh
+++ b/development/aosp.sh
@@ -18,7 +18,7 @@
   repo download "platform/frameworks/support" ${REPO_ID:1:-1}  \
     && BRANCH=`git log -1 | grep Change-Id | awk '{print $2}'` \
     && git checkout -B "aosp/$BRANCH"                          \
-    && git branch --set-upstream-to=aosp/androidx-master-dev
+    && git branch --set-upstream-to=aosp/androidx-main
 }
 
 function fn_aosp_merged {
@@ -89,7 +89,7 @@
   && repo download "platform/frameworks/support" "$PATCH"    \
   && BRANCH=`git log -1 | grep Change-Id | awk '{print $2}'` \
   && git checkout -B "`whoami`/$BRANCH"                     \
-  && git branch --set-upstream-to=aosp/androidx-master-dev
+  && git branch --set-upstream-to=aosp/androidx-main
 elif [[ "$1" == "r" ]]; then
   fn_git_check_uncommitted_changes
 
diff --git a/development/build_log_processor.sh b/development/build_log_processor.sh
index 9802bf4..15a6082 100755
--- a/development/build_log_processor.sh
+++ b/development/build_log_processor.sh
@@ -86,8 +86,6 @@
     if $SCRIPT_PATH/build_log_simplifier.py --validate $logFile >&2; then
       echo No unrecognized messages found in build log
     else
-      echo >&2
-      echo "Build log validation, enabled by the argument $validateArgument, failed" >&2
       exit 1
     fi
   fi
diff --git a/development/build_log_simplifier/VALIDATION_FAILURE.md b/development/build_log_simplifier/VALIDATION_FAILURE.md
index d02c57c..d0f8519 100644
--- a/development/build_log_simplifier/VALIDATION_FAILURE.md
+++ b/development/build_log_simplifier/VALIDATION_FAILURE.md
@@ -12,13 +12,13 @@
 
 ##   Where is the baseline file?
 
-   *   Exemptions for output that is always/deterministically generated by the build go in [messages.ignore](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/development/build_log_simplifier/messages.ignore)
+   *   Exemptions for output that is always/deterministically generated by the build go in [messages.ignore](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/development/build_log_simplifier/messages.ignore)
 
-       *   We may periodically garbage collect exemptions in this file via [gc.sh](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/development/build_log_simplifier/gc.sh), so they may be automatically removed after their underlying issue is fixed
+       *   We may periodically garbage collect exemptions in this file via [gc.sh](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/development/build_log_simplifier/gc.sh), so they may be automatically removed after their underlying issue is fixed
 
        *   In most cases, this will be the correct file to update
 
-   *   Exemptions for output that is only sometimes (flakily) generated by the build go in [message-flakes.ignore](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/development/build_log_simplifier/message-flakes.ignore)
+   *   Exemptions for output that is only sometimes (flakily) generated by the build go in [message-flakes.ignore](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/development/build_log_simplifier/message-flakes.ignore)
 
        *   These exemptions will not be automatically garbage collected
 
diff --git a/development/build_log_simplifier/build_log_simplifier.py b/development/build_log_simplifier/build_log_simplifier.py
index 34d6e9d..569b5a1 100755
--- a/development/build_log_simplifier/build_log_simplifier.py
+++ b/development/build_log_simplifier/build_log_simplifier.py
@@ -146,46 +146,6 @@
             prev_line_is_boring = False
     return result
 
-def remove_known_uninteresting_lines(lines):
-  skipLines = {
-      "A fine-grained performance profile is available: use the --scan option.",
-      "* Get more help at https://help.gradle.org",
-      "Use '--warning-mode all' to show the individual deprecation warnings.",
-      "See https://docs.gradle.org/6.5/userguide/command_line_interface.html#sec:command_line_warnings",
-
-      "Note: Some input files use or override a deprecated API.",
-      "Note: Recompile with -Xlint:deprecation for details.",
-      "Note: Some input files use unchecked or unsafe operations.",
-      "Note: Recompile with -Xlint:unchecked for details.",
-
-      "w: ATTENTION!",
-      "This build uses unsafe internal compiler arguments:",
-      "-XXLanguage:-NewInference",
-      "-XXLanguage:+InlineClasses",
-      "This mode is not recommended for production use,",
-      "as no stability/compatibility guarantees are given on",
-      "compiler or generated code. Use it at your own risk!"
-  }
-  skipPrefixes = [
-      "See the profiling report at:",
-
-      "Deprecated Gradle features were used in this build"
-  ]
-  result = []
-  for line in lines:
-      stripped = line.strip()
-      if stripped in skipLines:
-          continue
-      include = True
-      for prefix in skipPrefixes:
-          if stripped.startswith(prefix):
-              include = False
-              break
-      if include:
-          result.append(line)
-  return result
-
-
 # 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():
@@ -260,6 +220,23 @@
             prev_blank = False
     return result
 
+def extract_task_name(line):
+    prefix = "> Task "
+    if line.startswith(prefix):
+        return line[len(prefix):].strip()
+    return None
+
+def is_task_line(line):
+    return extract_task_name(line) is not None
+
+def extract_task_names(lines):
+    names = []
+    for line in lines:
+        name = extract_task_name(line)
+        if name is not None and name not in names:
+            names.append(name)
+    return names
+
 # If a task has no output (or only blank output), this function removes the task (and its output)
 # For example, turns this:
 #  > Task :a
@@ -277,8 +254,8 @@
     pending_task = None
     pending_blanks = []
     for line in lines:
-        is_task = line.startswith("> Task ") or line.startswith("> Configure project ")
-        if is_task:
+        is_section = is_task_line(line) or line.startswith("> Configure project ") or line.startswith("FAILURE: Build failed with an exception.")
+        if is_section:
             pending_task = line
             pending_blanks = []
         elif line.strip() == "":
@@ -408,12 +385,12 @@
         if line == "":
             continue
         # save task name
-        is_task = False
-        if line.startswith("> Task :") or line.startswith("> Configure project "):
+        is_section = False
+        if is_task_line(line) or line.startswith("> Configure project "):
             # If a task creates output, we record its name
             line = "# " + line
             pending_task_line = line
-            is_task = True
+            is_section = True
         # determine where to put task name
         current_found_index = existing_matcher.index_first_matching_regex(line)
         if current_found_index is not None:
@@ -423,7 +400,7 @@
             pending_task_line = None
             continue
         # skip outputting task names for tasks that don't output anything
-        if is_task:
+        if is_section:
             continue
 
         # escape message
@@ -543,7 +520,6 @@
     if not validate:
         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_by_regexes(interesting_lines, exemption_regexes, validate)
     interesting_lines = collapse_tasks_having_no_output(interesting_lines)
     interesting_lines = collapse_consecutive_blank_lines(interesting_lines)
@@ -562,28 +538,29 @@
         if len(interesting_lines) != 0:
             print("")
             print("=" * 80)
-            print("build_log_simplifier.py: Error: Found " + str(len(interesting_lines)) + " lines of new warning output:")
+            print("build_log_simplifier.py: Error: Found " + str(len(interesting_lines)) + " new lines of warning output!")
             print("")
-            print("".join(interesting_lines))
-            print("=" * 80)
-            print("Error: build_log_simplifier.py found " + str(len(interesting_lines)) + " new lines of output")
+            print("The new output:")
+            print("  " + "  ".join(interesting_lines))
             print("")
-            print("  Log     : " + ",".join(log_paths))
-            print("  Baseline: " + get_deterministic_exemptions_path())
+            print("To reproduce this failure:")
+            print("  Try $ ./gradlew -Pandroidx.validateNoUnrecognizedMessages --rerun-tasks " + " ".join(extract_task_names(interesting_lines)))
+            print("")
+            print("Instructions:")
+            print("  Fix these messages if you can.")
+            print("  Otherwise, you may suppress them.")
+            print("  See also https://android.googlesource.com/platform/frameworks/support/+/androidx-main/development/build_log_simplifier/VALIDATION_FAILURE.md")
+            print("")
             new_exemptions_path = log_paths[0] + ".ignore"
             # 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("  cp " + new_exemptions_path + " " + get_deterministic_exemptions_path())
-            print("")
-            print("For more information, see https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/development/build_log_simplifier/VALIDATION_FAILURE.md")
-            print("=" * 80)
+            print("Files:")
+            print("  Full Log                   : " + ",".join(log_paths))
+            print("  Baseline                   : " + get_deterministic_exemptions_path())
+            print("  Autogenerated new baseline : " + new_exemptions_path)
             exit(1)
     else:
         print("".join(interesting_lines))
diff --git a/development/build_log_simplifier/message-flakes.ignore b/development/build_log_simplifier/message-flakes.ignore
index 798e21b..eb09b8e 100644
--- a/development/build_log_simplifier/message-flakes.ignore
+++ b/development/build_log_simplifier/message-flakes.ignore
@@ -15,4 +15,4 @@
 Stream closed
 # > Task :compose:compiler:compiler-hosted:integration-tests:testDebugUnitTest
 # If a test fails, we don't want the build to fail, we want to pass the test output to the tests server and for the tests server to report the failure
-[0-9]+ tests .*failed.*
+[0-9]+ test.*failed.*
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 4306a57..26d8d03 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -386,6 +386,8 @@
 # b/166471969
 .*androidTest.*AndroidManifest.*xml Warning.*:
 Package name.*test' used in: .*AndroidManifest.*xml.*
+\[:internal\-testutils\-espresso\] \$OUT_DIR/androidx/internal\-testutils\-espresso/build/intermediates/merged_manifest/debug/AndroidManifest\.xml Warning:
+Package name 'androidx\.testutils' used in: :internal\-testutils\-espresso, :internal\-testutils\-runtime\.
 \$OUT_DIR/androidx/compose/ui/ui\-tooling/build/intermediates/tmp/manifest/androidTest/debug/manifestMerger[0-9]+\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
 \[:internal\-testutils\-runtime\] \$OUT_DIR/androidx/internal\-testutils\-runtime/build/intermediates/merged_manifest/debug/AndroidManifest\.xml Warning:
 # > Task :leanback:leanback-paging:generateApi
@@ -400,6 +402,8 @@
 Html results of .* zipped into.*\.zip
 # > Task :annotation:annotation-experimental-lint:test
 WARNING: An illegal reflective access operation has occurred
+WARNING\: Illegal reflective access by org\.jetbrains\.kotlin\.com\.intellij\.util\.ReflectionUtil \(file\:\$CHECKOUT\/prebuilts\/androidx\/external\/org\/jetbrains\/kotlin\/kotlin\-compiler\-embeddable\/[0-9]+\.[0-9]+\.[0-9]+\/kotlin\-compiler\-embeddable\-[0-9]+\.[0-9]+\.[0-9]+\.jar\) to method java\.util\.ResourceBundle\.setParent\(java\.util\.ResourceBundle\)
+WARNING\: Please consider reporting this to the maintainers of org\.jetbrains\.kotlin\.com\.intellij\.util\.ReflectionUtil
 WARNING: Illegal reflective access by com\.intellij\.util\.ReflectionUtil \(file:\$CHECKOUT/prebuilts/androidx/external/org/jetbrains/kotlin/kotlin\-compiler/[0-9]+\.[0-9]+\.[0-9]+/kotlin\-compiler\-[0-9]+\.[0-9]+\.[0-9]+\.jar\) to method java\.util\.ResourceBundle\.setParent\(java\.util\.ResourceBundle\)
 WARNING: Illegal reflective access by androidx\.room\.compiler\.processing\.javac\.JavacProcessingEnvMessager\$Companion\$isFromCompiledClass\$[0-9]+ \(file:\$OUT_DIR/androidx/room/room\-compiler\-processing/build/libs/room\-compiler\-processing\-[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\.jar\) to field com\.sun\.tools\.javac\.code\.Symbol\.owner
 WARNING: Illegal reflective access by com\.intellij\.util\.ReflectionUtil \(file:\$CHECKOUT/prebuilts/androidx/external/org/jetbrains/kotlin/kotlin\-compiler/[0-9]+\.[0-9]+\.[0-9]+\-M[0-9]+/kotlin\-compiler\-[0-9]+\.[0-9]+\.[0-9]+\-M[0-9]+\.jar\) to method java\.util\.ResourceBundle\.setParent\(java\.util\.ResourceBundle\)
@@ -408,13 +412,10 @@
 WARNING: Use \-\-illegal\-access=warn to enable warnings of further illegal reflective access operations
 WARNING: All illegal access operations will be denied in a future release
 # > Task :compose:compiler:compiler-hosted:integration-tests:testDebugUnitTest
-[0-9]+ tests completed, [0-9]+ failed, [0-9]+ skipped
 WARNING: Illegal reflective access by org\.robolectric\.util\.ReflectionHelpers \(file:\$CHECKOUT/prebuilts/androidx/external/org/robolectric/shadowapi/[0-9]+\.[0-9]+\-alpha\-[0-9]+/shadowapi\-[0-9]+\.[0-9]+\-alpha\-[0-9]+\.jar\) to field java\.lang\.reflect\.Field\.modifiers
 WARNING: Please consider reporting this to the maintainers of org\.robolectric\.util\.ReflectionHelpers
 # > Task :compose:compiler:compiler-hosted:integration-tests:test
 wrote dependency log to \$DIST_DIR/affected_module_detector_log\.txt
-Deprecated Gradle features were used in this build\, making it incompatible with Gradle [0-9]+\.[0-9]+\.
-Use \'\-\-warning\-mode all\' to show the individual deprecation warnings\.
 # > Task :enterprise-feedback-testing:compileDebugUnitTestJavaWithJavac
 Note: \$SUPPORT/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporterTest\.java uses or overrides a deprecated API\.
 # > Task :biometric:biometric:compileDebugUnitTestJavaWithJavac
@@ -428,6 +429,7 @@
 Set 'kapt\.includeCompileClasspath = false' to disable discovery\.
 Run the build with '\-\-info' for more details\.
 # > Task :camera:camera-core:compileDebugUnitTestJavaWithJavac
+Note\: \$SUPPORT\/camera\/camera\-core\/src\/test\/java\/androidx\/camera\/core\/CameraSelectorTest\.java uses or overrides a deprecated API\.
 Note: \$SUPPORT/camera/camera\-core/src/test/java/androidx/camera/core/ProcessingImageReaderTest\.java uses or overrides a deprecated API\.
 Note: \$SUPPORT/camera/camera\-core/src/test/java/androidx/camera/core/impl/DeferrableSurfaceTest\.java uses unchecked or unsafe operations\.
 # > Task :wear:wear-input:compileDebugUnitTestJavaWithJavac
@@ -446,9 +448,6 @@
 WARNING: Please consider reporting this to the maintainers of androidx\.room\.compiler\.processing\.javac\.JavacProcessingEnvMessager\$Companion\$isFromCompiledClass\$[0-9]+
 # > Task :wear:wear-watchface-complications-rendering:compileDebugUnitTestJavaWithJavac
 Note\: \$SUPPORT\/wear\/wear\-watchface\-complications\-rendering\/src\/test\/java\/androidx\/wear\/watchface\/complications\/rendering\/RoundedDrawableTest\.java uses or overrides a deprecated API\.
-# > Task :wear:wear-watchface:testDebugUnitTest
-System\.logW\: A resource was acquired at attached stack trace but never released\. See java\.io\.Closeable for information on avoiding resource leaks\.java\.lang\.Throwable\: Explicit termination method \'dispose\' not called
-System\.logW\: A resource was acquired at attached stack trace but never released\. See java\.io\.Closeable for information on avoiding resource leaks\.java\.lang\.Throwable\: Explicit termination method \'release\' not called
 # > Task :benchmark:benchmark-perfetto:mergeDebugAndroidTestJavaResource
 More than one file was found with OS independent path '.*'\. This version of the Android Gradle Plugin chooses the file from the app or dynamic\-feature module, but this can cause unexpected behavior or errors at runtime\. Future versions of the Android Gradle Plugin will throw an error in this case\.
 # > Task :docs-runner:dokkaJavaTipOfTreeDocs
@@ -467,7 +466,6 @@
 # > Task :annotation:annotation-experimental-lint:compileKotlin
 w\: ATTENTION\!
 This build uses unsafe internal compiler arguments\:
-\-XXLanguage\:\-NewInference
 This mode is not recommended for production use\,
 as no stability\/compatibility guarantees are given on
 compiler or generated code\. Use it at your own risk\!
@@ -610,7 +608,6 @@
 w: \$SUPPORT/navigation/navigation\-dynamic\-features\-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/ui/AbstractProgressFragment\.kt: \([0-9]+, [0-9]+\): 'startIntentSenderForResult\(IntentSender!, Int, Intent\?, Int, Int, Int, Bundle\?\): Unit' is deprecated\. Deprecated in Java
 w: \$SUPPORT/navigation/navigation\-dynamic\-features\-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/ui/AbstractProgressFragment\.kt: \([0-9]+, [0-9]+\): 'onActivityResult\(Int, Int, Intent\?\): Unit' is deprecated\. Deprecated in Java
 # > Task :inspection:inspection-gradle-plugin:test
-[0-9]+ test completed, [0-9]+ failed
 There were failing tests\. See the report at: .*.html
 # > Task :compose:ui:ui:processDebugUnitTestManifest
 \$OUT_DIR/androidx/compose/ui/ui/build/intermediates/tmp/manifest/test/debug/manifestMerger[0-9]+\.xml Warning:
@@ -644,16 +641,16 @@
 Stripped invalid locals information from [0-9]+ method\.
 Stripped invalid locals information from [0-9]+ methods\.
 Methods with invalid locals information:
-java\.lang\.Object androidx\.compose\.foundation\.gestures\.DragGestureDetectorKt\.awaitVerticalTouchSlopOrCancellation\-s[0-9]+qLkbw\(androidx\.compose\.ui\.input\.pointer\.HandlePointerInputScope, long, kotlin\.jvm\.functions\.Function[0-9]+, kotlin\.coroutines\.Continuation\)
-java\.lang\.Object androidx\.compose\.foundation\.gestures\.TapGestureDetectorKt\.waitForUpOrCancel\(androidx\.compose\.ui\.input\.pointer\.HandlePointerInputScope, kotlin\.coroutines\.Continuation\)
+java\.lang\.Object androidx\.compose\.foundation\.gestures\.DragGestureDetectorKt\.awaitVerticalTouchSlopOrCancellation\-qFc19kk\(androidx\.compose\.ui\.input\.pointer\.AwaitPointerEventScope, long, kotlin\.jvm\.functions\.Function[0-9]+, kotlin\.coroutines\.Continuation\)
+java\.lang\.Object androidx\.compose\.foundation\.gestures\.TapGestureDetectorKt\.waitForUpOrCancel\(androidx\.compose\.ui\.input\.pointer\.AwaitPointerEventScope, kotlin\.coroutines\.Continuation\)
 Type information in locals\-table is inconsistent\. Cannot constrain type: BOTTOM \(empty\) for value: v[0-9]+ by constraint INT\.
 Some warnings are typically a sign of using an outdated Java toolchain\. To fix, recompile the source with an updated toolchain\.
 Type information in locals\-table is inconsistent\. Cannot constrain type: BOTTOM \(empty\) for value: v[0-9]+ by constraint FLOAT\.
-java\.lang\.Object androidx\.compose\.foundation\.gestures\.DragGestureDetectorKt\.awaitHorizontalTouchSlopOrCancellation\-s[0-9]+qLkbw\(androidx\.compose\.ui\.input\.pointer\.HandlePointerInputScope, long, kotlin\.jvm\.functions\.Function[0-9]+, kotlin\.coroutines\.Continuation\)
+java\.lang\.Object androidx\.compose\.foundation\.gestures\.DragGestureDetectorKt\.awaitHorizontalTouchSlopOrCancellation\-qFc19kk\(androidx\.compose\.ui\.input\.pointer\.AwaitPointerEventScope, long, kotlin\.jvm\.functions\.Function[0-9]+, kotlin\.coroutines\.Continuation\)
 java\.lang\.Object androidx\.compose\.foundation\.gestures\.MultitouchGestureDetectorKt\$detectMultitouchGestures\$[0-9]+\$[0-9]+\.invokeSuspend\(java\.lang\.Object\)
 Attempt to define local of type int as it\$iv:java\.lang\.Object
 Type information in locals\-table is inconsistent\. Cannot constrain type: INT for value: v380\(index\$iv\$iv\) by constraint FLOAT\.
-java\.lang\.Object androidx\.compose\.foundation\.gestures\.TapGestureDetectorKt\.waitForUpOrCancellation\(androidx\.compose\.ui\.input\.pointer\.HandlePointerInputScope, kotlin\.coroutines\.Continuation\)
+java\.lang\.Object androidx\.compose\.foundation\.gestures\.TapGestureDetectorKt\.waitForUpOrCancellation\(androidx\.compose\.ui\.input\.pointer\.AwaitPointerEventScope, kotlin\.coroutines\.Continuation\)
 # > Task :hilt:integration-tests:hilt-testapp-viewmodel:kaptDebugKotlin
 warning: The following options were not recognized by any processor: '\[dagger\.fastInit, dagger\.hilt\.android\.internal\.disableAndroidSuperclassValidation, kapt\.kotlin\.generated\]'
 # > Task :preference:preference:compileDebugAndroidTestKotlin
@@ -670,4 +667,6 @@
 w\: \$SUPPORT\/viewpager[0-9]+\/viewpager[0-9]+\/src\/androidTest\/java\/androidx\/viewpager[0-9]+\/widget\/OnApplyWindowInsetsListenerTest\.kt\: \([0-9]+\, [0-9]+\)\: \'consumeSystemWindowInsets\(\)\: WindowInsetsCompat\' is deprecated\. Deprecated in Java
 w\: \$SUPPORT\/viewpager[0-9]+\/viewpager[0-9]+\/src\/androidTest\/java\/androidx\/viewpager[0-9]+\/widget\/OnApplyWindowInsetsListenerTest\.kt\: \([0-9]+\, [0-9]+\)\: Unnecessary non\-null assertion \(\!\!\) on a non\-null receiver of type WindowInsetsCompat
 w\: \$SUPPORT\/viewpager[0-9]+\/viewpager[0-9]+\/src\/androidTest\/java\/androidx\/viewpager[0-9]+\/widget\/OnApplyWindowInsetsListenerTest\.kt\: \([0-9]+\, [0-9]+\)\: \'consumeStableInsets\(\)\: WindowInsetsCompat\' is deprecated\. Deprecated in Java
-w\: \$SUPPORT\/viewpager[0-9]+\/viewpager[0-9]+\/src\/androidTest\/java\/androidx\/viewpager[0-9]+\/widget\/OnApplyWindowInsetsListenerTest\.kt\: \([0-9]+\, [0-9]+\)\: \'consumeDisplayCutout\(\)\: WindowInsetsCompat\' is deprecated\. Deprecated in Java
\ No newline at end of file
+w\: \$SUPPORT\/viewpager[0-9]+\/viewpager[0-9]+\/src\/androidTest\/java\/androidx\/viewpager[0-9]+\/widget\/OnApplyWindowInsetsListenerTest\.kt\: \([0-9]+\, [0-9]+\)\: \'consumeDisplayCutout\(\)\: WindowInsetsCompat\' is deprecated\. Deprecated in Java
+# > Task :contentpager:contentpager:compileDebugAndroidTestJavaWithJavac
+Note: \$SUPPORT/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/LoaderQueryRunnerTest\.java uses or overrides a deprecated API\.
\ No newline at end of file
diff --git a/development/build_log_simplifier/test.py b/development/build_log_simplifier/test.py
index 8a3b4a4..40ab24b 100755
--- a/development/build_log_simplifier/test.py
+++ b/development/build_log_simplifier/test.py
@@ -16,6 +16,7 @@
 
 from build_log_simplifier import collapse_consecutive_blank_lines
 from build_log_simplifier import collapse_tasks_having_no_output
+from build_log_simplifier import extract_task_names
 from build_log_simplifier import remove_unmatched_exemptions
 from build_log_simplifier import suggest_missing_exemptions
 from build_log_simplifier import normalize_paths
@@ -70,6 +71,23 @@
     assert(matcher.index_first_matching_regex("single") == 2)
     assert(matcher.index_first_matching_regex("absent") is None)
 
+def test_detect_task_names():
+    print("test_detect_task_names")
+    lines = [
+        "> Task :one\n",
+        "some output\n",
+        "> Task :two\n",
+        "more output\n"
+    ]
+    task_names = [":one", ":two"]
+    detected_names = extract_task_names(lines)
+    if detected_names != task_names:
+        fail("extract_task_names returned incorrect response\n" +
+            "Input   : " + str(lines) + "\n" +
+            "Output  : " + str(detected_names) + "\n" +
+            "Expected: " + str(task_names)
+        )
+
 def test_remove_unmatched_exemptions():
     print("test_remove_unmatched_exemptions")
     lines = [
@@ -188,8 +206,9 @@
         "",
         "output inside blanks",
         "",
-        "> Task :no-output2"
-        "> Task :no-output3"
+        "> Task :no-output2",
+        "> Task :no-output3",
+        "FAILURE: Build failed with an exception.\n"
     ]
     expected = [
         "> Task :some-output1",
@@ -201,7 +220,9 @@
     ]
     actual = collapse_tasks_having_no_output(lines)
     if (actual != expected):
-        fail("collapse_tasks_having_no_output gave incorrect error. Expected: " + str(expected) + ", actual = " + str(actual))
+        fail("collapse_tasks_having_no_output gave incorrect error.\n" +
+            "Expected: " + str(expected) + "\n" +
+            "Actual = " + str(actual))
 
 def test_collapse_consecutive_blank_lines():
     print("test_collapse_consecutive_blank_lines")
@@ -261,6 +282,7 @@
 def main():
     test_collapse_consecutive_blank_lines()
     test_collapse_tasks_having_no_output()
+    test_detect_task_names()
     test_suggest_missing_exemptions()
     test_normalize_paths()
     test_regexes_matcher_get_matching_regexes()
diff --git a/development/diagnose-build-failure/diagnose-build-failure.sh b/development/diagnose-build-failure/diagnose-build-failure.sh
index bce19c8..9d9dcae 100755
--- a/development/diagnose-build-failure/diagnose-build-failure.sh
+++ b/development/diagnose-build-failure/diagnose-build-failure.sh
@@ -93,7 +93,7 @@
   fi
 }
 
-function getBuildComand() {
+function getBuildCommand() {
   if [ "$expectedMessage" == "" ]; then
     testCommand="$*"
   else
diff --git a/development/importMaven/build.gradle.kts b/development/importMaven/build.gradle.kts
index 96faade..f6110c9 100644
--- a/development/importMaven/build.gradle.kts
+++ b/development/importMaven/build.gradle.kts
@@ -70,12 +70,12 @@
     java
 }
 
+val metalavaBuildId: String? = findProperty("metalavaBuildId") as String?
 repositories {
     jcenter()
     mavenCentral()
     google()
     gradlePluginPortal()
-    val metalavaBuildId: String? = findProperty("metalavaBuildId") as String?
     if (metalavaBuildId != null) {
         maven(url="https://androidx.dev/metalava/builds/${metalavaBuildId}/artifacts/repo/m2repository")
     }
@@ -457,18 +457,30 @@
 tasks {
     val fetchArtifacts by creating {
         doLast {
+            var numArtifactsFound = 0
             println("\r\nAll Files with Dependencies")
             allFilesWithDependencies.incoming.artifactView {
                 lenient(true)
             }.artifacts.forEach {
                 copyArtifact(it, internal = isInternalArtifact(it))
+                numArtifactsFound++
             }
             gradleModuleMetadata.incoming.artifactView {
                 lenient(true)
             }.artifacts.forEach {
                 copyArtifact(it, internal = isInternalArtifact(it))
+                numArtifactsFound++
             }
-            println("\r\nResolved artifacts for $artifactName.")
+            if (numArtifactsFound < 1) {
+                var message = "Artifact $artifactName not found!"
+                if (metalavaBuildId != null) {
+                    message += "\nMake sure that ab/$metalavaBuildId contains the `metalava` "
+                    message += "target and that it has finished building, or see "
+                    message += "ab/metalava-master for available build ids"
+                }
+                throw GradleException(message)
+            }
+	    println("\r\nResolved $numArtifactsFound artifacts for $artifactName.")
         }
     }
 }
diff --git a/development/importMaven/import_maven_artifacts.py b/development/importMaven/import_maven_artifacts.py
index 33df1c2..8242926 100755
--- a/development/importMaven/import_maven_artifacts.py
+++ b/development/importMaven/import_maven_artifacts.py
@@ -45,7 +45,7 @@
     parser.add_argument('-n', '--name', help=NAME_HELP,
                         required=True, dest='name')
     parser.add_argument('-mb', '--metalava-build-id', help=METALAVA_BUILD_ID_HELP,
-                        required=False, dest='matalava_build_id')
+                        required=False, dest='metalava_build_id')
     parser.add_argument('-ab', '--allow-bintray', help=ALLOW_BINTRAY_HELP,
                         required=False, action='store_true')
     parse_result = parser.parse_args()
@@ -55,9 +55,9 @@
     # Add -Dorg.gradle.debug=true to debug or --stacktrace to see the stack trace
     command = './gradlew --build-file build.gradle.kts -PartifactName=%s' % (
         artifact_name)
-    matalava_build_id = parse_result.matalava_build_id
-    if (matalava_build_id):
-      command = command + ' -PmetalavaBuildId=%s' % (matalava_build_id)
+    metalava_build_id = parse_result.metalava_build_id
+    if (metalava_build_id):
+      command = command + ' -PmetalavaBuildId=%s' % (metalava_build_id)
     if (parse_result.allow_bintray):
       command = command + ' -PallowBintray'
 
diff --git a/development/simplify-build-failure/simplify-build-failure.sh b/development/simplify-build-failure/simplify-build-failure.sh
index ddd75ab..734a109 100755
--- a/development/simplify-build-failure/simplify-build-failure.sh
+++ b/development/simplify-build-failure/simplify-build-failure.sh
@@ -363,6 +363,9 @@
   else
     failed
   fi
+  echo Copying minimal set of files into $fewestFilesOutputPath
+  rm -rf "$fewestFilesOutputPath"
+  cp -rT "$filtererStep1Output" "$fewestFilesOutputPath"
 fi
 
 if [ "$subfilePath" == "" ]; then
diff --git a/development/update_library_versions.py b/development/update_library_versions.py
index 1f2ba6b..826c22c1 100755
--- a/development/update_library_versions.py
+++ b/development/update_library_versions.py
@@ -138,7 +138,7 @@
 
 def increment_alpha_beta_version(version):
 	# Only increment alpha and beta versions.
-	# rc and stable should never need to be incremented in the androidx-master-dev branch
+	# rc and stable should never need to be incremented in the androidx-main branch
 	# Suffix changes should be done manually.
 	changed = False
 	if 'alpha' in version or 'beta' in version:
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 3a97ddb..da293ba 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -4,8 +4,8 @@
 }
 
 dependencies {
-    docs("androidx.activity:activity:1.2.0-beta02")
-    docs("androidx.activity:activity-ktx:1.2.0-beta02")
+    docs("androidx.activity:activity:1.2.0-rc01")
+    docs("androidx.activity:activity-ktx:1.2.0-rc01")
     docs("androidx.ads:ads-identifier:1.0.0-alpha04")
     docs("androidx.ads:ads-identifier-provider:1.0.0-alpha04")
     docs("androidx.annotation:annotation:1.2.0-alpha01")
@@ -22,53 +22,56 @@
     docs("androidx.biometric:biometric:1.2.0-alpha01")
     docs("androidx.biometric:biometric-ktx:1.2.0-alpha01")
     docs("androidx.browser:browser:1.3.0")
-    docs("androidx.camera:camera-camera2:1.0.0-beta12")
-    docs("androidx.camera:camera-core:1.0.0-beta12")
-    docs("androidx.camera:camera-extensions:1.0.0-alpha19")
+    docs("androidx.camera:camera-camera2:1.0.0-rc01")
+    docs("androidx.camera:camera-core:1.0.0-rc01")
+    docs("androidx.camera:camera-extensions:1.0.0-alpha20")
     stubs(fileTree(dir: '../camera/camera-extensions-stub', include: ['camera-extensions-stub.jar']))
-    docs("androidx.camera:camera-lifecycle:1.0.0-beta12")
-    docs("androidx.camera:camera-view:1.0.0-alpha19")
+    docs("androidx.camera:camera-lifecycle:1.0.0-rc01")
+    docs("androidx.camera:camera-view:1.0.0-alpha20")
     docs("androidx.cardview:cardview:1.0.0")
-    docs("androidx.collection:collection:1.1.0")
-    docs("androidx.collection:collection-ktx:1.1.0")
-    docs("androidx.compose.animation:animation:1.0.0-alpha08")
-    docs("androidx.compose.animation:animation-core:1.0.0-alpha08")
-    samples("androidx.compose.animation:animation-samples:1.0.0-alpha08")
-    samples("androidx.compose.animation:animation-core-samples:1.0.0-alpha08")
-    docs("androidx.compose.foundation:foundation:1.0.0-alpha08")
-    docs("androidx.compose.foundation:foundation-layout:1.0.0-alpha08")
-    samples("androidx.compose.foundation:foundation-layout-samples:1.0.0-alpha08")
-    samples("androidx.compose.foundation:foundation-samples:1.0.0-alpha08")
-    docs("androidx.compose.material:material:1.0.0-alpha08")
-    docs("androidx.compose.material:material-icons-core:1.0.0-alpha08")
-    samples("androidx.compose.material:material-icons-core-samples:1.0.0-alpha08")
-    docs("androidx.compose.material:material-icons-extended:1.0.0-alpha08")
-    samples("androidx.compose.material:material-samples:1.0.0-alpha08")
-    docs("androidx.compose.runtime:runtime:1.0.0-alpha08")
-    docs("androidx.compose.runtime:runtime-dispatch:1.0.0-alpha08")
-    docs("androidx.compose.runtime:runtime-livedata:1.0.0-alpha08")
-    samples("androidx.compose.runtime:runtime-livedata-samples:1.0.0-alpha08")
-    docs("androidx.compose.runtime:runtime-rxjava2:1.0.0-alpha08")
-    samples("androidx.compose.runtime:runtime-rxjava2-samples:1.0.0-alpha08")
-    docs("androidx.compose.runtime:runtime-rxjava3:1.0.0-alpha08")
-    docs("androidx.compose.runtime:runtime-saved-instance-state:1.0.0-alpha08")
-    samples("androidx.compose.runtime:runtime-saved-instance-state-samples:1.0.0-alpha08")
-    samples("androidx.compose.runtime:runtime-samples:1.0.0-alpha08")
-    docs("androidx.compose.ui:ui:1.0.0-alpha08")
-    docs("androidx.compose.ui:ui-geometry:1.0.0-alpha08")
-    docs("androidx.compose.ui:ui-graphics:1.0.0-alpha08")
-    samples("androidx.compose.ui:ui-graphics-samples:1.0.0-alpha08")
-    docs("androidx.compose.ui:ui-test:1.0.0-alpha08")
-    docs("androidx.compose.ui:ui-test-junit4:1.0.0-alpha08")
-    docs("androidx.compose.ui:ui-text:1.0.0-alpha08")
+    docs("androidx.collection:collection:1.2.0-alpha01")
+    docs("androidx.collection:collection-ktx:1.2.0-alpha01")
+    docs("androidx.compose.animation:animation:1.0.0-alpha09")
+    docs("androidx.compose.animation:animation-core:1.0.0-alpha09")
+    samples("androidx.compose.animation:animation-samples:1.0.0-alpha09")
+    samples("androidx.compose.animation:animation-core-samples:1.0.0-alpha09")
+    docs("androidx.compose.foundation:foundation:1.0.0-alpha09")
+    docs("androidx.compose.foundation:foundation-layout:1.0.0-alpha09")
+    samples("androidx.compose.foundation:foundation-layout-samples:1.0.0-alpha09")
+    samples("androidx.compose.foundation:foundation-samples:1.0.0-alpha09")
+    docs("androidx.compose.material:material:1.0.0-alpha09")
+    docs("androidx.compose.material:material-icons-core:1.0.0-alpha09")
+    samples("androidx.compose.material:material-icons-core-samples:1.0.0-alpha09")
+    docs("androidx.compose.material:material-icons-extended:1.0.0-alpha09")
+    docs("androidx.compose.material:material-ripple:1.0.0-alpha09")
+    samples("androidx.compose.material:material-samples:1.0.0-alpha09")
+    docs("androidx.compose.runtime:runtime:1.0.0-alpha09")
+    docs("androidx.compose.runtime:runtime-dispatch:1.0.0-alpha09")
+    docs("androidx.compose.runtime:runtime-livedata:1.0.0-alpha09")
+    samples("androidx.compose.runtime:runtime-livedata-samples:1.0.0-alpha09")
+    docs("androidx.compose.runtime:runtime-rxjava2:1.0.0-alpha09")
+    samples("androidx.compose.runtime:runtime-rxjava2-samples:1.0.0-alpha09")
+    docs("androidx.compose.runtime:runtime-rxjava3:1.0.0-alpha09")
+    samples("androidx.compose.runtime:runtime-rxjava3-samples:1.0.0-alpha09")
+    docs("androidx.compose.runtime:runtime-saved-instance-state:1.0.0-alpha09")
+    samples("androidx.compose.runtime:runtime-saved-instance-state-samples:1.0.0-alpha09")
+    samples("androidx.compose.runtime:runtime-samples:1.0.0-alpha09")
+    docs("androidx.compose.ui:ui:1.0.0-alpha09")
+    docs("androidx.compose.ui:ui-geometry:1.0.0-alpha09")
+    docs("androidx.compose.ui:ui-graphics:1.0.0-alpha09")
+    samples("androidx.compose.ui:ui-graphics-samples:1.0.0-alpha09")
+    docs("androidx.compose.ui:ui-test:1.0.0-alpha09")
+    docs("androidx.compose.ui:ui-test-junit4:1.0.0-alpha09")
+    docs("androidx.compose.ui:ui-text:1.0.0-alpha09")
     docs("androidx.compose.ui:ui-text-android:1.0.0-alpha06")
-    samples("androidx.compose.ui:ui-text-samples:1.0.0-alpha08")
-    docs("androidx.compose.ui:ui-unit:1.0.0-alpha08")
-    samples("androidx.compose.ui:ui-unit-samples:1.0.0-alpha08")
-    docs("androidx.compose.ui:ui-util:1.0.0-alpha08")
-    docs("androidx.compose.ui:ui-viewbinding:1.0.0-alpha08")
-    samples("androidx.compose.ui:ui-viewbinding-samples:1.0.0-alpha08")
-    samples("androidx.compose.ui:ui-samples:1.0.0-alpha08")
+    samples("androidx.compose.ui:ui-text-samples:1.0.0-alpha09")
+    docs("androidx.compose.ui:ui-tooling:1.0.0-alpha09")
+    docs("androidx.compose.ui:ui-unit:1.0.0-alpha09")
+    samples("androidx.compose.ui:ui-unit-samples:1.0.0-alpha09")
+    docs("androidx.compose.ui:ui-util:1.0.0-alpha09")
+    docs("androidx.compose.ui:ui-viewbinding:1.0.0-alpha09")
+    samples("androidx.compose.ui:ui-viewbinding-samples:1.0.0-alpha09")
+    samples("androidx.compose.ui:ui-samples:1.0.0-alpha09")
     docs("androidx.concurrent:concurrent-futures:1.1.0")
     docs("androidx.concurrent:concurrent-futures-ktx:1.1.0")
     docs("androidx.contentpager:contentpager:1.0.0")
@@ -94,9 +97,9 @@
     docs("androidx.enterprise:enterprise-feedback:1.1.0-rc01")
     docs("androidx.enterprise:enterprise-feedback-testing:1.1.0-rc01")
     docs("androidx.exifinterface:exifinterface:1.3.2")
-    docs("androidx.fragment:fragment:1.3.0-beta01")
-    docs("androidx.fragment:fragment-ktx:1.3.0-beta01")
-    docs("androidx.fragment:fragment-testing:1.3.0-beta01")
+    docs("androidx.fragment:fragment:1.3.0-rc01")
+    docs("androidx.fragment:fragment-ktx:1.3.0-rc01")
+    docs("androidx.fragment:fragment-testing:1.3.0-rc01")
     docs("androidx.gridlayout:gridlayout:1.0.0")
     docs("androidx.heifwriter:heifwriter:1.1.0-alpha01")
     docs("androidx.hilt:hilt-common:1.0.0-alpha02")
@@ -107,33 +110,33 @@
     docs("androidx.leanback:leanback-paging:1.1.0-alpha06")
     docs("androidx.leanback:leanback-preference:1.1.0-beta01")
     docs("androidx.leanback:leanback-tab:1.1.0-beta01")
-    docs("androidx.lifecycle:lifecycle-common:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-common-java8:2.3.0-beta01")
+    docs("androidx.lifecycle:lifecycle-common:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-common-java8:2.3.0-rc01")
     docs("androidx.lifecycle:lifecycle-extensions:2.2.0")
-    docs("androidx.lifecycle:lifecycle-livedata:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-livedata-core:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-process:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-reactivestreams:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-reactivestreams-ktx:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-runtime:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-service:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-viewmodel:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-beta01")
-    docs("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0-beta01")
+    docs("androidx.lifecycle:lifecycle-livedata:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-livedata-core:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-process:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-reactivestreams:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-reactivestreams-ktx:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-runtime:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-service:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-viewmodel:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-rc01")
+    docs("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0-rc01")
     docs("androidx.loader:loader:1.1.0")
     docs("androidx.localbroadcastmanager:localbroadcastmanager:1.1.0-alpha01")
-    docs("androidx.media2:media2-common:1.1.0")
-    docs("androidx.media2:media2-player:1.1.0")
-    docs("androidx.media2:media2-session:1.1.0")
-    docs("androidx.media2:media2-widget:1.1.0")
+    docs("androidx.media2:media2-common:1.1.1")
+    docs("androidx.media2:media2-player:1.1.1")
+    docs("androidx.media2:media2-session:1.1.1")
+    docs("androidx.media2:media2-widget:1.1.1")
     docs("androidx.media:media:1.2.1")
     docs("androidx.mediarouter:mediarouter:1.2.0")
     docs("androidx.navigation:navigation-common:2.3.2")
     docs("androidx.navigation:navigation-common-ktx:2.3.2")
-    docs("androidx.navigation:navigation-compose:1.0.0-alpha03")
+    docs("androidx.navigation:navigation-compose:1.0.0-alpha04")
     samples("androidx.navigation:navigation-compose-samples:1.0.0-alpha01")
     docs("androidx.navigation:navigation-dynamic-features-fragment:2.3.2")
     docs("androidx.navigation:navigation-dynamic-features-runtime:2.3.2")
@@ -144,17 +147,17 @@
     docs("androidx.navigation:navigation-testing:2.3.2")
     docs("androidx.navigation:navigation-ui:2.3.2")
     docs("androidx.navigation:navigation-ui-ktx:2.3.2")
-    docs("androidx.paging:paging-common:3.0.0-alpha10")
-    docs("androidx.paging:paging-common-ktx:3.0.0-alpha10")
-    docs("androidx.paging:paging-compose:1.0.0-alpha03")
+    docs("androidx.paging:paging-common:3.0.0-alpha11")
+    docs("androidx.paging:paging-common-ktx:3.0.0-alpha11")
+    docs("androidx.paging:paging-compose:1.0.0-alpha04")
     samples("androidx.paging:paging-compose-samples:3.0.0-alpha08")
-    docs("androidx.paging:paging-guava:3.0.0-alpha10")
-    docs("androidx.paging:paging-runtime:3.0.0-alpha10")
-    docs("androidx.paging:paging-runtime-ktx:3.0.0-alpha10")
-    docs("androidx.paging:paging-rxjava2:3.0.0-alpha10")
-    docs("androidx.paging:paging-rxjava2-ktx:3.0.0-alpha10")
-    docs("androidx.paging:paging-rxjava3:3.0.0-alpha10")
-    samples("androidx.paging:paging-samples:3.0.0-alpha10")
+    docs("androidx.paging:paging-guava:3.0.0-alpha11")
+    docs("androidx.paging:paging-runtime:3.0.0-alpha11")
+    docs("androidx.paging:paging-runtime-ktx:3.0.0-alpha11")
+    docs("androidx.paging:paging-rxjava2:3.0.0-alpha11")
+    docs("androidx.paging:paging-rxjava2-ktx:3.0.0-alpha11")
+    docs("androidx.paging:paging-rxjava3:3.0.0-alpha11")
+    samples("androidx.paging:paging-samples:3.0.0-alpha11")
     docs("androidx.palette:palette:1.0.0")
     docs("androidx.palette:palette-ktx:1.0.0")
     docs("androidx.percentlayout:percentlayout:1.0.1")
@@ -165,15 +168,16 @@
     docs("androidx.recyclerview:recyclerview:1.2.0-beta01")
     docs("androidx.recyclerview:recyclerview-selection:2.0.0-alpha01")
     docs("androidx.remotecallback:remotecallback:1.0.0-alpha02")
-    docs("androidx.room:room-common:2.3.0-alpha03")
-    docs("androidx.room:room-guava:2.3.0-alpha03")
-    docs("androidx.room:room-ktx:2.3.0-alpha03")
-    docs("androidx.room:room-migration:2.3.0-alpha03")
-    docs("androidx.room:room-runtime:2.3.0-alpha03")
-    docs("androidx.room:room-rxjava2:2.3.0-alpha03")
-    docs("androidx.room:room-testing:2.3.0-alpha03")
-    docs("androidx.savedstate:savedstate:1.1.0-beta01")
-    docs("androidx.savedstate:savedstate-ktx:1.1.0-beta01")
+    docs("androidx.room:room-common:2.3.0-alpha04")
+    docs("androidx.room:room-guava:2.3.0-alpha04")
+    docs("androidx.room:room-ktx:2.3.0-alpha04")
+    docs("androidx.room:room-migration:2.3.0-alpha04")
+    docs("androidx.room:room-runtime:2.3.0-alpha04")
+    docs("androidx.room:room-rxjava2:2.3.0-alpha04")
+    docs("androidx.room:room-rxjava3:2.3.0-alpha04")
+    docs("androidx.room:room-testing:2.3.0-alpha04")
+    docs("androidx.savedstate:savedstate:1.1.0-rc01")
+    docs("androidx.savedstate:savedstate-ktx:1.1.0-rc01")
     docs("androidx.security:security-crypto:1.1.0-alpha03")
     docs("androidx.security:security-crypto-ktx:1.1.0-alpha02")
     docs("androidx.security:security-identity-credential:1.0.0-alpha01")
@@ -201,19 +205,19 @@
     docs("androidx.versionedparcelable:versionedparcelable:1.1.1")
     docs("androidx.viewpager2:viewpager2:1.1.0-alpha01")
     docs("androidx.viewpager:viewpager:1.0.0")
-    docs("androidx.wear:wear:1.2.0-alpha03")
+    docs("androidx.wear:wear:1.2.0-alpha04")
     stubs(fileTree(dir: '../wear/wear_stubs/', include: ['com.google.android.wearable-stubs.jar']))
-    docs("androidx.wear:wear-complications-data:1.0.0-alpha03")
-    docs("androidx.wear:wear-complications-provider:1.0.0-alpha03")
-    docs("androidx.wear:wear-watchface:1.0.0-alpha03")
-    docs("androidx.wear:wear-watchface-client:1.0.0-alpha03")
-    docs("androidx.wear:wear-watchface-complications-rendering:1.0.0-alpha03")
-    docs("androidx.wear:wear-watchface-data:1.0.0-alpha03")
+    docs("androidx.wear:wear-complications-data:1.0.0-alpha04")
+    docs("androidx.wear:wear-complications-provider:1.0.0-alpha04")
+    docs("androidx.wear:wear-watchface:1.0.0-alpha04")
+    docs("androidx.wear:wear-watchface-client:1.0.0-alpha04")
+    docs("androidx.wear:wear-watchface-complications-rendering:1.0.0-alpha04")
+    docs("androidx.wear:wear-watchface-data:1.0.0-alpha04")
     samples("androidx.wear:wear-watchface-samples:1.0.0-alpha02")
-    docs("androidx.wear:wear-watchface-style:1.0.0-alpha03")
+    docs("androidx.wear:wear-watchface-style:1.0.0-alpha04")
     docs("androidx.wear:wear-input:1.0.0")
     docs("androidx.wear:wear-input-testing:1.0.0")
-    docs("androidx.webkit:webkit:1.4.0-rc02")
+    docs("androidx.webkit:webkit:1.4.0")
     docs("androidx.window:window:1.0.0-alpha01")
     stubs(fileTree(dir: '../window/stubs/', include: ['window-sidecar-release-0.1.0-alpha01.aar']))
     stubs(project(":window:window-extensions"))
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index e2d581f..c45a191 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -232,6 +232,7 @@
     docs(project(":wear:wear-complications-provider"))
     docs(project(":wear:wear-input"))
     docs(project(":wear:wear-input-testing"))
+    docs(project(":wear:wear-remote-interactions"))
     docs(project(":wear:wear-watchface"))
     docs(project(":wear:wear-watchface-complications-rendering"))
     docs(project(":wear:wear-watchface-data"))
diff --git a/docs/api_guidelines.md b/docs/api_guidelines.md
new file mode 100644
index 0000000..3781200
--- /dev/null
+++ b/docs/api_guidelines.md
@@ -0,0 +1,1522 @@
+# Library API guidelines
+
+[TOC]
+
+## Introduction {#introduction}
+
+s.android.com/api-guidelines,
+which covers standard and practices for designing platform APIs.
+
+All platform API design guidelines also apply to Jetpack libraries, with any
+additional guidelines or exceptions noted in this document. Jetpack libraries
+also follow
+[explicit API mode](https://kotlinlang.org/docs/reference/whatsnew14.html#explicit-api-mode-for-library-authors)
+for Kotlin libraries.
+
+## Modules {#module}
+
+### Packaging and naming {#module-naming}
+
+Java packages within Jetpack follow the format `androidx.<feature-name>`. All
+classes within a feature's artifact must reside within this package, and may
+further subdivide into `androidx.<feature-name>.<layer>` using standard Android
+layers (app, widget, etc.) or layers specific to the feature.
+
+Maven artifacts use the groupId format `androidx.<feature-name>` and artifactId
+format `<feature-name>` to match the Java package.
+
+Sub-features that can be separated into their own artifact should use the
+following formats:
+
+Java package: `androidx.<feature-name>.<sub-feature>.<layer>`
+
+Maven groupId: `androidx.<feature-name>`
+
+Maven artifactId: `<feature-name>-<sub-feature>`
+
+#### Common sub-feature names {#module-naming-subfeature}
+
+*   `-testing` for an artifact intended to be used while testing usages of your
+    library, e.g. `androidx.room:room-testing`
+*   `-core` for a low-level artifact that *may* contain public APIs but is
+    primarily intended for use by other libraries in the group
+*   `-ktx` for an Kotlin artifact that exposes idiomatic Kotlin APIs as an
+    extension to a Java-only library
+*   `-java8` for a Java 8 artifact that exposes idiomatic Java 8 APIs as an
+    extension to a Java 7 library
+*   `-<third-party>` for an artifact that integrates an optional third-party API
+    surface, e.g. `-proto` or `-rxjava2`. Note that a major version is included
+    in the sub-feature name for third-party API surfaces where the major version
+    indicates binary compatibility (only needed for post-1.x).
+
+Artifacts **should not** use `-impl` or `-base` to indicate that a library is an
+implementation detail shared within the group. Instead, use `-core`.
+
+#### Splitting existing modules
+
+Existing modules _should not_ be split into smaller modules; doing so creates
+the potential for class duplication issues when a developer depends on a new
+sub-module alongside the older top-level module. Consider the following
+scenario:
+
+*   `androidx.library:1.0.0`
+    *   contains classes `androidx.library.A` and `androidx.library.util.B`
+
+This module is split, moving `androidx.library.util.B` to a new module:
+
+*   `androidx.library:1.1.0`
+    *   contains class `androidx.library.A`
+    *   depends on `androidx.library.util:1.0.0`
+*   `androidx.library.util:1.0.0`
+    *   depends on `androidx.library.util.B`
+
+A developer writes an app that depends directly on `androidx.library.util:1.0.0`
+and transitively pulls in `androidx.library:1.0.0`. Their app will no longer
+compile due to class duplication of `androidx.library.util.B`.
+
+While it is possible for the developer to fix this by manually specifying a
+dependency on `androidx.library:1.1.0`, there is no easy way for the developer
+to discover this solution from the class duplication error raised at compile
+time.
+
+#### Same-version (atomic) groups
+
+Library groups are encouraged to opt-in to a same-version policy whereby all
+libraries in the group use the same version and express exact-match dependencies
+on libraries within the group. Such groups must increment the version of every
+library at the same time and release all libraries at the same time.
+
+Atomic groups are specified in
+[`LibraryGroups.kt`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt):
+
+```kotlin
+// Non-atomic library group
+val APPCOMPAT = LibraryGroup("androidx.appcompat", null)
+// Atomic library group
+val APPSEARCH = LibraryGroup("androidx.appsearch", LibraryVersions.APPSEARCH)
+```
+
+Libraries within an atomic group should not specify a version in their
+`build.gradle`:
+
+```groovy
+androidx {
+    name = 'AppSearch'
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    mavenGroup = LibraryGroups.APPSEARCH
+    inceptionYear = '2019'
+    description = 'Provides local and centralized app indexing'
+}
+```
+
+There is one exception to this policy. Newly-added libraries within an atomic
+group may stay within the `1.0.0-alphaXX` before conforming to the same-version
+policy. When the library would like to move to `beta`, it must match the version
+used by the atomic group (which must be `beta` at the time).
+
+The benefits of using an atomic group are:
+
+-   Easier for developers to understand dependency versioning
+-   `@RestrictTo(LIBRARY_GROUP)` APIs are treated as private APIs and not
+    tracked for binary compatibility
+-   `@RequiresOptIn` APIs defined within the group may be used without any
+    restrictions between libraries in the group
+
+Potential drawbacks include:
+
+-   All libraries within the group must be versioned identically at head
+-   All libraries within the group must release at the same time
+
+
+### Choosing a `minSdkVersion` {#module-minsdkversion}
+
+The recommended minimum SDK version for new Jetpack libraries is currently
+**17** (Android 4.2, Jelly Bean). This SDK was chosen to represent 99% of active
+devices based on Play Store check-ins (see Android Studio
+[distribution metadata](https://dl.google.com/android/studio/metadata/distributions.json)
+for current statistics). This maximizes potential users for external developers
+while minimizing the amount of overhead necessary to support legacy versions.
+
+However, if no explicit minimum SDK version is specified for a library, the
+default is 14.
+
+Note that a library **must not** depend on another library with a higher
+`minSdkVersion` that its own, so it may be necessary for a new library to match
+its dependent libraries' `minSdkVersion`.
+
+Individual modules may choose a higher minimum SDK version for business or
+technical reasons. This is common for device-specific modules such as Auto or
+Wear.
+
+Individual classes or methods may be annotated with the
+[@RequiresApi](https://developer.android.com/reference/android/annotation/RequiresApi.html)
+annotation to indicate divergence from the overall module's minimum SDK version.
+Note that this pattern is _not recommended_ because it leads to confusion for
+external developers and should be considered a last-resort when backporting
+behavior is not feasible.
+
+## Platform compatibility API patterns {#platform-compatibility-apis}
+
+### Static shims (ex. [ViewCompat](https://developer.android.com/reference/android/support/v4/view/ViewCompat.html)) {#static-shim}
+
+When to use?
+
+*   Platform class exists at module's `minSdkVersion`
+*   Compatibility implementation does not need to store additional metadata
+
+Implementation requirements
+
+*   Class name **must** be `<PlatformClass>Compat`
+*   Package name **must** be `androidx.<feature>.<platform.package>`
+*   Superclass **must** be `Object`
+*   Class **must** be non-instantiable, i.e. constructor is private no-op
+*   Static fields and static methods **must** match match signatures with
+    `PlatformClass`
+    *   Static fields that can be inlined, ex. integer constants, **must not**
+        be shimmed
+*   Public method names **must** match platform method names
+*   Public methods **must** be static and take `PlatformClass` as first
+    parameter
+*   Implementation _may_ delegate to `PlatformClass` methods when available
+
+#### Sample {#static-shim-sample}
+
+The following sample provides static helper methods for the platform class
+`android.os.Process`.
+
+```java
+/**
+ * Helper for accessing features in {@link Process}.
+ */
+public final class ProcessCompat {
+    private ProcessCompat() {
+        // This class is non-instantiable.
+    }
+
+    /**
+     * [Docs should match platform docs.]
+     *
+     * Compatibility behavior:
+     * <ul>
+     * <li>SDK 24 and above, this method matches platform behavior.
+     * <li>SDK 16 through 23, this method is a best-effort to match platform behavior, but may
+     * default to returning {@code true} if an accurate result is not available.
+     * <li>SDK 15 and below, this method always returns {@code true} as application UIDs and
+     * isolated processes did not exist yet.
+     * </ul>
+     *
+     * @param [match platform docs]
+     * @return [match platform docs], or a value based on platform-specific fallback behavior
+     */
+    public static boolean isApplicationUid(int uid) {
+        if (Build.VERSION.SDK_INT >= 24) {
+            return Api24Impl.isApplicationUid(uid);
+        } else if (Build.VERSION.SDK_INT >= 17) {
+            return Api17Impl.isApplicationUid(uid);
+        } else if (Build.VERSION.SDK_INT == 16) {
+            return Api16Impl.isApplicationUid(uid);
+        } else {
+            return true;
+        }
+    }
+
+    @RequiresApi(24)
+    static class Api24Impl {
+        static boolean isApplicationUid(int uid) {
+            // In N, the method was made public on android.os.Process.
+            return Process.isApplicationUid(uid);
+        }
+    }
+
+    @RequiresApi(17)
+    static class Api17Impl {
+        private static Method sMethod_isAppMethod;
+        private static boolean sResolved;
+
+        static boolean isApplicationUid(int uid) {
+            // In JELLY_BEAN_MR2, the equivalent isApp(int) hidden method moved to public class
+            // android.os.UserHandle.
+            try {
+                if (!sResolved) {
+                    sResolved = true;
+                    sMethod_isAppMethod = UserHandle.class.getDeclaredMethod("isApp",int.class);
+                }
+                if (sMethod_isAppMethod != null) {
+                    return (Boolean) sMethod_isAppMethod.invoke(null, uid);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            return true;
+        }
+    }
+
+    ...
+}
+```
+
+### Wrapper (ex. [AccessibilityNodeInfoCompat](https://developer.android.com/reference/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.html)) {#wrapper}
+
+When to use?
+
+*   Platform class may not exist at module's `minSdkVersion`
+*   Compatibility implementation may need to store additional metadata
+*   Needs to integrate with platform APIs as return value or method argument
+*   **Note:** Should be avoided when possible, as using wrapper classes makes it
+    very difficult to deprecate classes and migrate source code when the
+    `minSdkVersion` is raised
+
+#### Sample {#wrapper-sample}
+
+The following sample wraps a hypothetical platform class `ModemInfo` that was
+added to the platform SDK in API level 23:
+
+```java
+public final class ModemInfoCompat {
+  // Only guaranteed to be non-null on SDK_INT >= 23. Note that referencing the
+  // class itself directly is fine -- only references to class members need to
+  // be pushed into static inner classes.
+  private final ModemInfo wrappedObj;
+
+  /**
+   * [Copy platform docs for matching constructor.]
+   */
+  public ModemInfoCompat() {
+    if (SDK_INT >= 23) {
+      wrappedObj = Api23Impl.create();
+    } else {
+      wrappedObj = null;
+    }
+    ...
+  }
+
+  @RequiresApi(23)
+  private ModemInfoCompat(@NonNull ModemInfo obj) {
+    mWrapped = obj;
+  }
+
+  /**
+   * Provides a backward-compatible wrapper for {@link ModemInfo}.
+   * <p>
+   * This method is not supported on devices running SDK < 23 since the platform
+   * class will not be available.
+   *
+   * @param info platform class to wrap
+   * @return wrapped class, or {@code null} if parameter is {@code null}
+   */
+  @RequiresApi(23)
+  @NonNull
+  public static ModemInfoCompat toModemInfoCompat(@NonNull ModemInfo info) {
+    return new ModemInfoCompat(obj);
+  }
+
+  /**
+   * Provides the {@link ModemInfo} represented by this object.
+   * <p>
+   * This method is not supported on devices running SDK < 23 since the platform
+   * class will not be available.
+   *
+   * @return platform class object
+   * @see ModemInfoCompat#toModemInfoCompat(ModemInfo)
+   */
+  @RequiresApi(23)
+  @NonNull
+  public ModemInfo toModemInfo() {
+    return mWrapped;
+  }
+
+  /**
+   * [Docs should match platform docs.]
+   *
+   * Compatibility behavior:
+   * <ul>
+   * <li>API level 23 and above, this method matches platform behavior.
+   * <li>API level 18 through 22, this method ...
+   * <li>API level 17 and earlier, this method always returns false.
+   * </ul>
+   *
+   * @return [match platform docs], or platform-specific fallback behavior
+   */
+  public boolean isLteSupported() {
+    if (SDK_INT >= 23) {
+      return Api23Impl.isLteSupported(mWrapped);
+    } else if (SDK_INT >= 18) {
+      // Smart fallback behavior based on earlier APIs.
+      ...
+    }
+    // Default behavior.
+    return false;
+  }
+
+  // All references to class members -- including the constructor -- must be
+  // made on an inner class to avoid soft-verification errors that slow class
+  // loading and prevent optimization.
+  @RequiresApi(23)
+  private static class Api23Impl {
+    @NonNull
+    static ModemInfo create() {
+      return new ModemInfo();
+    }
+
+    static boolean isLteSupported(PlatformClass obj) {
+      return obj.isLteSupported();
+    }
+  }
+}
+```
+
+Note that libraries written in Java should express conversion to and from the
+platform class differently than Kotlin classes. For Java classes, conversion
+from the platform class to the wrapper should be expressed as a `static` method,
+while conversion from the wrapper to the platform class should be a method on
+the wrapper object:
+
+```java
+@NonNull
+public static ModemInfoCompat toModemInfoCompat(@NonNull ModemInfo info);
+
+@NonNull
+public ModemInfo toModemInfo();
+```
+
+In cases where the primary library is written in Java and has an accompanying
+`-ktx` Kotlin extensions library, the following conversion should be provided as
+an extension function:
+
+```kotlin
+fun ModemInfo.toModemInfoCompat() : ModemInfoCompat
+```
+
+Whereas in cases where the primary library is written in Kotlin, the conversion
+should be provided as an extension factory:
+
+```kotlin
+class ModemInfoCompat {
+  fun toModemInfo() : ModemInfo
+
+  companion object {
+    @JvmStatic
+    @JvmName("toModemInfoCompat")
+    fun ModemInfo.toModemInfoCompat() : ModemInfoCompat
+  }
+}
+```
+
+#### API guidelines {#wrapper-api-guidelines}
+
+##### Naming {#wrapper-naming}
+
+*   Class name **must** be `<PlatformClass>Compat`
+*   Package name **must** be `androidx.core.<platform.package>`
+*   Superclass **must not** be `<PlatformClass>`
+
+##### Construction {#wrapper-construction}
+
+*   Class _may_ have public constructor(s) to provide parity with public
+    `PlatformClass` constructors
+    *   Constructor used to wrap `PlatformClass` **must not** be public
+*   Class **must** implement a static `PlatformClassCompat
+    toPlatformClassCompat(PlatformClass)` method to wrap `PlatformClass` on
+    supported SDK levels
+    *   If class does not exist at module's `minSdkVersion`, method must be
+        annotated with `@RequiresApi(<sdk>)` for SDK version where class was
+        introduced
+
+#### Implementation {#wrapper-implementation}
+
+*   Class **must** implement a `PlatformClass toPlatformClass()` method to
+    unwrap `PlatformClass` on supported SDK levels
+    *   If class does not exist at module's `minSdkVersion`, method must be
+        annotated with `@RequiresApi(<sdk>)` for SDK version where class was
+        introduced
+*   Implementation _may_ delegate to `PlatformClass` methods when available (see
+    below note for caveats)
+*   To avoid runtime class verification issues, all operations that interact
+    with the internal structure of `PlatformClass` must be implemented in inner
+    classes targeted to the SDK level at which the operation was added.
+    *   See the [sample](#wrapper-sample) for an example of interacting with a
+        method that was added in SDK level 23.
+
+### Standalone (ex. [ArraySet](https://developer.android.com/reference/android/support/v4/util/ArraySet.html), [Fragment](https://developer.android.com/reference/android/support/v4/app/Fragment.html)) {#standalone}
+
+When to use?
+
+*   Platform class may exist at module's `minSdkVersion`
+*   Does not need to integrate with platform APIs
+*   Does not need to coexist with platform class, ex. no potential `import`
+    collision due to both compatibility and platform classes being referenced
+    within the same source file
+
+Implementation requirements
+
+*   Class name **must** be `<PlatformClass>`
+*   Package name **must** be `androidx.<platform.package>`
+*   Superclass **must not** be `<PlatformClass>`
+*   Class **must not** expose `PlatformClass` in public API
+*   Implementation _may_ delegate to `PlatformClass` methods when available
+
+### Standalone JAR library (no Android dependencies) {#standalone-jar-library-no-android-dependencies}
+
+When to use
+
+*   General purpose library with minimal interaction with Android types
+    *   or when abstraction around types can be used (e.g. Room's SQLite
+        wrapper)
+*   Lib used in parts of app with minimal Android dependencies
+    *   ex. Repository, ViewModel
+*   When Android dependency can sit on top of common library
+*   Clear separation between android dependent and independent parts of your
+    library
+*   Clear that future integration with android dependencies can be layered
+    separately
+
+**Examples:**
+
+The **Paging Library** pages data from DataSources (such as DB content from Room
+or network content from Retrofit) into PagedLists, so they can be presented in a
+RecyclerView. Since the included Adapter receives a PagedList, and there are no
+other Android dependencies, Paging is split into two parts - a no-android
+library (paging-common) with the majority of the paging code, and an android
+library (paging-runtime) with just the code to present a PagedList in a
+RecyclerView Adapter. This way, tests of Repositories and their components can
+be tested in host-side tests.
+
+**Room** loads SQLite data on Android, but provides an abstraction for those
+that want to use a different SQL implementation on device. This abstraction, and
+the fact that Room generates code dynamically, means that Room interfaces can be
+used in host-side tests (though actual DB code should be tested on device, since
+DB impls may be significantly different on host).
+
+## Implementing compatibility {#compat}
+
+### Referencing new APIs {#compat-newapi}
+
+Generally, methods on extension library classes should be available to all
+devices above the library's `minSdkVersion`.
+
+#### Checking device SDK version {#compat-sdk}
+
+The most common way of delegating to platform or backport implementations is to
+compare the device's `Build.VERSION.SDK_INT` field to a known-good SDK version;
+for example, the SDK in which a method first appeared or in which a critical bug
+was first fixed.
+
+Non-reflective calls to new APIs gated on `SDK_INT` **must** be made from
+version-specific static inner classes to avoid verification errors that
+negatively affect run-time performance. For more information, see Chromium's
+guide to
+[Class Verification Failures](https://chromium.googlesource.com/chromium/src/+/HEAD/build/android/docs/class_verification_failures.md).
+
+Methods in implementation-specific classes **must** be paired with the
+`@DoNotInline` annotation to prevent them from being inlined.
+
+```java {.good}
+public static void saveAttributeDataForStyleable(@NonNull View view, ...) {
+  if (Build.VERSION.SDK_INT >= 29) {
+    Api29Impl.saveAttributeDataForStyleable(view, ...);
+  }
+}
+
+@RequiresApi(29)
+private static class Api29Impl {
+  @DoNotInline
+  static void saveAttributeDataForStyleable(@NonNull View view, ...) {
+    view.saveAttributeDataForStyleable(...);
+  }
+}
+```
+
+Alternatively, in Kotlin sources:
+
+```kotlin {.good}
+@RequiresApi(29)
+object Api25 {
+  @DoNotInline
+  fun saveAttributeDataForStyleable(view: View, ...) { ... }
+}
+```
+
+When developing against pre-release SDKs where the `SDK_INT` has not been
+finalized, SDK checks **must** use `BuildCompat.isAtLeastX()` methods.
+
+```java {.good}
+@NonNull
+public static List<Window> getAllWindows() {
+  if (BuildCompat.isAtLeastR()) {
+    return ApiRImpl.getAllWindows();
+  }
+  return Collections.emptyList();
+}
+```
+
+#### Device-specific issues {#compat-oem}
+
+Library code may work around device- or manufacturer-specific issues -- issues
+not present in AOSP builds of Android -- *only* if a corresponding CTS test
+and/or CDD policy is added to the next revision of the Android platform. Doing
+so ensures that such issues can be detected and fixed by OEMs.
+
+#### Handling `minSdkVersion` disparity {#compat-minsdk}
+
+Methods that only need to be accessible on newer devices, including
+`to<PlatformClass>()` methods, may be annotated with `@RequiresApi(<sdk>)` to
+indicate they will fail to link on older SDKs. This annotation is enforced at
+build time by Lint.
+
+#### Handling `targetSdkVersion` behavior changes {#compat-targetsdk}
+
+To preserve application functionality, device behavior at a given API level may
+change based on an application's `targetSdkVersion`. For example, if an app with
+`targetSdkVersion` set to API level 22 runs on a device with API level 29, all
+required permissions will be granted at installation time and the run-time
+permissions framework will emulate earlier device behavior.
+
+Libraries do not have control over the app's `targetSdkVersion` and -- in rare
+cases -- may need to handle variations in platform behavior. Refer to the
+following pages for version-specific behavior changes:
+
+*   API level 29:
+    [Android Q behavior changes: apps targeting Q](https://developer.android.com/preview/behavior-changes-q)
+*   API level 28:
+    [Behavior changes: apps targeting API level 28+](https://developer.android.com/about/versions/pie/android-9.0-changes-28)
+*   API level 26:
+    [Changes for apps targeting Android 8.0](https://developer.android.com/about/versions/oreo/android-8.0-changes#o-apps)
+*   API level 24:
+    [Changes for apps targeting Android 7.0](https://developer.android.com/about/versions/nougat/android-7.0-changes#n-apps)
+*   API level 21:
+    [Android 5.0 Behavior Changes](https://developer.android.com/about/versions/android-5.0-changes)
+*   API level 19:
+    [Android 4.4 APIs](https://developer.android.com/about/versions/android-4.4)
+
+#### Working around Lint issues {#compat-lint}
+
+In rare cases, Lint may fail to interpret API usages and yield a `NewApi` error
+and require the use of `@TargetApi` or `@SuppressLint('NewApi')` annotations.
+Both of these annotations are strongly discouraged and may only be used
+temporarily. They **must never** be used in a stable release. Any usage of these
+annotation **must** be associated with an active bug, and the usage must be
+removed when the bug is resolved.
+
+### Delegating to API-specific implementations {#delegating-to-api-specific-implementations}
+
+#### SDK-dependent reflection
+
+Starting in API level 28, the platform restricts which
+[non-SDK interfaces](https://developer.android.com/distribute/best-practices/develop/restrictions-non-sdk-interfaces)
+can be accessed via reflection by apps and libraries. As a general rule, you
+will **not** be able to use reflection to access hidden APIs on devices with
+`SDK_INT` greater than `Build.VERSION_CODES.P` (28).
+
+On earlier devices, reflection on hidden platform APIs is allowed **only** when
+an alternative public platform API exists in a later revision of the Android
+SDK. For example, the following implementation is allowed:
+
+```java
+public AccessibilityDelegate getAccessibilityDelegate(View v) {
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+        // Retrieve the delegate using a public API.
+        return v.getAccessibilityDelegate();
+    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+        // Retrieve the delegate by reflecting on a private field. If the
+        // field does not exist or cannot be accessed, this will no-op.
+        if (sAccessibilityDelegateField == null) {
+            try {
+                sAccessibilityDelegateField = View.class
+                        .getDeclaredField("mAccessibilityDelegate");
+                sAccessibilityDelegateField.setAccessible(true);
+            } catch (Throwable t) {
+                sAccessibilityDelegateCheckFailed = true;
+                return null;
+            }
+        }
+        try {
+            Object o = sAccessibilityDelegateField.get(v);
+            if (o instanceof View.AccessibilityDelegate) {
+                return (View.AccessibilityDelegate) o;
+            }
+            return null;
+        } catch (Throwable t) {
+            sAccessibilityDelegateCheckFailed = true;
+            return null;
+        }
+    } else {
+        // There is no way to retrieve the delegate, even via reflection.
+        return null;
+    }
+```
+
+Calls to public APIs added in pre-release revisions *must* be gated using
+`BuildCompat`:
+
+```java
+if (BuildCompat.isAtLeastQ()) {
+   // call new API added in Q
+} else if (Build.SDK_INT.VERSION >= Build.VERSION_CODES.SOME_RELEASE) {
+   // make a best-effort using APIs that we expect to be available
+} else {
+   // no-op or best-effort given no information
+}
+```
+
+### Inter-process communication {#inter-process-communication}
+
+Protocols and data structures used for IPC must support interoperability between
+different versions of libraries and should be treated similarly to public API.
+
+#### Data structures
+
+**Do not** use Parcelable for any class that may be used for IPC or otherwise
+exposed as public API. The data format used by Parcelable does not provide any
+compatibility guarantees and will result in crashes if fields are added or
+removed between library versions.
+
+**Do not** design your own serialization mechanism or wire format for disk
+storage or inter-process communication. Preserving and verifying compatibility
+is difficult and error-prone.
+
+If you expose a `Bundle` to callers that can cross processes, you should
+[prevent apps from adding their own custom parcelables](https://android.googlesource.com/platform/frameworks/base/+/6cddbe14e1ff67dc4691a013fe38a2eb0893fe03)
+as top-level entries; if *any* entry in a `Bundle` can't be loaded, even if it's
+not actually accessed, the receiving process is likely to crash.
+
+**Do** use protocol buffers or, in some simpler cases, `VersionedParcelable`.
+
+#### Communication protocols
+
+Any communication prototcol, handshake, etc. must maintain compatibility
+consistent with SemVer guidelines. Consider how your protocol will handle
+addition and removal of operations or constants, compatibility-breaking changes,
+and other modifications without crashing either the host or client process.
+
+## Deprecation and removal
+
+While SemVer's binary compatibility guarantees restrict the types of changes
+that may be made within a library revision and make it difficult to remove an
+API, there are many other ways to influence how developers interact with your
+library.
+
+### Deprecation (`@deprecated`)
+
+Deprecation lets a developer know that they should stop using an API or class.
+All deprecations must be marked with a `@Deprecated` Java annotation as well as
+a `@deprecated <migration-docs>` docs annotation explaining how the developer
+should migrate away from the API.
+
+Deprecation is an non-breaking API change that must occur in a **major** or
+**minor** release.
+
+### Soft removal (@removed)
+
+Soft removal preserves binary compatibility while preventing source code from
+compiling against an API. It is a *source-breaking change* and not recommended.
+
+Soft removals **must** do the following:
+
+*   Mark the API as deprecated for at least one stable release prior to removal.
+*   Mark the API with a `@RestrictTo(LIBRARY)` Java annotation as well as a
+    `@removed <reason>` docs annotation explaining why the API was removed.
+*   Maintain binary compatibility, as the API may still be called by existing
+    dependent libraries.
+*   Maintain behavioral compatibility and existing tests.
+
+This is a disruptive change and should be avoided when possible.
+
+Soft removal is a source-breaking API change that must occur in a **major** or
+**minor** release.
+
+### Hard removal
+
+Hard removal entails removing the entire implementation of an API that was
+exposed in a public release. Prior to removal, an API must be marked as
+`@deprecated` for a full **minor** version (`alpha`->`beta`->`rc`->stable),
+prior to being hard removed.
+
+This is a disruptive change and should be avoided when possible.
+
+Hard removal is a binary-breaking API change that must occur in a **major**
+release.
+
+### For entire artifacts
+
+We do not typically deprecate or remove entire artifacts; however, it may be
+useful in cases where we want to halt development and focus elsewhere or
+strongly discourage developers from using a library.
+
+Halting development, either because of staffing or prioritization issues, leaves
+the door open for future bug fixes or continued development. This quite simply
+means we stop releasing updates but retain the source in our tree.
+
+Deprecating an artifact provides developers with a migration path and strongly
+encourages them -- through Lint warnings -- to migrate elsewhere. This is
+accomplished by adding a `@Deprecated` and `@deprecated` (with migration
+comment) annotation pair to *every* class and interface in the artifact.
+
+The fully-deprecated artifact will be released as a deprecation release -- it
+will ship normally with accompanying release notes indicating the reason for
+deprecation and migration strategy, and it will be the last version of the
+artifact that ships. It will ship as a new minor stable release. For example, if
+`1.0.0` was the last stable release, then the deprecation release will be
+`1.1.0`. This is so Android Studio users will get a suggestion to update to a
+new stable version, which will contain the `@deprecated` annotations.
+
+After an artifact has been released as fully-deprecated, it can be removed from
+the source tree.
+
+## Resources {#resources}
+
+Generally, follow the official Android guidelines for
+[app resources](https://developer.android.com/guide/topics/resources/providing-resources).
+Special guidelines for library resources are noted below.
+
+### Defining new resources
+
+Libraries may define new value and attribute resources using the standard
+application directory structure used by Android Gradle Plugin:
+
+```
+src/main/res/
+  values/
+    attrs.xml   Theme attributes and styleables
+    dimens.xml  Dimensional values
+    public.xml  Public resource definitions
+    ...
+```
+
+However, some libraries may still be using non-standard, legacy directory
+structures such as `res-public` for their public resource declarations or a
+top-level `res` directory and accompanying custom source set in `build.gradle`.
+These libraries will eventually be migrated to follow standard guidelines.
+
+#### Naming conventions
+
+Libraries follow the Android platform's resource naming conventions, which use
+`camelCase` for attributes and `underline_delimited` for values. For example,
+`R.attr.fontProviderPackage` and `R.dimen.material_blue_grey_900`.
+
+#### Attribute formats
+
+At build time, attribute definitions are pooled globally across all libraries
+used in an application, which means attribute `format`s *must* be identical for
+a given `name` to avoid a conflict.
+
+Within Jetpack, new attribute names *must* be globally unique. Libraries *may*
+reference existing public attributes from their dependencies. See below for more
+information on public attributes.
+
+When adding a new attribute, the format should be defined *once* in an `<attr
+/>` element in the definitions block at the top of `src/main/res/attrs.xml`.
+Subsequent references in `<declare-styleable>` elements *must* not include a
+`format`:
+
+`src/main/res/attrs.xml`
+
+```xml
+<resources>
+  <attr name="fontProviderPackage" format="string" />
+
+  <declare-styleable name="FontFamily">
+      <attr name="fontProviderPackage" />
+  </declare-styleable>
+</resources>
+```
+
+### Public resources
+
+Library resources are private by default, which means developers are discouraged
+from referencing any defined attributes or values from XML or code; however,
+library resources may be declared public to make them available to developers.
+
+Public library resources are considered API surface and are thus subject to the
+same API consistency and documentation requirements as Java APIs.
+
+Libraries will typically only expose theme attributes, ex. `<attr />` elements,
+as public API so that developers can set and retrieve the values stored in
+styles and themes. Exposing values -- such as `<dimen />` and `<string />` -- or
+images -- such as drawable XML and PNGs -- locks the current state of those
+elements as public API that cannot be changed without a major version bump. That
+means changing a publicly-visible icon would be considered a breaking change.
+
+#### Documentation
+
+All public resource definitions should be documented, including top-level
+definitions and re-uses inside `<styleable>` elements:
+
+`src/main/res/attrs.xml`
+
+```xml
+<resources>
+  <!-- String specifying the application package for a Font Provider. -->
+  <attr name="fontProviderPackage" format="string" />
+
+  <!-- Attributes that are read when parsing a <fontfamily> tag. -->
+  <declare-styleable name="FontFamily">
+      <!-- The package for the Font Provider to be used for the request. This is
+           used to verify the identity of the provider. -->
+      <attr name="fontProviderPackage" />
+  </declare-styleable>
+</resources>
+```
+
+`src/main/res/colors.xml`
+
+```xml
+<resources>
+  <!-- Color for Material Blue-Grey 900. -->
+  <color name="material_blue_grey_900">#ff263238</color>
+</resources>
+```
+
+#### Public declaration
+
+Resources are declared public by providing a separate `<public />` element with
+a matching type:
+
+`src/main/res/public.xml`
+
+```xml
+<resources>
+  <public name="fontProviderPackage" type="attr" />
+  <public name="material_blue_grey_900" type="color" />
+</resources>
+```
+
+#### More information
+
+See also the official Android Gradle Plugin documentation for
+[Private Resources](https://developer.android.com/studio/projects/android-library#PrivateResources).
+
+### Manifest entries (`AndroidManifest.xml`) {#resources-manifest}
+
+#### Metadata tags (`<meta-data>`) {#resources-manifest-metadata}
+
+Developers **must not** add `<application>`-level `<meta-data>` tags to library
+manifests or advise developers to add such tags to their application manifests.
+Doing so may _inadvertently cause denial-of-service attacks against other apps_.
+
+Assume a library adds a single item of meta-data at the application level. When
+an app uses the library, that meta-data will be merged into the resulting app's
+application entry via manifest merger.
+
+If another app attempts to obtain a list of all activities associated with the
+primary app, that list will contain multiple copies of the `ApplicationInfo`,
+each of which in turn contains a copy of the library's meta-data. As a result,
+one `<metadata>` tag may become hundreds of KB on the binder call to obtain the
+list -- resulting in apps hitting transaction too large exceptions and crashing.
+
+```xml {.bad}
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.librarypackage">
+  <application>
+    <meta-data
+        android:name="keyName"
+        android:value="@string/value" />
+  </application>
+</manifest>
+```
+
+Instead, developers may consider adding `<metadata>` nested inside of
+placeholder `<service>` tags.
+
+```xml {.good}
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.librarypackage">
+  <application>
+    <service
+        android:name="androidx.librarypackage.MetadataHolderService"
+        android:enabled="false"
+        android:exported="false">
+      <meta-data
+          android:name="androidx.librarypackage.MetadataHolderService.KEY_NAME"
+          android:resource="@string/value" />
+    </service>
+  </application>
+```
+
+```java {.good}
+package androidx.libraryname.featurename;
+
+/**
+ * A placeholder service to avoid adding application-level metadata. The service
+ * is only used to expose metadata defined in the library's manifest. It is
+ * never invoked.
+ */
+public final class MetadataHolderService {
+  private MetadataHolderService() {}
+
+  @Override
+  public IBinder onBind(Intent intent) {
+    throw new UnsupportedOperationException();
+  }
+}
+```
+
+## Dependencies {#dependencies}
+
+Generally, Jetpack libraries should avoid dependencies that negatively impact
+developers without providing substantial benefit. This includes large
+dependencies where only a small portion is needed, dependencies that slow down
+build times through annotation processing or compiler overhead, and generally
+any dependency that negatively affects system health.
+
+### Kotlin {#dependencies-kotlin}
+
+Kotlin is _recommended_ for new libraries; however, it's important to consider
+its size impact on clients. Currently, the Kotlin stdlib adds a minimum of 40kB
+post-optimization.
+
+### Kotlin coroutines {#dependencies-coroutines}
+
+Kotlin's coroutine library adds around 100kB post-shrinking. New libraries that
+are written in Kotlin should prefer coroutines over `ListenableFuture`, but
+existing libraries must consider the size impact on their clients. See
+[Asynchronous work with return values](#async-return) for more details on using
+Kotlin coroutines in Jetpack libraries.
+
+### Guava {#dependencies-guava}
+
+The full Guava library is very large and *must not* be used. Libraries that
+would like to depend on Guava's `ListenableFuture` may instead depend on the
+standalone `com.google.guava:listenablefuture` artifact. See
+[Asynchronous work with return values](#async-return) for more details on using
+`ListenableFuture` in Jetpack libraries.
+
+### Java 8 {#dependencies-java8}
+
+Libraries that take a dependency on a library targeting Java 8 must _also_
+target Java 8, which will incur a ~5% build performance (as of 8/2019) hit for
+clients. New libraries targeting Java 8 may use Java 8 dependencies; however,
+existing libraries targeting Java 7 should not.
+
+The default language level for `androidx` libraries is Java 8, and we encourage
+libraries to stay on Java 8. However, if you have a business need to target Java
+7, you can specify Java 7 in your `build.gradle` as follows:
+
+```Groovy
+android {
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_1_7
+        targetCompatibility = JavaVersion.VERSION_1_7
+    }
+}
+```
+
+## More API guidelines {#more-api-guidelines}
+
+### Annotations {#annotation}
+
+#### Annotation processors {#annotation-processor}
+
+Annotation processors should opt-in to incremental annotation processing to
+avoid triggering a full recompilation on every client source code change. See
+Gradle's
+[Incremental annotation processing](https://docs.gradle.org/current/userguide/java_plugin.html#sec:incremental_annotation_processing)
+documentation for information on how to opt-in.
+
+### Experimental APIs {#experimental-api}
+
+Jetpack libraries may choose to annotate API surfaces as unstable using either
+Kotlin's
+[`@Experimental` annotation](https://kotlinlang.org/docs/reference/experimental.html)
+for APIs written in Kotlin or Jetpack's
+[`@Experimental` annotation](https://developer.android.com/reference/kotlin/androidx/annotation/experimental/Experimental)
+for APIs written in Java.
+
+In both cases, API surfaces marked as experimental are considered alpha and will
+be excluded from API compatibility guarantees. Due to the lack of compatibility
+guarantees, libraries *must never* call experimental APIs exposed by other
+libraries and *may not* use the `@UseExperimental` annotation except in the
+following cases:
+
+*   A library within a same-version group *may* call an experimental API exposed
+    by another library **within its same-version group**. In this case, API
+    compatibility guarantees are covered under the same-version group policies
+    and the library *may* use the `@UsesExperimental` annotation to prevent
+    propagation of the experimental property. **Library owners must exercise
+    care to ensure that post-alpha APIs backed by experimental APIs actually
+    meet the release criteria for post-alpha APIs.**
+
+#### How to mark an API surface as experimental
+
+All libraries using `@Experimental` annotations *must* depend on the
+`androidx.annotation:annotation-experimental` artifact regardless of whether
+they are using the `androidx` or Kotlin annotation. This artifact provides Lint
+enforcement of experimental usage restrictions for Kotlin callers as well as
+Java (which the Kotlin annotation doesn't handle on its own, since it's a Kotlin
+compiler feature). Libraries *may* include the dependency as `api`-type to make
+`@UseExperimental` available to Java clients; however, this will also
+unnecessarily expose the `@Experimental` annotation.
+
+```java
+dependencies {
+    implementation(project(":annotation:annotation-experimental"))
+}
+```
+
+See Kotlin's
+[experimental marker documentation](https://kotlinlang.org/docs/reference/experimental.html)
+for general usage information. If you are writing experimental Java APIs, you
+will use the Jetpack
+[`@Experimental` annotation](https://developer.android.com/reference/kotlin/androidx/annotation/experimental/Experimental)
+rather than the Kotlin compiler's annotation.
+
+#### How to transition an API out of experimental
+
+When an API surface is ready to transition out of experimental, the annotation
+may only be removed during an alpha pre-release stage since removing the
+experimental marker from an API is equivalent to adding the API to the current
+API surface.
+
+When transitioning an entire feature surface out of experimental, you *should*
+remove the associated annotations.
+
+When making any change to the experimental API surface, you *must* run
+`./gradlew updateApi` prior to uploading your change.
+
+### Restricted APIs {#restricted-api}
+
+Jetpack's library tooling supports hiding Java-visible (ex. `public` and
+`protected`) APIs from developers using a combination of the `@hide` docs
+annotation and `@RestrictTo` source annotation. These annotations **must** be
+paired together when used, and are validated as part of presubmit checks for
+Java code (Kotlin not yet supported by Checkstyle).
+
+The effects of hiding an API are as follows:
+
+*   The API will not appear in documentation
+*   Android Studio will warn the developer not to use the API
+
+Hiding an API does *not* provide strong guarantees about usage:
+
+*   There are no runtime restrictions on calling hidden APIs
+*   Android Studio will not warn if hidden APIs are called using reflection
+*   Hidden APIs will still show in Android Studio's auto-complete
+
+#### When to use `@hide` {#restricted-api-usage}
+
+Generally, avoid using `@hide`. The `@hide` annotation indicates that developers
+should not call an API that is _technically_ public from a Java visibility
+perspective. Hiding APIs is often a sign of a poorly-abstracted API surface, and
+priority should be given to creating public, maintainable APIs and using Java
+visibility modifiers.
+
+*Do not* use `@hide` to bypass API tracking and review for production APIs;
+instead, rely on API+1 and API Council review to ensure APIs are reviewed on a
+timely basis.
+
+*Do not* use `@hide` for implementation detail APIs that are used between
+libraries and could reasonably be made public.
+
+*Do* use `@hide` paired with `@RestrictTo(LIBRARY)` for implementation detail
+APIs used within a single library (but prefer Java language `private` or
+`default` visibility).
+
+#### `RestrictTo.Scope` and inter- versus intra-library API surfaces {#private-api-types}
+
+To maintain binary compatibility between different versions of libraries,
+restricted API surfaces that are used between libraries (inter-library APIs)
+must follow the same Semantic Versioning rules as public APIs. Inter-library
+APIs should be annotated with the `@RestrictTo(LIBRARY_GROUP)` source
+annotation.
+
+Restricted API surfaces used within a single library (intra-library APIs), on
+the other hand, may be added or removed without any compatibility
+considerations. It is safe to assume that developers _never_ call these APIs,
+even though it is technically feasible. Intra-library APIs should be annotated
+with the `@RestrictTo(LIBRARY)` source annotation.
+
+The following table shows the visibility of a hypothetical API within Maven
+coordinate `androidx.concurrent:concurrent` when annotated with a variety of
+scopes:
+
+<table>
+    <tr>
+        <td><code>RestrictTo.Scope</code></td>
+        <td>Visibility by Maven coordinate</td>
+    </tr>
+    <tr>
+        <td><code>LIBRARY</code></td>
+        <td><code>androidx.concurrent:concurrent</code></td>
+    </tr>
+    <tr>
+        <td><code>LIBRARY_GROUP</code></td>
+        <td><code>androidx.concurrent:*</code></td>
+    </tr>
+    <tr>
+        <td><code>LIBRARY_GROUP_PREFIX</code></td>
+        <td><code>androidx.*:*</code></td>
+    </tr>
+</table>
+
+### Constructors {#constructors}
+
+#### View constructors {#view-constructors}
+
+The four-arg View constructor -- `View(Context, AttributeSet, int, int)` -- was
+added in SDK 21 and allows a developer to pass in an explicit default style
+resource rather than relying on a theme attribute to resolve the default style
+resource. Because this API was added in SDK 21, care must be taken to ensure
+that it is not called through any < SDK 21 code path.
+
+Views _may_ implement a four-arg constructor in one of the following ways:
+
+1.  Do not implement.
+1.  Implement and annotate with `@RequiresApi(21)`. This means the three-arg
+    constructor **must not** call into the four-arg constructor.
+
+### Asynchronous work {#async}
+
+#### With return values {#async-return}
+
+Traditionally, asynchronous work on Android that results in an output value
+would use a callback; however, better alternatives exist for libraries.
+
+Kotlin libraries should prefer
+[coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) and
+`suspend` functions, but please refer to the guidance on
+[allowable dependencies](#dependencies-coroutines) before adding a new
+dependency on coroutines.
+
+Java libraries should prefer `ListenableFuture` and the
+[`CallbackToFutureAdapter`](https://developer.android.com/reference/androidx/concurrent/futures/CallbackToFutureAdapter)
+implementation provided by the `androidx.concurrent:concurrent-futures` library.
+
+Libraries **must not** use `java.util.concurrent.CompletableFuture`, as it has a
+large API surface that permits arbitrary mutation of the future's value and has
+error-prone defaults.
+
+See the [Dependencies](#dependencies) section for more information on using
+Kotlin coroutines and Guava in your library.
+
+#### Avoid `synchronized` methods
+
+Whenever multiple threads are interacting with shared (mutable) references those
+reads and writes must be synchronized in some way. However synchronized blocks
+make your code thread-safe at the expense of concurrent execution. Any time
+execution enters a synchronized block or method any other thread trying to enter
+a synchronized block on the same object has to wait; even if in practice the
+operations are unrelated (e.g. they interact with different fields). This can
+dramatically reduce the benefit of trying to write multi-threaded code in the
+first place.
+
+Locking with synchronized is a heavyweight form of ensuring ordering between
+threads, and there are a number of common APIs and patterns that you can use
+that are more lightweight, depending on your use case:
+
+*   Compute a value once and make it available to all threads
+*   Update Set and Map data structures across threads
+*   Allow a group of threads to process a stream of data concurrently
+*   Provide instances of a non-thread-safe type to multiple threads
+*   Update a value from multiple threads atomically
+*   Maintain granular control of your concurrency invariants
+
+### Kotlin {#kotlin}
+
+#### Data classes {#kotlin-data}
+
+Kotlin `data` classes provide a convenient way to define simple container
+objects, where Kotlin will generate `equals()` and `hashCode()` for you.
+However, they are not designed to preserve API/binary compatibility when members
+are added. This is due to other methods which are generated for you -
+[destructuring declarations](https://kotlinlang.org/docs/reference/multi-declarations.html),
+and [copying](https://kotlinlang.org/docs/reference/data-classes.html#copying).
+
+Example data class as tracked by metalava:
+
+<pre>
+  public final class TargetAnimation {
+    ctor public TargetAnimation(float target, androidx.animation.AnimationBuilder animation);
+    <b>method public float component1();</b>
+    <b>method public androidx.animation.AnimationBuilder component2();</b>
+    <b>method public androidx.animation.TargetAnimation copy(float target, androidx.animation.AnimationBuilder animation);</b>
+    method public androidx.animation.AnimationBuilder getAnimation();
+    method public float getTarget();
+  }
+</pre>
+
+Because members are exposed as numbered components for destructuring, you can
+only safely add members at the end of the member list. As `copy` is generated
+with every member name in order as well, you'll also have to manually
+re-implement any old `copy` variants as items are added. If these constraints
+are acceptable, data classes may still be useful to you.
+
+As a result, Kotlin `data` classes are _strongly discouraged_ in library APIs.
+Instead, follow best-practices for Java data classes including implementing
+`equals`, `hashCode`, and `toString`.
+
+See Jake Wharton's article on
+[Public API challenges in Kotlin](https://jakewharton.com/public-api-challenges-in-kotlin/)
+for more details.
+
+#### Extension and top-level functions {#kotlin-extension-functions}
+
+If your Kotlin file contains any sybmols outside of class-like types
+(extension/top-level functions, properties, etc), the file must be annotated
+with `@JvmName`. This ensures unanticipated use-cases from Java callers don't
+get stuck using `BlahKt` files.
+
+Example:
+
+```kotlin {.bad}
+package androidx.example
+
+fun String.foo() = // ...
+```
+
+```kotlin {.good}
+@file:JvmName("StringUtils")
+
+package androidx.example
+
+fun String.foo() = // ...
+```
+
+NOTE This guideline may be ignored for libraries that only work in Kotlin (think
+Compose).
+
+## Testing Guidelines
+
+### [Do not Mock, AndroidX](do_not_mock.md)
+
+## Android Lint Guidelines
+
+### Suppression vs Baselines
+
+Lint sometimes flags false positives, even though it is safe to ignore these
+errors (for example WeakerAccess warnings when you are avoiding synthetic
+access). There may also be lint failures when your library is in the middle of a
+beta / rc / stable release, and cannot make the breaking changes needed to fix
+the root cause. There are two ways of ignoring lint errors:
+
+1.  Suppression - using `@SuppressLint` (for Java) or `@Suppress` annotations to
+    ignore the warning per call site, per method, or per file. *Note
+    `@SuppressLint` - Requires Android dependency*.
+2.  Baselines - allowlisting errors in a lint-baseline.xml file at the root of
+    the project directory.
+
+Where possible, you should use a **suppression annotation at the call site**.
+This helps ensure that you are only suppressing the *exact* failure, and this
+also keeps the failure visible so it can be fixed later on. Only use a baseline
+if you are in a Java library without Android dependencies, or when enabling a
+new lint check, and it is prohibitively expensive / not possible to fix the
+errors generated by enabling this lint check.
+
+To update a lint baseline (lint-baseline.xml) after you have fixed issues, add
+`-PupdateLintBaseline` to the end of your lint command. This will delete and
+then regenerate the baseline file.
+
+```shell
+./gradlew core:lintDebug -PupdateLintBaseline
+```
+
+## Metalava API Lint
+
+As well as Android Lint, which runs on all source code, Metalava will also run
+checks on the public API surface of each library. Similar to with Android Lint,
+there can sometimes be false positives / intended deviations from the API
+guidelines that Metalava will lint your API surface against. When this happens,
+you can suppress Metalava API lint issues using `@SuppressLint` (for Java) or
+`@Suppress` annotations. In cases where it is not possible, update Metalava's
+baseline with the `updateApiLintBaseline` task.
+
+```shell
+./gradlew core:updateApiLintBaseline
+```
+
+This will create/amend the `api_lint.ignore` file that lives in a library's
+`api` directory.
+
+## Build Output Guidelines
+
+In order to more easily identify the root cause of build failures, we want to
+keep the amount of output generated by a successful build to a minimum.
+Consequently, we track build output similarly to the way in which we track Lint
+warnings.
+
+### Invoking build output validation
+
+You can add `-Pandroidx.validateNoUnrecognizedMessages` to any other AndroidX
+gradlew command to enable validation of build output. For example:
+
+```shell
+/gradlew -Pandroidx.validateNoUnrecognizedMessages :help
+```
+
+### Exempting new build output messages
+
+Please avoid exempting new build output and instead fix or suppress the warnings
+themselves, because that will take effect not only on the build server but also
+in Android Studio, and will also run more quickly.
+
+If you cannot prevent the message from being generating and must exempt the
+message anyway, follow the instructions in the error:
+
+```shell
+$ ./gradlew -Pandroidx.validateNoUnrecognizedMessages :help
+
+Error: build_log_simplifier.py found 15 new messages found in /usr/local/google/workspace/aosp-androidx-git/out/dist/gradle.log.
+
+Please fix or suppress these new messages in the tool that generates them.
+If you cannot, then you can exempt them by doing:
+
+  1. cp /usr/local/google/workspace/aosp-androidx-git/out/dist/gradle.log.ignore /usr/local/google/workspace/aosp-androidx-git/frameworks/support/development/build_log_simplifier/messages.ignore
+  2. modify the new lines to be appropriately generalized
+```
+
+Each line in this exemptions file is a regular expressing matching one or more
+lines of output to be exempted. You may want to make these expressions as
+specific as possible to ensure that the addition of new, similar messages will
+also be detected (for example, discovering an existing warning in a new source
+file).
+
+## Behavior changes
+
+### Changes that affect API documentation
+
+Do not make behavior changes that require altering API documentation in a way
+that would break existing clients, even if such changes are technically binary
+compatible. For example, changing the meaning of a method's return value to
+return true rather than false in a given state would be considered a breaking
+change. Because this change is binary-compatible, it will not be caught by
+tooling and is effectively invisible to clients.
+
+Instead, add new methods and deprecate the existing ones if necessary, noting
+behavior changes in the deprecation message.
+
+### High-risk behavior changes
+
+Behavior changes that conform to documented API contracts but are highly complex
+and difficult to comprehensively test are considered high-risk and should be
+implemented using behavior flags. These changes may be flagged on initially, but
+the original behaviors must be preserved until the library enters release
+candidate stage and the behavior changes have been appropriately verified by
+integration testing against public pre-release
+revisions.
+
+It may be necessary to soft-revert a high-risk behavior change with only 24-hour
+notice, which should be achievable by flipping the behavior flag to off.
+
+```java
+[example code pending]
+```
+
+Avoid adding multiple high-risk changes during a feature cycle, as verifying the
+interaction of multiple feature flags leads to unnecessary complexity and
+exposes clients to high risk even when a single change is flagged off. Instead,
+wait until one high-risk change has landed in RC before moving on to the next.
+
+#### Testing
+
+Relevant tests should be run for the behavior change in both the on and off
+flagged states to prevent regressions.
+
+## Sample code in Kotlin modules
+
+### Background
+
+Public API can (and should!) have small corresponding code snippets that
+demonstrate functionality and usage of a particular API. These are often exposed
+inline in the documentation for the function / class - this causes consistency
+and correctness issues as this code is not compiled against, and the underlying
+implementation can easily change.
+
+KDoc (JavaDoc for Kotlin) supports a `@sample` tag, which allows referencing the
+body of a function from documentation. This means that code samples can be just
+written as a normal function, compiled and linted against, and reused from other
+modules such as tests! This allows for some guarantees on the correctness of a
+sample, and ensuring that it is always kept up to date.
+
+### Enforcement
+
+There are still some visibility issues here - it can be hard to tell if a
+function is a sample, and is used from public documentation - so as a result we
+have lint checks to ensure sample correctness.
+
+Primarily, there are three requirements when using sample links:
+
+1.  All functions linked to from a `@sample` KDoc tag must be annotated with
+    `@Sampled`
+2.  All sample functions annotated with `@Sampled` must be linked to from a
+    `@sample` KDoc tag
+3.  All sample functions must live inside a separate `samples` library
+    submodule - see the section on module configuration below for more
+    information.
+
+This enforces visibility guarantees, and make it easier to know that a sample is
+a sample. This also prevents orphaned samples that aren't used, and remain
+unmaintained and outdated.
+
+### Sample usage
+
+The follow demonstrates how to reference sample functions from public API. It is
+also recommended to reuse these samples in unit tests / integration tests / test
+apps / library demos where possible.
+
+**Public API:**
+
+```
+/*
+ * Fancy prints the given [string]
+ *
+ * @sample androidx.printer.samples.fancySample
+ */
+fun fancyPrint(str: String) ...
+```
+
+**Sample function:**
+
+```
+package androidx.printer.samples
+
+import androidx.printer.fancyPrint
+
+@Sampled
+fun fancySample() {
+   fancyPrint("Fancy!")
+}
+```
+
+**Generated documentation visible on d.android.com\***
+
+```
+fun fancyPrint(str: String)
+
+Fancy prints the given [string]
+
+<code>
+ import androidx.printer.fancyPrint
+
+ fancyPrint("Fancy!")
+<code>
+```
+
+\**still some improvements to be made to DAC side, such as syntax highlighting*
+
+### Module configuration
+
+The following module setups should be used for sample functions, and are
+enforced by lint:
+
+**Group-level samples**
+
+For library groups with strongly related samples that want to share code.
+
+Gradle project name: `:foo-library:samples`
+
+```
+foo-library/
+  foo-module/
+  bar-module/
+  samples/
+```
+
+**Per-module samples**
+
+For library groups with complex, relatively independent sub-libraries
+
+Gradle project name: `:foo-library:foo-module:samples`
+
+```
+foo-library/
+  foo-module/
+    samples/
+```
diff --git a/docs/benchmarking.md b/docs/benchmarking.md
new file mode 100644
index 0000000..cc38f78
--- /dev/null
+++ b/docs/benchmarking.md
@@ -0,0 +1,273 @@
+# Benchmarking in AndroidX
+
+[TOC]
+
+The public documentation at
+[d.android.com/benchmark](http://d.android.com/benchmark) explains how to use
+the library - this page focuses on specifics to writing libraries in the
+AndroidX repo, and our continuous testing / triage process.
+
+### Writing the benchmark
+
+Benchmarks are just regular instrumentation tests! Just use the
+[`BenchmarkRule`](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/benchmark/junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt)
+provided by the library:
+
+<section class="tabs">
+
+#### Kotlin {.new-tab}
+
+```kotlin
+@RunWith(AndroidJUnit4::class)
+class ViewBenchmark {
+    @get:Rule
+    val benchmarkRule = BenchmarkRule()
+
+    @Test
+    fun simpleViewInflate() {
+        val context = InstrumentationRegistry
+                .getInstrumentation().targetContext
+        val inflater = LayoutInflater.from(context)
+        val root = FrameLayout(context)
+
+        benchmarkRule.measure {
+            inflater.inflate(R.layout.test_simple_view, root, false)
+        }
+    }
+}
+```
+
+#### Java {.new-tab}
+
+```java
+@RunWith(AndroidJUnit4.class)
+public class ViewBenchmark {
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
+
+    @Test
+    public void simpleViewInflate() {
+        Context context = InstrumentationRegistry
+                .getInstrumentation().getTargetContext();
+        final BenchmarkState state = mBenchmarkRule.getState();
+        LayoutInflater inflater = LayoutInflater.from(context);
+        FrameLayout root = new FrameLayout(context);
+
+        while (state.keepRunning()) {
+            inflater.inflate(R.layout.test_simple_view, root, false);
+        }
+    }
+}
+```
+
+</section>
+
+## Project structure
+
+As in the public documentation, benchmarks in the AndroidX repo are test-only
+library modules. Differences for AndroidX repo:
+
+1.  Module name must end with `-benchmark` in `settings.gradle`.
+2.  You do not need to apply the benchmark plugin (it's pulled in automatically
+    from source)
+
+### I'm lazy and want to start quickly
+
+Start by copying one of the following projects:
+
+*   [navigation-benchmark](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/navigation/benchmark/)
+*   [recyclerview-benchmark](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/recyclerview/recyclerview-benchmark/)
+
+### Compose
+
+Compose builds the benchmark from source, so usage matches the rest of the
+AndroidX project. See existing Compose benchmark projects:
+
+*   [Compose UI benchmarks](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/ui/integration-tests/benchmark/)
+*   [Compose Runtime benchmarks](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/compose/compose-runtime/compose-runtime-benchmark/)
+
+## Profiling
+
+### Command Line
+
+The benchmark library supports capturing profiling information - sampled and
+method - from the command line. Here's an example which runs the
+`androidx.ui.benchmark.test.CheckboxesInRowsBenchmark#draw` method with
+`MethodSampling` profiling:
+
+```
+./gradlew compose:integ:bench:cC \
+    -P android.testInstrumentationRunnerArguments.androidx.benchmark.profiling.mode=MethodSampling \
+    -P android.testInstrumentationRunnerArguments.class=androidx.ui.benchmark.test.CheckboxesInRowsBenchmark#draw
+```
+
+The command output will tell you where to look for the file on your host
+machine:
+
+```
+04:33:49 I/Benchmark: Benchmark report files generated at
+/androidx-master-dev/out/ui/ui/integration-tests/benchmark/build/outputs/connected_android_test_additional_output
+```
+
+To inspect the captured trace, open the appropriate `*.trace` file in that
+directory with Android Studio, using `File > Open`.
+
+For more information on the `MethodSampling` and `MethodTracing` profiling
+modes, see the
+[Studio Profiler configuration docs](https://developer.android.com/studio/profile/cpu-profiler#configurations),
+specifically Java Sampled Profiling, and Java Method Tracing.
+
+![Sample flame chart](benchmarking_images/profiling_flame_chart.png "Sample flame chart")
+
+### Advanced: Simpleperf Method Sampling
+
+[Simpleperf](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/)
+offers more accurate profiling for apps than standard method sampling, due to
+lower overhead (as well as C++ profiling support). Simpleperf support will be
+simplified and improved over time.
+
+[Simpleperf app profiling docs](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/android_application_profiling.md).
+
+#### Device
+
+Get an API 28+ device (Or a rooted API 27 device). The rest of this section is
+about *why* those constraints exist, skip if not interested.
+
+Simpleperf has restrictions about where it can be used - Jetpack Benchmark will
+only support API 28+ for now, due to
+[platform/simpleperf constraints](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/android_application_profiling.md#prepare-an-android-application)
+(see last subsection titled "If you want to profile Java code"). Summary is:
+
+-   <=23 (M): Unsupported for Java code.
+
+-   24-25 (N): Requires compiled Java code. We haven't investigated support.
+
+-   26 (O): Requires compiled Java code, and wrapper script. We haven't
+    investigated support.
+
+-   27 (P): Can profile all Java code, but requires `userdebug`/rooted device
+
+-   \>=28 (Q): Can profile all Java code, requires profileable (or
+    `userdebug`/rooted device)
+
+We aren't planning to support profiling debuggable APK builds, since they're
+misleading for profiling.
+
+#### Initial setup
+
+Currently, we rely on Python scripts built by the simpleperf team. We can
+eventually build this into the benchmark library / gradle plugin. Download the
+scripts from AOSP:
+
+```
+# copying to somewhere outside of the androidx repo
+git clone https://android.googlesource.com/platform/system/extras ~/simpleperf
+```
+
+Next configure your path to ensure the ADB that the scripts will use matches the
+androidx tools:
+
+```
+export PATH=$PATH:<path/to/androidx>/prebuilts/fullsdk-<linux or darwin>/platform-tools
+```
+
+Now, setup your device for simpleperf:
+
+```
+~/simpleperf/simpleperf/scripts/api_profiler.py prepare --max-sample-rate 10000000
+```
+
+#### Build and Run, Option 1: Studio (slightly recommended)
+
+Running from Studio is simpler, since you don't have to manually install and run
+the APKs, avoiding Gradle.
+
+Add the following to the benchmark module's build.gradle:
+
+```
+android {
+    defaultConfig {
+        // DO NOT COMMIT!!
+        testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'MethodSamplingSimpleperf'
+        // Optional: Control freq / duration.
+        testInstrumentationRunnerArgument 'androidx.benchmark.profiler.sampleFrequency', '1000000'
+        testInstrumentationRunnerArgument 'androidx.benchmark.profiler.sampleDurationSeconds', '5'
+    }
+}
+```
+
+And run the test or tests you'd like to measure from within Studio.
+
+#### Build and Run, Option 2: Command Line
+
+**Note - this will be significantly simplified in the future**
+
+Since we're not using AGP to pull the files yet, we can't invoke the benchmark
+through Gradle, because Gradle uninstalls after each test run. Instead, let's
+just build and run manually:
+
+```
+./gradlew compose:integration-tests:benchmark:assembleReleaseAndroidTest
+
+adb install -r ../../../out/ui/compose/integration-tests/benchmark/build/outputs/apk/androidTest/release/benchmark-release-androidTest.apk
+
+# run the test (can copy this line from Studio console, when running a benchmark)
+adb shell am instrument -w -m --no-window-animation -e androidx.benchmark.profiling.mode MethodSamplingSimpleperf -e debug false -e class 'androidx.ui.benchmark.test.CheckboxesInRowsBenchmark#toggleCheckbox_draw' androidx.ui.benchmark.test/androidx.benchmark.junit4.AndroidBenchmarkRunner
+```
+
+#### Pull and open the trace
+
+```
+# move the files to host
+# (Note: removes files from device)
+~/simpleperf/simpleperf/scripts/api_profiler.py collect -p androidx.ui.benchmark.test -o ~/simpleperf/results
+
+# create/open the HTML report
+~/simpleperf/simpleperf/scripts/report_html.py -i ~/simpleperf/results/CheckboxesInRowsBenchmark_toggleCheckbox_draw\[1\].data
+```
+
+### Advanced: Studio Profiling
+
+Profiling for allocations and simpleperf profiling requires Studio to capture.
+
+Studio profiling tools require `debuggable=true`. First, temporarily override it
+in your benchmark's `androidTest/AndroidManifest.xml`.
+
+Next choose which profiling you want to do: Allocation, or Sampled (SimplePerf)
+
+`ConnectedAllocation` will help you measure the allocations in a single run of a
+benchmark loop, after warmup.
+
+`ConnectedSampled` will help you capture sampled profiling, but with the more
+detailed / accurate Simpleperf sampling.
+
+Set the profiling type in your benchmark module's `build.gradle`:
+
+```
+android {
+    defaultConfig {
+        // Local only, don't commit this!
+        testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'ConnectedAllocation'
+    }
+}
+```
+
+Run `File > Sync Project with Gradle Files`, or sync if Studio asks you. Now any
+benchmark runs in that project will permit debuggable, and pause before and
+after the test, to allow you to connect a profiler and start recording, and then
+stop recording.
+
+#### Running and Profiling
+
+After the benchmark test starts, you have about 20 seconds to connect the
+profiler:
+
+1.  Click the profiler tab at the bottom
+1.  Click the plus button in the top left, `<device name>`, `<process name>`
+1.  Next step depends on which you intend to capture
+
+#### Allocations
+
+Click the memory section, and right click the window, and select `Record
+allocations`. Approximately 20 seconds later, right click again and select `Stop
+recording`.
diff --git a/docs/benchmarking_images/filter_build.png b/docs/benchmarking_images/filter_build.png
new file mode 100644
index 0000000..f4d15d4
--- /dev/null
+++ b/docs/benchmarking_images/filter_build.png
Binary files differ
diff --git a/docs/benchmarking_images/filter_initial.png b/docs/benchmarking_images/filter_initial.png
new file mode 100644
index 0000000..61bdc30
--- /dev/null
+++ b/docs/benchmarking_images/filter_initial.png
Binary files differ
diff --git a/docs/benchmarking_images/filter_test.png b/docs/benchmarking_images/filter_test.png
new file mode 100644
index 0000000..9a8a0ee
--- /dev/null
+++ b/docs/benchmarking_images/filter_test.png
Binary files differ
diff --git a/docs/benchmarking_images/profiling_flame_chart.png b/docs/benchmarking_images/profiling_flame_chart.png
new file mode 100644
index 0000000..33cd76f
--- /dev/null
+++ b/docs/benchmarking_images/profiling_flame_chart.png
Binary files differ
diff --git a/docs/benchmarking_images/result_plot.png b/docs/benchmarking_images/result_plot.png
new file mode 100644
index 0000000..d0b878e
--- /dev/null
+++ b/docs/benchmarking_images/result_plot.png
Binary files differ
diff --git a/docs/benchmarking_images/triage_bug.png b/docs/benchmarking_images/triage_bug.png
new file mode 100644
index 0000000..816122c
--- /dev/null
+++ b/docs/benchmarking_images/triage_bug.png
Binary files differ
diff --git a/docs/benchmarking_images/triage_cl_list.png b/docs/benchmarking_images/triage_cl_list.png
new file mode 100644
index 0000000..d65a7e2
--- /dev/null
+++ b/docs/benchmarking_images/triage_cl_list.png
Binary files differ
diff --git a/docs/benchmarking_images/triage_complete.png b/docs/benchmarking_images/triage_complete.png
new file mode 100644
index 0000000..3a04763
--- /dev/null
+++ b/docs/benchmarking_images/triage_complete.png
Binary files differ
diff --git a/docs/benchmarking_images/triage_graph.png b/docs/benchmarking_images/triage_graph.png
new file mode 100644
index 0000000..90defbc
--- /dev/null
+++ b/docs/benchmarking_images/triage_graph.png
Binary files differ
diff --git a/docs/benchmarking_images/triage_initial.png b/docs/benchmarking_images/triage_initial.png
new file mode 100644
index 0000000..ea0d033
--- /dev/null
+++ b/docs/benchmarking_images/triage_initial.png
Binary files differ
diff --git a/docs/benchmarking_images/triage_regression.png b/docs/benchmarking_images/triage_regression.png
new file mode 100644
index 0000000..bed4b5f
--- /dev/null
+++ b/docs/benchmarking_images/triage_regression.png
Binary files differ
diff --git a/docs/benchmarking_images/triage_word_cloud.png b/docs/benchmarking_images/triage_word_cloud.png
new file mode 100644
index 0000000..35dbe43
--- /dev/null
+++ b/docs/benchmarking_images/triage_word_cloud.png
Binary files differ
diff --git a/docs/branching.md b/docs/branching.md
new file mode 100644
index 0000000..8b5cd06
--- /dev/null
+++ b/docs/branching.md
@@ -0,0 +1,40 @@
+# AndroidX Branch Workflow
+
+[TOC]
+
+## Single Development Branch [androidx-master-dev]
+
+All feature development occurs in the public AndroidX master dev branch of the
+Android Open Source Project: `androidx-master-dev`. This branch serves as the
+central location and source of truth for all AndroidX library source code. All
+alpha and beta version development, builds, and releases will be done ONLY in
+this branch.
+
+## Release Branches [androidx-\<feature\>-release]
+
+When a library updates to rc (release-candidate) or stable, that library version
+will be snapped over to that library’s release branch. If that release branch
+doesn’t exist, then a release branch will be created for that library, snapped
+from androidx-master-dev at the commit that changed the library to an rc or
+stable version.
+
+Release branches have the following properties:
+
+*   A release branch will contain rc or stable versions of libraries.
+*   Release branches are internal branches.
+*   Release branches can **ONLY** be changed through
+    cherry-picks
+*   Bug-fixes and updates to that rc or stable version will need to be
+    individually cherry-picked
+*   No alpha or beta versions will exist in a release branch.
+*   Toolchain and other library wide changes to androidx-master-dev will be
+    synced to each release branch.
+*   Release branches will have the naming format
+    `androidx-<feature-name>-release`
+*   Release branches will be re-snapped from `androidx-master-dev` for each new
+    minor version release (for example, releasing 2.2.0-rc01 after 2.1.0)
+
+## Platform Developement and AndroidX [androidx-platform-dev]
+
+Platform specific development is done using our INTERNAL platform development
+branch `androidx-platform-dev`.
diff --git a/docs/branching_images/cs_branch_switcher.png b/docs/branching_images/cs_branch_switcher.png
new file mode 100644
index 0000000..660a877
--- /dev/null
+++ b/docs/branching_images/cs_branch_switcher.png
Binary files differ
diff --git a/docs/branching_images/cs_change.png b/docs/branching_images/cs_change.png
new file mode 100644
index 0000000..15c7475
--- /dev/null
+++ b/docs/branching_images/cs_change.png
Binary files differ
diff --git a/docs/branching_images/cs_editor.png b/docs/branching_images/cs_editor.png
new file mode 100644
index 0000000..72ec1ee
--- /dev/null
+++ b/docs/branching_images/cs_editor.png
Binary files differ
diff --git a/docs/branching_images/jetpack_branch_workflow.png b/docs/branching_images/jetpack_branch_workflow.png
new file mode 100644
index 0000000..ca4b094
--- /dev/null
+++ b/docs/branching_images/jetpack_branch_workflow.png
Binary files differ
diff --git a/docs/branching_images/release_branch_diagram.png b/docs/branching_images/release_branch_diagram.png
new file mode 100644
index 0000000..7b54025
--- /dev/null
+++ b/docs/branching_images/release_branch_diagram.png
Binary files differ
diff --git a/docs/do_not_mock.md b/docs/do_not_mock.md
new file mode 100644
index 0000000..d614001
--- /dev/null
+++ b/docs/do_not_mock.md
@@ -0,0 +1,123 @@
+# Do Not Mock, AndroidX
+
+All APIs created in AndroidX **must have a testing story**: how developers
+should write tests for their code that relies on a library, this story should
+not be "use mockito to mock class `Foo`". Your goal as API owner is to **create
+better alternatives** to mocking.
+
+## Why can't I suggest mocks as testing strategy?
+
+Frequently mocks don't follow guarantees outlined in the API they mock. That
+leads to:
+
+*   Significant difference in the behavior that diminishes test value.
+*   Brittle tests, that make hard to evolve both apps and libraries, because new
+    code may start to rely on the guarantees broken in a mock. Let's take a look
+    at a simplified example. So, let's say you mocked a bundle and getString in
+    it:
+
+    ```java
+    Bundle mock = mock(Bundle.class);
+    when(mock.getString("key")).thenReturn("result");
+    ```
+
+    But you don't mock it to simply call `getString()` in your test. A goal is
+    not to test a mock, the goal is always to test your app code, so your app
+    code always interacts with a mock in some way:
+
+    ```java
+    Bundle bundle = mock(Bundle.class);
+    when(mock.getString("key")).thenReturn("result");
+    mycomponent.consume(bundle)
+    ```
+
+    Originally the test worked fine, but over time `component.consume` is
+    evolving, and, for example, it may start to call `containsKey` on the given
+    bundle. But our test passes a mock that don't expect such call and, boom,
+    test is broken. However, component code is completely valid and has nothing
+    to do with the broken test. We observed a lot of issues like that during
+    updates of android SDK and AndroidX libraries to newer versions internally
+    at google. Suggesting to mock our own components is shooting ourselves in
+    the foot, it will make adoption of newer version of libraries even slower.
+
+*   Messy tests. It always starts with simple mock with one method, but then
+    this mock grows with the project, and as a result test code has sub-optimal
+    half-baked class implementation of on top of the mock.
+
+## But it is ok to mock interfaces, right?
+
+It depends. There are interfaces that don't imply any behavior guarantees and
+they are ok to be mocked. However, **not all** interfaces are like that: for
+example, `Map` is an interface but it has a lot of contracts required from
+correct implementation. Examples of interfaces that are ok to mock are callback
+interfaces in general, for example: `View.OnClickListener`, `Runnable`.
+
+## What about spying?
+
+Spying on these classes is banned as well - mockito spies permit stubbing of
+methods just like mocks do, and interaction verification is brittle and
+unnecessary for these classes. Rather than verifying an interaction with a
+class, developers should observe the result of an interaction - the effect of a
+task submitted to an `Executor`, or the presence of a fragment added to your
+layout. If an API in your library misses a way to have such checks, you should
+add methods to do that. If you think it is dangerous to open such methods in the
+main surface of your library, consult with
+[API council](https://sites.google.com/corp/google.com/android-api-council), it
+may have seen similar patterns before. For example, one of the possible ways to
+resolve such issue can be adding test artifact with special capabilities. So
+`fragment-testing` module was created to drive lifecycle of Fragment and ease
+interaction with fragments in tests.
+
+## Avoid mockito in your own tests.
+
+One of the things that would help you to identify if your library is testable
+without mockito is not using mockito yourself. Yes, historically we heavily
+relied on mockito ourselves and old tests are not rewritten, but new tests
+shouldn't follow up that and should take as an example good citizens, for
+example, `-ktx` modules. These modules don't rely on mockito and have concise
+expressive tests.
+
+One of the popular and legit patterns for mockito usage were tests that verify
+that a simple callback-like interface receives correct parameters.
+
+```java
+class MyApi {
+   interface Callback {
+     void onFoo(Value value);
+  }
+  void foo() { … }
+  void registerFooCallback(Callback callback) {...}
+}
+```
+
+In api like the one above, in java 7 tests for value received in `Callback`
+tended to become very wordy without mockito. But now in your tests you can use
+Kotlin and test will be as short as with mockito:
+
+```kotlin
+fun test() {
+    var receivedValue = null
+    myApi.registerCallback { value -> receivedValue = value }
+    myApi.foo()
+   // verify receivedValue
+}
+```
+
+## Don't compromise in API to enable mockito
+
+Mockito on android
+[had an issue](https://github.com/mockito/mockito/issues/1173) with mocking
+final classes. Moreover, internally at google this feature is disabled even for
+non-android code. So you may hear complaints that some of your classes are not
+mockable, however **It is not a reason for open up a class for extension**. What
+you should instead is verify that is possible to write the same test without
+mocking, if not, again you should **provide better alternative in your API**.
+
+## How do I approach testing story for my API?
+
+Best way is to step into developer's shoes and write a sample app that is a
+showcase for your API, then go to the next step - test that code also. If you
+are able to implement tests for your demo app, then users of your API will also
+be able to implement tests for functionalities where your API is also used.
+
+## ~~Use @DoNotMock on most of your APIs ~~(Not available yet)
diff --git a/docs/faq.md b/docs/faq.md
new file mode 100644
index 0000000..786cc23
--- /dev/null
+++ b/docs/faq.md
@@ -0,0 +1,171 @@
+# FAQ
+
+[TOC]
+
+## General FAQ
+
+### What is AndroidX?
+
+The Android Extension (AndroidX) Libraries provide functionality that extends
+the capabilities of the Android platform. These libraries, which ship separately
+from the Android OS, focus on improving the experience of developing apps
+through broad OS- and device-level compatibility, high-level abstractions to
+simplify and unify platform features, and other new features that target
+developer pain points. To find out more about AndroidX, see the public
+documentation on developer.android.com.
+
+### Why did we move to AndroidX?
+
+Please read our
+[blog post](https://android-developers.googleblog.com/2018/05/hello-world-androidx.html)
+about our migration to AndroidX.
+
+### What happened to the Support Library?
+
+As part of the Jetpack effort to improve developer experience on Android, the
+Support Library team undertook a massive refactoring project. Over the course of
+2017 and 2018, we streamlined and enforced consistency in our packaging,
+developed new policies around vesioning and releasing, and developed tools to
+make it easy for developers to migrate.
+
+### Will there be any more updates to Support Library?
+
+No, Revision 28.0.0 of the Support Library, which launched as stable in
+September 2018, was the last feature release in the android.support package.
+There will be no further releases under Support Library packaging.
+
+### How is AndroidX related to Jetpack?
+
+They are the same thing! In a sentence, AndroidX is the packaging and
+internally-facing development project for all components in Jetpack. Jetpack is
+the external branding for libraries within AndroidX.
+
+In more detail, Jetpack is the external branding for the set of components,
+tools, and guidance that improve the developer experience on Android. AndroidX
+is the open-source development project that defines the workflow, versioning,
+and release policies for ALL libraries included in Jetpack. All libraries within
+the androidx Java package follow a consistent set of API design guidelines,
+conform to SemVer and alpha/beta revision cycles, and use the Android issue
+tracker for bugs and feature requests.
+
+### What AndroidX library versions have been officially released?
+
+You can see all publicly released versions on the interactive
+[Google Maven page](https://dl.google.com/dl/android/maven2/index.html).
+
+### How do I jetify something?
+
+The Standalone Jetifier documentation and download link can be found
+[here](https://developer.android.com/studio/command-line/jetifier), under the
+Android Studio DAC.
+
+### How do I update my library version?
+
+See the steps specified on the version page
+[here](versioning.md#how-to-update-your-version).
+
+### How do I test my change in a separate Android Studio project?
+
+If you're working on a new feature or bug fix in AndroidX, you may want to test
+your changes against another project to verify that the change makes sense in a
+real-world context or that a bug's specific repro case has been fixed.
+
+If you need to be absolutely sure that your test will exactly emulate the
+developer's experience, you can repeatedly build the AndroidX archive and
+rebuild your application. In this case, you will need to create a local build of
+AndroidX's local Maven repository artifact and install it in your Android SDK
+path.
+
+First, use the `createArchive` Gradle task to generate the local Maven
+repository artifact:
+
+```shell
+# Creates <path-to-checkout>/out/dist/sdk-repo-linux-m2repository-##.zip
+./gradlew createArchive
+```
+
+Next, take the ZIP output from this task and extract the contents to the Android
+SDK path that you are using for your alternate (non-AndroidX) version of Android
+Studio. For example, you may be using `~/Android/SDK/extras` if you are using
+the default Android Studio SDK for app development or
+`prebuilts/fullsdk-linux/extras` if you are using fullsdk for platform
+development.
+
+```shell
+# Creates or overwrites android/m2repository
+cd <path-to-sdk>/extras
+unzip <path-to-checkout>/out/dist/top-of-tree-m2repository-##.zip
+```
+
+Finally, in the dependencies section of your standalone project's `build.gradle`
+file, add or update the `compile` entries to reflect the AndroidX modules that
+you would like to test:
+
+```
+dependencies {
+    ...
+    compile "com.android.support:appcompat-v7:26.0.0-SNAPSHOT"
+}
+```
+
+## Version FAQ {#version}
+
+### How are changes in dependency versions propagated?
+
+If you declare `api(project(":depGroupId"))` in your `build.gradle`, then the
+version change will occur automatically. While convienent, be intentional when
+doing so because this causes your library to have a direct dependency on the
+version in development.
+
+If you declare `api("androidx.depGroupId:depArtifactId:1.0.0")`, then the
+version change will need to be done manually and intentionally. This is
+considered best practice.
+
+### How does a library begin work on a new Minor version?
+
+Set the version to the next minor version, as an alpha.
+
+### How does a library ship an API reference documentation bugfix?
+
+Developers obtain API reference documentation from two sources -- HTML docs on
+[d.android.com](https://d.android.com), which are generated from library release
+artifacts, and Javadoc from source JARs on Google Maven.
+
+As a result, documentation bug fixes should be held with other fixes until they
+can go through a normal release cycle. Critical (e.g. P0) documentation issues
+**may** result in a [bugfix](loaf.md#bugfix) release independent of other fixes.
+
+### When does an alpha ship?
+
+For public releases, an alpha ships when the library lead believes it is ready.
+Generally, these occur during the batched bi-weekly (every 2 weeks) release
+because all tip-of-tree dependencies will need to be released too.
+
+### Are there restrictions on when or how often an alpha can ship?
+
+Nope.
+
+### Can Alpha work (ex. for the next Minor release) occur in the primary development branch during Beta API lockdown?
+
+No. This is by design. Focus should be spent on improving the Beta version and
+adding documentation/samples/blog posts for usage!
+
+### Is there an API freeze window between Alpha and Beta while API surface is reviewed and tests are added, but before the Beta is released?
+
+Yes. If any new APIs are added in this window, the beta release will be blocked
+until API review is complete and addressed.
+
+### How often can a Beta release?
+
+As often as needed, however, releases outside of the bi-weekly (every 2 weeks)
+release will need to get approval from the TPM (nickanthony@).
+
+### What are the requirements for moving from Alpha to Beta?
+
+See the [Beta section of Versioning guidelines](versioning.md?#beta) for
+pre-release cycle transition requirements.
+
+### What are the requirements for a Beta launch?
+
+See the [Beta section of Versioning guidelines](versioning.md?#beta) for
+pre-release cycle transition requirements.
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..4a29386
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,47 @@
+# What is Jetpack?
+
+## Jetpack Ethos
+
+To create recommended components, tools, and guidance that makes it quick and
+easy to build great Android apps, including pieces both from Google and from
+trusted OSS sources.
+
+## Team Mission
+
+To improve the Android developer experience by providing architectural guidance,
+addressing common pain points, and simplifying the app development process
+through broad compatibility across Android versions and elimination of
+boilerplate code so developers can focus on what makes their app special.
+
+## What is `androidx`?
+
+Artifacts within the `androidx` package comprise the libraries of
+[Android Jetpack](https://developer.android.com/jetpack).
+
+Libraries in the `androidx` package provide functionality that extends the
+capabilities of the Android platform. These libraries, which ship separately
+from the Android OS, focus on improving the experience of developing apps
+through broad OS- and device-level compatibility, high-level abstractions to
+simplify and unify platform features, and other new features that target
+developer pain points.
+
+## What happened to the Support Library?
+
+As part of the Jetpack project to improve developer experience on Android, the
+Support Library team undertook a massive refactoring project. Over the course of
+2017 and 2018, we streamlined and enforced consistency in our packaging,
+developed new policies around versioning and release, and developed tools to
+make it easy for developers to migrate.
+
+Revision 28.0.0 of the Support Library, which launched as stable in September
+2018, was the last feature release in the `android.support` package. There will
+be no further releases under Support Library packaging.
+
+## Quick links
+
+### Filing an issue
+
+Have a bug or feature request? Please check our
+[public Issue Tracker component](http://issuetracker.google.com/issues/new?component=192731&template=842428)
+for duplicates first, then file against the appropriate sub-component according
+to the library package or infrastructure system.
diff --git a/docs/issue_tracking.md b/docs/issue_tracking.md
new file mode 100644
index 0000000..3413ab2
--- /dev/null
+++ b/docs/issue_tracking.md
@@ -0,0 +1,101 @@
+# Issue Lifecycle and Reporting Guidelines
+
+[TOC]
+
+## Issue tracker
+
+The public-facing issue tracker URL is
+[issuetracker.google.com](https://issuetracker.google.com). If you visit this
+URL from a corp account, it will immediately redirect you to the internal-facing
+issue tracker URL. Make sure that any links you paste publicly have the correct
+public-facing URL.
+
+The top-level Jetpack component is
+[`Android Public Tracker > App Development > Jetpack (androidx)`](https://issuetracker.google.com/components/192731/manage#basic).
+
+## Reporting guidelines
+
+Issue Tracker isn't a developer support forum. For support information, consider
+[StackOverflow](http://stackoverflow.com).
+
+Support for Google apps is through
+[Google's support site](http://support.google.com/). Support for third-party
+apps is provided by the app's developer, for example through the contact
+information provided on Google Play.
+
+1.  Search for your bug to see if anyone has already reported it. Don't forget
+    to search for all issues, not just open ones, as your issue might already
+    have been reported and closed. To help you find the most popular results,
+    sort the result by number of stars.
+
+1.  If you find your issue and it's important to you, star it! The number of
+    stars on a bug helps us know which bugs are most important to fix.
+
+1.  If no one has reported your bug, file the bug. First, browse for the correct
+    component -- typically this has a 1:1 correspondence with Maven group ID --
+    and fill out the provided template.
+
+1.  Include as much information in the bug as you can, following the
+    instructions for the bug queue that you're targeting. A bug that simply says
+    something isn't working doesn't help much, and will probably be closed
+    without any action. The amount of detail that you provide, such as a minimal
+    sample project, log files, repro steps, and even a patch set, helps us
+    address your issue.
+
+## Status definitions
+
+| Status   | Description                                                       |
+| -------- | ----------------------------------------------------------------- |
+| New      | The default for public bugs. Waiting for someone to validate,     |
+:          : reproduce, or otherwise confirm that this is actionable.          :
+| Assigned | Pending action from the assignee. May be reassigned.              |
+| Accepted | Actively being worked on by the assignee. Do not reassign.        |
+| Fixed    | Fixed in the development branch. Do not re-open unless the fix is |
+:          : reverted.                                                         :
+| WontFix  | Covers all the reasons we chose to close the issue without taking |
+:          : action (can't repro, working as intended, obsolete).              :
+
+## Priority criteria and SLOs
+
+| Priority | Criteria                       | Resolution time                |
+| -------- | ------------------------------ | ------------------------------ |
+| P0       | This priority is limited to    | Less than 1 day. Don't go home |
+:          : service outages, blocking      : until this is fixed.           :
+:          : issues, or other types of work :                                :
+:          : stoppage such as issues on the :                                :
+:          : Platform chase list requiring  :                                :
+:          : immediate attention.           :                                :
+| P1       | This priority is limited to    | Within the next 7 days         |
+:          : work that requires rapid       :                                :
+:          : resolution, but can be dealt   :                                :
+:          : with in a slightly longer time :                                :
+:          : window than P0.                :                                :
+| P2       | Won't ship without this.       | Within the current release     |
+| P3       | Would rather not ship without  | Less than 365 days             |
+:          : this, but would decide case by :                                :
+:          : case.                          :                                :
+| P4       | Issue has not yet been         | N/A (must triage in under 14   |
+:          : prioritized (default as of Feb : days)                          :
+:          : 2013).                         :                                :
+
+## Issue lifecycle
+
+1.  When an issue is reported, it is set to **Assigned** status for default
+    assignee (typically the [library owner](owners.md)) with a priority of
+    **P4**.
+    *   Some components have an empty default assignee and will be manually
+        assigned by the [triage cop](triage_cop.md)
+1.  Once an issue has been triaged by the assignee, its priority will be raised
+    from **P4** according to severity.
+1.  The issue may still be reassigned at this point.
+    [Bug bounty](onboarding.md#bug-bounty) issues are likely to change
+    assignees.
+1.  A status of **Accepted** means the assignee is actively working on the
+    issue.
+1.  A status of **Fixed** means that the issue has been resolved in the
+    development branch. Please note that it may take some time for the fix to
+    propagate into various release channels (internal repositories, Google
+    Maven, etc.). **Do not** re-open an issue because the fix has not yet
+    propagated into a specific release channel. **Do not** re-open an issue that
+    has been fixed unless the fix was reverted or the exact reported issue is
+    still occurring.
diff --git a/docs/LINT.md b/docs/lint_guide.md
similarity index 86%
rename from docs/LINT.md
rename to docs/lint_guide.md
index 5916b6d..5e40600 100644
--- a/docs/LINT.md
+++ b/docs/lint_guide.md
@@ -1,5 +1,7 @@
 # Adding custom Lint checks
 
+[TOC]
+
 ## Getting started
 
 Lint is a static analysis tool that checks Android project source files. Lint
@@ -47,8 +49,8 @@
 }
 
 dependencies {
-    // compileOnly because we use lintChecks and it doesn't allow other types of deps
-    // this ugly hack exists because of b/63873667
+    // compileOnly because lint runtime is provided when checks are run
+    // Use latest lint for running from IDE to make sure checks always run
     if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
         compileOnly LINT_API_LATEST
     } else {
@@ -81,7 +83,7 @@
 
 Your new module will need to have a registry that contains a list of all of the
 checks to be performed on the library. There is an
-[`IssueRegistry`](https://cs.android.com/android/platform/superproject/+/master:tools/base/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/IssueRegistry.java)
+[`IssueRegistry`](https://cs.android.com/android/platform/superproject/+/master:tools/base/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/IssueRegistry.java;l=47)
 class provided by the tools team. Extend this class into your own
 `IssueRegistry` class, and provide it with the issues in the module.
 
@@ -103,7 +105,7 @@
 `CURRENT_API` is defined by the Lint API version against which your project is
 compiled, as defined in the module's `build.gradle` file. Jetpack Lint modules
 should compile using Lint API version 3.3 defined in
-[Dependencies.kt](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt;l=84).
+[Dependencies.kt](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt;l=176).
 
 We guarantee that our Lint checks work with versions 3.3-3.6 by running our
 tests with both versions 3.3 and 3.6. For newer versions of Android Studio (and
@@ -268,7 +270,7 @@
 These are Lint checks that will apply to source code files -- primarily Java and
 Kotlin, but can also be used for other similar file types. All code detectors
 that analyze Java or Kotlin files should implement the
-[SourceCodeScanner](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/SourceCodeScanner.kt).
+[SourceCodeScanner](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/SourceCodeScanner.kt).
 
 ### API surface
 
@@ -401,7 +403,7 @@
 These are Lint rules that will apply to resource files including `anim`,
 `layout`, `values`, etc. Lint rules being applied to resource files should
 extend
-[`ResourceXmlDetector`](https://cs.android.com/android/platform/superproject/+/master:tools/base/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ResourceXmlDetector.java).
+[`ResourceXmlDetector`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ResourceXmlDetector.java).
 The `Detector` must define the issue it is going to detect, most commonly as a
 static variable of the class.
 
@@ -436,7 +438,7 @@
 #### appliesTo
 
 This determines the
-[ResourceFolderType](https://cs.android.com/android/platform/superproject/+/master:tools/base/layoutlib-api/src/main/java/com/android/resources/ResourceFolderType.java)
+[ResourceFolderType](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:layoutlib-api/src/main/java/com/android/resources/ResourceFolderType.java)
 that the check will run against.
 
 ```kotlin
@@ -503,7 +505,7 @@
 ```
 
 Next, you must test the `Detector` class. The Tools team provides a
-[`LintDetectorTest`](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java)
+[`LintDetectorTest`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java)
 class that should be extended. Override `getDetector()` to return an instance of
 the `Detector` class:
 
@@ -517,13 +519,13 @@
 getIssues(): MutableList<Issue> = mutableListOf(MyLibraryDetector.ISSUE)
 ```
 
-[`LintDetectorTest`](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java)
+[`LintDetectorTest`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java)
 provides a `lint()` method that returns a
-[`TestLintTask`](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintTask.java).
+[`TestLintTask`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintTask.java).
 `TestLintTask` is a builder class for setting up lint tests. Call the `files()`
 method and provide an `.xml` test file, along with a file stub. After completing
 the set up, call `run()` which returns a
-[`TestLintResult`](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintResult.kt).
+[`TestLintResult`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintResult.kt).
 `TestLintResult` provides methods for checking the outcome of the provided
 `TestLintTask`. `ExpectClean()` means the output is expected to be clean because
 the lint rule was followed. `Expect()` takes a string literal of the expected
@@ -536,13 +538,13 @@
 ## Android manifest detector
 
 Lint checks targeting `AndroidManifest.xml` files should implement the
-[XmlScanner](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/XmlScanner.kt)
+[XmlScanner](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/XmlScanner.kt)
 and define target scope in issues as `Scope.MANIFEST`
 
 ## Gradle detector
 
 Lint checks targeting Gradle configuration files should implement the
-[GradleScanner](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/GradleScanner.kt)
+[GradleScanner](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/GradleScanner.kt)
 and define target scope in issues as `Scope.GRADLE_SCOPE`
 
 ### API surface
@@ -599,7 +601,7 @@
 
 Sometimes it is necessary to implement multiple different scanners in a Lint
 detector. For example, the
-[Unused Resource](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnusedResourceDetector.java)
+[Unused Resource](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnusedResourceDetector.java)
 Lint check implements an XML and SourceCode Scanner in order to determine if
 resources defined in XML files are ever references in the Java/Kotlin source
 code.
@@ -621,16 +623,16 @@
 
 ## Useful classes/packages
 
-### [`SdkConstants`](https://cs.android.com/android/platform/superproject/+/master:tools/base/common/src/main/java/com/android/SdkConstants.java;l=38?q=SdkCon)
+### [`SdkConstants`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:common/src/main/java/com/android/SdkConstants.java)
 
 Contains most of the canonical names for android core library classes, as well
 as XML tag names.
 
 ## Helpful links
 
-[Studio Lint Rules](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks)
+[Studio Lint Rules](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/)
 
-[Lint Detectors and Scanners Source Code](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api)
+[Lint Detectors and Scanners Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/)
 
 [Creating Custom Link Checks (external)](https://twitter.com/alexjlockwood/status/1176675045281693696)
 
@@ -644,4 +646,4 @@
 
 [ADS 19 Presentation by Alan & Rahul](https://www.youtube.com/watch?v=jCmJWOkjbM0)
 
-[META-INF vs Manifest](https://groups.google.com/forum/#!msg/lint-dev/z3NYazgEIFQ/hbXDMYp5AwAJ)
\ No newline at end of file
+[META-INF vs Manifest](https://groups.google.com/forum/#!msg/lint-dev/z3NYazgEIFQ/hbXDMYp5AwAJ)
diff --git a/docs/manual_prebuilts_dance.md b/docs/manual_prebuilts_dance.md
new file mode 100644
index 0000000..d06e86f
--- /dev/null
+++ b/docs/manual_prebuilts_dance.md
@@ -0,0 +1,22 @@
+# The Manual Prebuilts Dance™
+
+NOTE There is also a [script](releasing.md#the-prebuilts-dance™) that automates
+this step.
+
+Public-facing Jetpack library docs are built from prebuilts to reconcile our
+monolithic docs update process with our independently-versioned library release
+process.
+
+Submit the following changes in the same Gerrit topic so that they merge in the
+same build ID:
+
+1.  Commit your release artifact to the AndroidX AOSP checkout's local Maven
+    repository under `prebuilts/androidx/internal`.
+
+2.  Update the version for your library in the public docs configuration
+    ([docs-public/build.gradle](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:docs-public/build.gradle)).
+    If this is the first time that your library is being published, you will
+    need to add a new entry.
+
+Once both changes are, make sure to note the build ID where they landed. You
+will need to put this in your release request bug for Docs team.
diff --git a/docs/onboarding.md b/docs/onboarding.md
new file mode 100644
index 0000000..5bd331b
--- /dev/null
+++ b/docs/onboarding.md
@@ -0,0 +1,790 @@
+# Getting started
+
+[TOC]
+
+This page describes how to set up your workstation to check out source code,
+make simple changes in Android Studio, and upload commits to Gerrit for review.
+
+This page does **not** cover best practices for the content of changes. Please
+see [Life of a Jetpack Feature](loaf.md) for details on developing and releasing
+a library, [API Guidelines](api_guidelines.md) for best practices regarding
+public APIs, or [Policies and Processes](policies.md) for an overview of the
+constraints placed on changes.
+
+## Workstation setup {#setup}
+
+You will need to install the `repo` tool, which is used for Git branch and
+commit management. If you want to learn more about `repo`, see the
+[Repo Command Reference](https://source.android.com/setup/develop/repo).
+
+### Linux and MacOS {#setup-linux-mac}
+
+First, download `repo` using `curl`.
+
+```shell
+test -d ~/bin || mkdir ~/bin
+curl https://storage.googleapis.com/git-repo-downloads/repo \
+    > ~/bin/repo && chmod 700 ~/bin/repo
+```
+
+Then, modify `~/.bash_profile` (if using `bash`) to ensure you can find local
+binaries from the command line.
+
+```shell
+export PATH=~/bin:$PATH
+```
+
+You will need to either start a new terminal session or run `source
+~/.bash_profile` to pick up the new path.
+
+If you encounter an SSL `CERTIFICATE_VERIFY_FAILED` error or warning about
+Python 2 being no longer supported, you will need to install Python 3 and alias
+your `repo` command to run with `python3`.
+
+```shell {.bad}
+repo: warning: Python 2 is no longer supported; Please upgrade to Python 3.6+.
+```
+
+```shell {.bad}
+Downloading Repo source from https://gerrit.googlesource.com/git-repo
+fatal: Cannot get https://gerrit.googlesource.com/git-repo/clone.bundle
+fatal: error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:777)
+```
+
+First, install Python 3 from the [official website](https://www.python.org).
+Please read the "Important Information" displayed during installation for
+information about SSL/TLS certificate validation and the running the "Install
+Certificates.command".
+
+Next, open your `~/.bash_profile` and add the following lines to wrap the `repo`
+command:
+
+```shell
+# Force repo to run with Python3
+function repo() {
+  command python3 "$(which repo)" $@
+}
+```
+
+### Windows {#setup-win}
+
+Sorry, Windows is not a supported platform for AndroidX development.
+
+## Set up access control {#access}
+
+### Authenticate to AOSP Gerrit {#access-gerrit}
+
+Before you can upload changes, you will need to associate your Google
+credentials with the AOSP Gerrit code review system by signing in to
+[android-review.googlesource.com](https://android-review.googlesource.com) at
+least once using the account you will use to submit patches.
+
+Next, you will need to
+[set up authentication](https://android-review.googlesource.com/new-password).
+This will give you a shell command to update your local Git cookies, which will
+allow you to upload changes.
+
+Finally, you will need to accept the
+[CLA for new contributors](https://android-review.googlesource.com/settings/new-agreement).
+
+## Check out the source {#source}
+
+Like ChromeOS, Chromium, and the Android build system, we develop in the open as
+much as possible. All feature development occurs in the public
+[androidx-master-dev](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev)
+branch of the Android Open Source Project.
+
+As of 2020/03/20, you will need about 38 GB for a fully-built checkout.
+
+### Synchronize the branch {#source-checkout}
+
+Use the following `repo` commands to check out your branch.
+
+#### Public master development branch {#source-checkout-master}
+
+All development should occur in this branch unless otherwise specified by the
+AndroidX Core team.
+
+The following command will check out the public master development branch:
+
+```shell
+mkdir androidx-master-dev && cd androidx-master-dev
+repo init -u https://android.googlesource.com/platform/manifest \
+    -b androidx-master-dev --partial-clone --clone-filter=blob:limit=10M
+repo sync -c -j8
+```
+
+NOTE On MacOS, if you receive an SSL error like `SSL: CERTIFICATE_VERIFY_FAILED`
+you may need to install Python3 and boot strap the SSL certificates in the
+included version of pip. You can execute `Install Certificates.command` under
+`/Applications/Python 3.6/` to do so.
+
+### Increase Git rename limit {#source-config}
+
+To ensure `git` can detect diffs and renames across significant changes (namely,
+the `androidx.*` package rename), we recommend that you set the following `git
+config` properties:
+
+```shell
+git config --global merge.renameLimit 999999
+git config --global diff.renameLimit 999999
+```
+
+## Explore source code from a browser {#code-search}
+
+`androidx-master-dev` has a publicly-accessible
+[code search](https://cs.android.com/androidx/platform/frameworks/support) that
+allows you to explore all of the source code in the repository. Links to this
+URL may be shared on public Buganizer and other external sites.
+
+We recommend setting up a custom search engine in Chrome as a faster (and
+publicly-accessible) alternative to `cs/`.
+
+### Custom search engine for `androidx-master-dev` {#custom-search-engine}
+
+1.  Open `chrome://settings/searchEngines`
+1.  Click the `Add` button
+1.  Enter a name for your search engine, ex. "AndroidX Code Search"
+1.  Enter a keyword, ex. "csa"
+1.  Enter the following URL:
+    `https://cs.android.com/search?q=%s&ss=androidx%2Fplatform%2Fframeworks%2Fsupport`
+1.  Click the `Add` button
+
+Now you can select the Chrome omnibox, type in `csa` and press tab, then enter a
+query to search for, e.g. `AppCompatButton file:appcompat`, and press the
+`Enter` key to get to the search result page.
+
+## Develop in Android Studio {#studio}
+
+Library development uses a curated version of Android Studio to ensure
+compatibility between various components of the development workflow.
+
+From the `frameworks/support` directory, you can use `ANDROIDX_PROJECTS=MAIN
+./gradlew studio` to automatically download and run the correct version of
+Studio to work on main set of androidx projects. `ANDROIDX_PROJECTS` has several
+other options like `ANDROIDX_PROJECTS=ALL` to open other subsets of the
+projects.
+[settings.gradle](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:settings.gradle)
+file in the repository has these options listed.
+
+```shell
+ANDROIDX_PROJECTS=MAIN ./gradlew studio
+```
+
+Next, open the `framework/support` project root from your checkout. If Studio
+asks you which SDK you would like to use, select `Use project SDK`. Importing
+projects may take a while, but once that finishes you can use Studio as you
+normally would for application or library development -- right-click on a test
+or sample to run or debug it, search through classes, and so on.
+
+If you see any errors (red underlines), click Gradle's elephant button in the
+toolbar ("Sync Project with Gradle Files") and they should resolve once the
+build completes.
+
+> NOTE: You should choose "Use project SDK" when prompted by Studio. If you
+> picked "Android Studio SDK" by mistake, don't panic! You can fix this by
+> opening `File > Project Structure > Platform Settings > SDKs` and manually
+> setting the Android SDK home path to
+> `<project-root>/prebuilts/fullsdk-<platform>`.
+
+> NOTE: If Android Studio's UI looks scaled up, ex. twice the size it should be,
+> you may need to add the following line to your `studio64.vmoptions` file using
+> `Help -> Edit Custom VM Options`:
+>
+> ```
+> -Dsun.java2d.uiScale.enabled=false
+> ```
+
+## Making changes {#changes}
+
+Similar to Android framework development, library developmnent should occur in
+CL-specific working branches. Use `repo` to create, upload, and abandon local
+branches. Use `git` to manage changes within a local branch.
+
+```shell
+cd path/to/checkout/frameworks/support/
+repo start my_branch_name .
+# make necessary code changes
+# use git to commit changes
+repo upload --cbr -t .
+```
+
+The `--cbr` switch automatically picks the current repo branch for upload. The
+`-t` switch sets the Gerrit topic to the branch name, e.g. `my-branch-name`.
+
+## Building {#building}
+
+### Modules and Maven artifacts {#modules-and-maven-artifacts}
+
+To build a specific module, use the module's `assemble` Gradle task. For
+example, if you are working on `core` module use:
+
+```shell
+./gradlew core:core:assemble
+```
+
+Use the `-Pandroidx.allWarningsAsErrors` to make warnings fail your build (same
+as presubmits):
+
+```shell
+./gradlew core:core:assemble -Pandroidx.allWarningsAsErrors
+```
+
+To build every module, run the Lint verifier, verify the public API surface, and
+generate the local Maven repository artifact, use the `createArchive` Gradle
+task:
+
+```shell
+./gradlew createArchive
+```
+
+To run the complete build task that our build servers use, use the
+`buildOnServer` Gradle task:
+
+```shell
+./gradlew buildOnServer
+```
+
+### Attaching a debugger to the build
+
+Gradle tasks, including building a module, may be run or debugged from Android
+Studio's `Gradle` pane by finding the task to be debugged -- for example,
+`androidx > androidx > appcompat > appcompat > build > assemble` --
+right-clicking on it, and then selecting `Debug...`.
+
+Note that debugging will not be available until Gradle sync has completed.
+
+## From the command line
+
+Tasks may also be debugged from the command line, which may be useful if
+`./gradlew studio` cannot run due to a Gradle task configuration issue.
+
+1.  From the configurations dropdown in Studio, select "Edit Configurations".
+1.  Click the plus in the top left to create a new "Remote" configuration. Give
+    it a name and hit "Ok".
+1.  Set breakpoints.
+1.  Run your task with added flags: `./gradlew <your_task_here>
+    -Dorg.gradle.debug=true --no-daemon`
+1.  Hit the "Debug" button to the right of the configuration dropdown to attach
+    to the process.
+
+#### Troubleshooting the debugger
+
+If you get a "Connection refused" error, it's likely because a gradle daemon is
+still running on the port specified in the config, and you can fix this by
+killing the running gradle daemons:
+
+```shell
+./gradlew --stop
+```
+
+Note: This is described in more detail in this
+[Medium article](https://medium.com/grandcentrix/how-to-debug-gradle-plugins-with-intellij-eef2ef681a7b).
+
+#### Attaching to an annotation processor
+
+Annotation processors run as part of the build, to debug them is similar to
+debugging the build.
+
+For a Java project:
+
+```shell
+./gradlew <your_project>:compileDebugJava --no-daemon --rerun-tasks -Dorg.gradle.debug=true
+```
+
+For a Kotlin project:
+
+```shell
+./gradlew <your_project>:compileDebugKotlin --no-daemon --rerun-tasks -Dorg.gradle.debug=true -Dkotlin.compiler.execution.strategy="in-process" -Dkotlin.daemon.jvm.options="-Xdebug,-Xrunjdwp:transport=dt_socket\,address=5005\,server=y\,suspend=n"
+```
+
+### Optional: Enabling internal menu in IntelliJ/Studio
+
+To enable tools such as `PSI tree` inside of IntelliJ/Studio to help debug
+Android Lint checks and Metalava, you can enable the
+[internal menu](https://www.jetbrains.org/intellij/sdk/docs/reference_guide/internal_actions/enabling_internal.html)
+which is typically used for plugin and IDE development.
+
+### Reference documentation {#docs}
+
+Our reference docs (Javadocs and KotlinDocs) are published to
+https://developer.android.com/reference/androidx/packages and may be built
+locally.
+
+NOTE `./gradlew tasks` always has the canonical task information! When in doubt,
+run `./gradlew tasks`
+
+#### Javadocs
+
+To build API reference docs for tip-of-tree Java source code, run the Gradle
+task:
+
+```
+./gradlew disttipOfTreeDocs
+```
+
+This will output docs in the zip file:
+`{androidx-master-dev}/out/dist/android-support-tipOfTree-docs-0.zip`, as well
+as in local html files that you can check from your browser:
+`{androidx-master-dev}/out/androidx/build/javadoc/tipOfTree/offline/reference/packages.html`
+
+#### KotlinDocs
+
+To build API reference docs for tip-of-tree Kotlin source code, run the Gradle
+task:
+
+```
+./gradlew distTipOfTreeDokkaDocs
+```
+
+This will output docs in the zip file:
+`{androidx-master-dev}/out/dist/dokkaTipOfTreeDocs-0.zip`
+
+#### Release docs
+
+To build API reference docs for published artifacts formatted for use on
+[d.android.com](http://d.android.com), run the Gradle command:
+
+```
+./gradlew distpublicDocs
+```
+
+This will create the artifact
+`{androidx-master-dev}/out/dist/android-support-public-docs-0.zip`. This command
+builds docs based on the version specified in
+`{androidx-master-dev-checkout}/frameworks/support/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt`
+and uses the prebuilt checked into
+`{androidx-master-dev-checkout}/prebuilts/androidx/internal/androidx/`. We
+colloquially refer to this two step process of (1) updating PublishDocsRules.kt
+and (2) checking in a prebuilt artifact into the prebuilts directory as
+[The Prebuilts Dance](releasing.md#the-prebuilts-dance™). So, to build javadocs
+that will be published to
+https://developer.android.com/reference/androidx/packages, both of these steps
+need to be completed.
+
+Once you done the above steps, Kotlin docs will also be generated, with the only
+difference being that we use the Gradle command:
+
+```
+./gradlew distPublicDokkaDocs
+```
+
+which generates the kotlin docs artifact
+`{androidx-master-dev}/out/dist/dokkaPublicDocs-0.zip`
+
+### Updating public APIs {#updating-public-apis}
+
+Public API tasks -- including tracking, linting, and verifying compatibility --
+are run under the following conditions based on the `androidx` configuration
+block, evaluated in order:
+
+*   `runApiTasks=Yes` => yes
+*   `runApiTasks=No` => no
+*   `toolingProject=true` => no
+*   `mavenVersion` or group version not set => no
+*   Has an existing `api/` directory => yes
+*   `publish=SNAPSHOT_AND_RELEASE` => yes
+*   Otherwise, no
+
+If you make changes to tracked public APIs, you will need to acknowledge the
+changes by updating the `<component>/api/current.txt` and associated API files.
+This is handled automatically by the `updateApi` Gradle task:
+
+```shell
+# Run updateApi for all modules.
+./gradlew updateApi
+
+# Run updateApi for a single module, ex. appcompat-resources in group appcompat.
+./gradlew :appcompat:appcompat-resources:updateApi
+```
+
+If you change the public APIs without updating the API file, your module will
+still build **but** your CL will fail Treehugger presubmit checks.
+
+### Release notes & the `Relnote:` tag {#relnote}
+
+Prior to releasing, release notes are pre-populated using a script and placed
+into a Google Doc. The Google Doc is manually double checked by library owners
+before the release goes live. To auto-populate your release notes, you can use
+the semi-optional commit tag `Relnote:` in your commit, which will automatically
+include that message the commit in the pre-populated release notes.
+
+The presence of a `Relnote:` tag is required for API changes in
+`androidx-master-dev`.
+
+#### How to use it?
+
+One-line release note:
+
+``` {.good}
+Relnote: Fixed a critical bug
+```
+
+``` {.good}
+Relnote: "Fixed a critical bug"
+```
+
+``` {.good}
+Relnote: Added the following string function: `myFoo(\"bar\")`
+```
+
+Multi-line release note:
+
+Note: If the following lines do not contain an indent, you may hit b/165570183.
+
+``` {.good}
+Relnote: "We're launching this awesome new feature!  It solves a whole list of
+    problems that require a lot of explaining! "
+```
+
+``` {.good}
+Relnote: """Added the following string function: `myFoo("bar")`
+    It will fix cases where you have to call `myFoo("baz").myBar("bar")`
+    """
+```
+
+Opt out of the Relnote tag:
+
+``` {.good}
+Relnote: N/A
+```
+
+``` {.good}
+Relnote: NA
+```
+
+NOT VALID:
+
+``` {.bad}
+Relnote: This is an INVALID multi-line release note.  Our current scripts won't
+include anything beyond the first line.  The script has no way of knowing when
+the release note actually stops.
+```
+
+``` {.bad}
+Relnote: This is an INVALID multi-line release note.  "Quotes" need to be
+  escaped in order for them to be parsed properly.
+```
+
+### Common build errors
+
+#### Diagnosing build failures
+
+If you've encountered a build failure and you're not sure what is triggering it,
+then please run
+`./development/diagnose-build-failure/diagnose-build-failure.sh`.
+
+This script can categorize your build failure into one of the following
+categories:
+
+*   The Gradle Daemon is saving state in memory and triggering a failure
+*   Your source files have been changed and/or incompatible git commits have
+    been checked out
+*   Some file in the out/ dir is triggering an error
+    *   If this happens, diagnose-build-failure.sh should also identify which
+        file(s) specifically
+*   The build is nondeterministic and/or affected by timestamps
+*   The build via gradlew actually passes and this build failure is specific to
+    Android Studio
+
+Some more-specific build failures are listed below in this page.
+
+#### Out-of-date platform prebuilts
+
+Like a normal Android library developed in Android Studio, libraries within
+`androidx` are built against prebuilts of the platform SDK. These are checked in
+to the `prebuilts/fullsdk-darwin/platforms/<android-version>` directory.
+
+If you are developing against pre-release platform APIs in the internal
+`androidx-platform-dev` branch, you may need to update these prebuilts to obtain
+the latest API changes.
+
+### Missing external dependency
+
+If Gradle cannot resolve a dependency listed in your `build.gradle`, you may
+need to import the corresponding artifact into `prebuilts/androidx/external`.
+Our workflow does not automatically download artifacts from the internet to
+facilitate reproducible builds even if remote artifacts are changed.
+
+You can download a dependency by running:
+
+```shell
+cd frameworks/support && ./development/importMaven/import_maven_artifacts.py -n 'someGroupId:someArtifactId:someVersion'
+```
+
+This will create a change within the `prebuilts/androidx/external` directory.
+Make sure to upload this change before or concurrently (ex. in the same Gerrit
+topic) with the dependent library code.
+
+Libraries typically reference dependencies using constants defined in
+[`Dependencies.kt`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt),
+so please update this file to include a constant for the version of the library
+that you have checked in. You will reference this constant in your library's
+`build.gradle` dependencies.
+
+#### Updating an existing dependency
+
+If an older version of a dependency prebuilt was already checked in, please
+manually remove it within the same CL that adds the new prebuilt. You will also
+need to update `Dependencies.kt` to reflect the version change.
+
+#### My gradle build fails with "Cannot invoke method getURLs() on null object"
+
+You're using Java 9's javac, possibly because you ran envsetup.sh from the
+platform build or specified Java 9 as the global default Java compiler. For the
+former, you can simply open a new shell and avoid running envsetup.sh. For the
+latter, we recommend you set Java 8 as the default compiler using sudo
+update-java-alternatives; however, if you must use Java 9 as the default then
+you may alternatively set JAVA_HOME to the location of the Java 8 SDK.
+
+#### My gradle build fails with "error: cannot find symbol" after making framework-dependent changes.
+
+You probably need to update the prebuilt SDK used by the gradle build. If you
+are referencing new framework APIs, you will need to wait for the framework
+changes to land in an SDK build (or build it yourself) and then land in both
+prebuilts/fullsdk and prebuilts/sdk. See
+[Updating SDK prebuilts](playbook.md#prebuilts-fullsdk) for more information.
+
+#### How do I handle refactoring a framework API referenced from a library?
+
+Because AndroidX must compile against both the current framework and the latest
+SDK prebuilt, and because compiling the SDK prebuilt depends on AndroidX, you
+will need to refactor in stages: Remove references to the target APIs from
+AndroidX Perform the refactoring in the framework Update the framework prebuilt
+SDK to incorporate changes in (2) Add references to the refactored APIs in
+AndroidX Update AndroidX prebuilts to incorporate changes in (4)
+
+## Testing {#testing}
+
+AndroidX libraries are expected to include unit or integration test coverage for
+100% of their public API surface. Additionally, all CLs must include a `Test:`
+stanza indicating which tests were used to verify correctness. Any CLs
+implementing bug fixes are expected to include new regression tests specific to
+the issue being fixed
+
+See the [Testing](testing.md) page for more resources on writing, running, and
+monitoring tests.
+
+### AVD Manager
+
+The Android Studio instance started by `./gradlew studio` uses a custom SDK
+directory, which means any virtual devices created by a "standard" non-AndroidX
+instance of Android Studio will be _visible_ from the `./gradlew studio`
+instance but will be unable to locate the SDK artifacts -- they will display a
+`Download` button.
+
+You can either use the `Download` button to download an extra copy of the SDK
+artifacts _or_ you can set up a symlink to your "standard" non-AndroidX SDK
+directory to expose your existing artifacts to the `./gradlew studio` instance:
+
+```shell
+# Using the default MacOS Android SDK directory...
+ln -s /Users/$(whoami)/Library/Android/sdk/system-images \
+      ../../prebuilts/fullsdk-darwin/system-images
+```
+
+### Benchmarking {#testing-benchmarking}
+
+Libraries are encouraged to write and monitor performance benchmarks. See the
+[Benchmarking](benchmarking.md) page for more details.
+
+## Library snapshots {#snapshots}
+
+### Quick how to
+
+Add the following snippet to your build.gradle file, replacing `buildId` with a
+snapshot build Id.
+
+```groovy {highlight=context:[buildId]}
+allprojects {
+    repositories {
+        google()
+        jcenter()
+        maven { url 'https://androidx.dev/snapshots/builds/[buildId]/artifacts/repository' }
+    }
+}
+```
+
+You must define dependencies on artifacts using the SNAPSHOT version suffix, for
+example:
+
+```groovy {highlight=context:SNAPSHOT}
+dependencies {
+    implementation "androidx.core:core:1.2.0-SNAPSHOT"
+}
+```
+
+### Where to find snapshots
+
+If you want to use unreleased `SNAPSHOT` versions of `androidx` artifacts, you
+can find them on either our public-facing build server:
+
+`https://ci.android.com/builds/submitted/<build_id>/androidx_snapshot/latest`
+
+or on our slightly-more-convenient [androidx.dev](https://androidx.dev) site:
+
+`https://androidx.dev/snapshots/builds/<build-id>/artifacts/repository` for a
+specific build ID
+
+`https://androidx.dev/snapshots/builds/latest/artifacts/repository` for
+tip-of-tree snapshots
+
+### Obtaining a build ID
+
+To browse build IDs, you can visit either
+[androidx-master-dev](https://ci.android.com/builds/branches/aosp-androidx-master-dev/grid?)
+on ci.android.com or [Snapshots](https://androidx.dev/snapshots/builds) on the
+androidx.dev site.
+
+Note that if you are using androidx.dev, you may substitute `latest` for a build
+ID to use the last known good build.
+
+To manually find the last known good `build-id`, you have several options.
+
+#### Snapshots on androidx.dev
+
+[Snapshots](https://androidx.dev/snapshots/builds) on androidx.dev only lists
+usable builds.
+
+#### Programmatically via `jq`
+
+Install `jq`:
+
+```shell
+sudo apt-get install jq
+```
+
+```shell
+ID=`curl -s "https://ci.android.com/builds/branches/aosp-androidx-master-dev/status.json" | jq ".targets[] | select(.ID==\"aosp-androidx-master-dev.androidx_snapshot\") | .last_known_good_build"` \
+  && echo https://ci.android.com/builds/submitted/"${ID:1:-1}"/androidx_snapshot/latest/raw/repository/
+```
+
+#### Android build server
+
+Go to
+[androidx-master-dev](https://ci.android.com/builds/branches/aosp-androidx-master-dev/grid?)
+on ci.android.com.
+
+For `androidx-snapshot` target, wait for the green "last known good build"
+button to load and then click it to follow it to the build artifact URL.
+
+### Using in a Gradle build
+
+To make these artifacts visible to Gradle, you need to add it as a respository:
+
+```groovy
+allprojects {
+    repositories {
+        google()
+        maven {
+          // For all Jetpack libraries (including Compose)
+          url 'https://androidx.dev/snapshots/builds/<build-id>/artifacts/repository'
+        }
+    }
+}
+```
+
+Note that the above requires you to know the `build-id` of the snapshots you
+want.
+
+#### Specifying dependencies
+
+All artifacts in the snapshot repository are versioned as `x.y.z-SNAPSHOT`. So
+to use a snapshot artifact, the version in your `build.gradle` will need to be
+updated to `androidx.<groupId>:<artifactId>:X.Y.Z-SNAPSHOT`
+
+For example, to use the `core:core:1.2.0-SHAPSHOT` snapshot, you would add the
+following to your `build.gradle`:
+
+```
+dependencies {
+    ...
+    implementation("androidx.core:core:1.2.0-SNAPSHOT")
+    ...
+}
+```
+
+## FAQ {#faq}
+
+### How do I test my change in a separate Android Studio project? {#faq-test-change-studio}
+
+If you're working on a new feature or bug fix in AndroidX, you may want to test
+your changes against another project to verify that the change makes sense in a
+real-world context or that a bug's specific repro case has been fixed.
+
+If you need to be absolutely sure that your test will exactly emulate the
+developer's experience, you can repeatedly build the AndroidX archive and
+rebuild your application. In this case, you will need to create a local build of
+AndroidX's local Maven repository artifact and install it in your Android SDK
+path.
+
+First, use the `createArchive` Gradle task to generate the local Maven
+repository artifact:
+
+```shell
+# Creates <path-to-checkout>/out/dist/sdk-repo-linux-m2repository-##.zip
+./gradlew createArchive
+```
+
+Next, take the ZIP output from this task and extract the contents to the Android
+SDK path that you are using for your alternate (non-AndroidX) version of Android
+Studio. For example, you may be using `~/Android/SDK/extras` if you are using
+the default Android Studio SDK for app development or
+`prebuilts/fullsdk-linux/extras` if you are using fullsdk for platform
+development.
+
+```shell
+# Creates or overwrites android/m2repository
+cd <path-to-sdk>/extras
+unzip <path-to-checkout>/out/dist/top-of-tree-m2repository-##.zip
+```
+
+In the project's 'build.gradle' within 'repositories' notify studio of the
+location of m2repository:
+
+```groovy
+allprojects {
+    repositories {
+        ...
+        maven {
+            url "<path-to-sdk>/extras/m2repository"
+        }
+    }
+}
+```
+
+NOTE Gradle resolves dependencies in the order that the repositories are defined
+(if 2 repositories can resolve the same dependency, the first listed will do so
+and the second will not). Therefore, if the library you are testing has the same
+group, artifact, and version as one already published, you will want to list
+your custom maven repo first.
+
+Finally, in the dependencies section of your standalone project's `build.gradle`
+file, add or update the `implementation` entries to reflect the AndroidX modules
+that you would like to test. Example:
+
+```
+dependencies {
+    ...
+    implementation "androidx.appcompat:appcompat::1.0.0-alpha02"
+}
+```
+
+If you are testing your changes in the Android Platform code, you can replace
+the module you are testing
+`YOUR_ANDROID_PATH/prebuilts/sdk/current/androidx/m2repository` with your own
+module. We recommend only replacing the module you are modifying instead of the
+full m2repository to avoid version issues of other modules. You can either take
+the unzipped directory from
+`<path-to-checkout>/out/dist/top-of-tree-m2repository-##.zip`, or from
+`<path-to-checkout>/out/androidx/build/support_repo/` after buiding `androidx`.
+Here is an example of replacing the RecyclerView module:
+
+```shell
+$TARGET=YOUR_ANDROID_PATH/prebuilts/sdk/current/androidx/m2repository/androidx/recyclerview/recyclerview/1.1.0-alpha07;
+rm -rf $TARGET;
+cp -a <path-to-sdk>/extras/m2repository/androidx/recyclerview/recyclerview/1.1.0-alpha07 $TARGET
+```
+
+Make sure the library versions are the same before and after replacement. Then
+you can build the Android platform code with the new `androidx` code.
diff --git a/docs/onboarding_images/image1.png b/docs/onboarding_images/image1.png
new file mode 100644
index 0000000..9a32d42
--- /dev/null
+++ b/docs/onboarding_images/image1.png
Binary files differ
diff --git a/docs/onboarding_images/image2.png b/docs/onboarding_images/image2.png
new file mode 100644
index 0000000..9c215f1
--- /dev/null
+++ b/docs/onboarding_images/image2.png
Binary files differ
diff --git a/docs/onboarding_images/image3.png b/docs/onboarding_images/image3.png
new file mode 100644
index 0000000..e672255
--- /dev/null
+++ b/docs/onboarding_images/image3.png
Binary files differ
diff --git a/docs/policies.md b/docs/policies.md
new file mode 100644
index 0000000..b099299
--- /dev/null
+++ b/docs/policies.md
@@ -0,0 +1,190 @@
+## AndroidX policies and processes
+
+This document is intended to describe release policies that affect the workflow
+of an engineer developing within the AndroidX libraries. It also describes the
+process followed by a release engineer or TPM to take a development branch and
+publish it as a release on Google Maven.
+
+Policies and processes automated via tooling are noted in
+<span style="color:#bf9000;">yellow</span>.
+
+[TOC]
+
+## Project directory structure {#directory-structure}
+
+Libraries developed in AndroidX follow a consistent project naming and directory
+structure.
+
+Library groups should organize their modules into directories and module names
+(in brackets) as:
+
+```
+<feature-name>/
+  <feature-name>-<sub-feature>/ [<feature-name>:<feature-name>-<sub-feature>]
+  integration-tests/
+    testapp/ [<feature-name>:testapp]
+    testlib/ [<feature-name>:testlib]
+    samples/ [<feature-name>:samples]
+```
+
+For example, the `room` library group's directory structure is:
+
+```
+room/
+  common/ [room:room-common]
+  ...
+  rxjava2/ [room:room-rxjava2]
+  testing/ [room:room-testing]
+  integration-tests/
+    testapp/ [room:testapp]
+    testapp-kotlin/ [room:testapp-kotlin]
+```
+
+## Terminology {#terminology}
+
+**Artifact**
+:   Previously referred to as "a Support Library library." A library --
+    typically Java or Android -- that maps to a single Maven artifact, ex.
+    `androidx.recyclerview:recyclerview`. An artifact is associated with a
+    single Android Studio module and a directory containing a `build.gradle`
+    configuration, resources, and source code.
+
+**API Council**
+:   A committee that reviews Android APIs, both platform and library, to ensure
+    they are consistent and follow the best-practices defined in our API
+    guidelines.
+
+**Semantic Versioning (SemVer)**
+:   A versioning standard developed by one of the co-founders of GitHub that is
+    understood by common dependency management systems, including Maven. In this
+    document, we are referring specifically to
+    [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
+
+## Managing versions {#managing-versions}
+
+This section outlines the steps for a variety of common versioning-related
+tasks. Artifact versions should **only** be modified by their owners as
+specified in the artifact directory's `OWNERS` file.
+
+Artifact versions are specified in
+[`LibraryVersions.kt`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt).
+Versions are bound to your artifact in the `supportLibrary` block in your
+artifact's `build.gradle` file. The `Version` class validates the version string
+at build time.
+
+In the
+[`LibraryVersions.kt`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt)
+file:
+
+```
+object LibraryVersions {
+    val SNAZZY_ARTIFACT = Version("1.1.0-alpha03")
+}
+```
+
+In your artifact's `build.gradle` file:
+
+```
+import androidx.build.LibraryVersions
+
+supportLibrary {
+   mavenVersion = LibraryVersions.SNAZZY_ARTIFACT
+}
+```
+
+#### Finalizing for release {#finalizing-for-release}
+
+When the artifact is ready for release, its versioned API file should be
+finalized to ensure that the subsequent versioned release conforms to the
+versioning policies.
+
+```
+./gradlew <module>:finalizeApi
+```
+
+This will prevent any subsequent changes to the API surface until the artifact
+version is updated. To update the artifact version and allow changes within the
+semantic versioning contract, simply update the version string in the artifact's
+`build.gradle` (see [Workflow](#workflow) introduction).
+
+To avoid breaking the development workflow, we recommend that API finalization
+and version string updates be submitted as a single CL.
+
+## Dependencies {#dependencies}
+
+Artifacts may depend on other artifacts within AndroidX as well as sanctioned
+third-party libraries.
+
+### Versioned artifacts {#versioned-artifacts}
+
+One of the most difficult aspects of independently-versioned releases is
+maintaining compatibility with public artifacts. In a mono repo such as Google's
+repository or Android Git at master revision, it's easy for an artifact to
+accidentally gain a dependency on a feature that may not be released on the same
+schedule.
+
+#### Pre-release dependencies {#pre-release-dependencies}
+
+Pre-release suffixes **must** propagate up the dependency tree. For example, if
+your artifact has API-type dependencies on pre-release artifacts, ex.
+`1.1.0-alpha01`, then your artifact must also carry the `alpha` suffix. If you
+only have implementation-type dependencies, your artifact may carry either the
+`alpha` or `beta` suffix.
+
+#### Pinned versions {#pinned-versions}
+
+To avoid issues with dependency versioning, consider pinning your artifact's
+dependencies to the oldest version (available via local `maven_repo` or Google
+Maven) that satisfies the artifact's API requirements. This will ensure that the
+artifact's release schedule is not accidentally tied to that of another artifact
+and will allow developers to use older libraries if desired.
+
+```
+dependencies {
+   api("androidx.collection:collection:1.0.0")
+   ...
+}
+```
+
+Artifacts should be built and tested against both pinned and tip-of-tree
+versions of their dependencies to ensure behavioral compatibility.
+
+#### Non-Pinned versions {#nonpinned-versions}
+
+Below is an example of a non-pinned dependency. It ties the artifact's release
+schedule to that of the dependency artifact, because the dependency will need to
+be released at the same time.
+
+```
+dependencies {
+   api(project(":collection"))
+   ...
+}
+```
+
+### Non-public APIs {#non-public-apis}
+
+Artifacts may depend on non-public (e.g. `@hide`) APIs exposed within their own
+artifact or another artifact in the same `groupId`; however, cross-artifact
+usages are subject to binary compatibility guarantees and
+`@RestrictTo(Scope.LIBRARY_GROUP)` APIs must be tracked like public APIs.
+
+```
+Dependency versioning policies are enforced at build time in the createArchive task. This task will ensure that pre-release version suffixes are propagated appropriately.
+
+Cross-artifact API usage policies are enforced by the checkApi and checkApiRelease tasks (see Life of a release).
+```
+
+### Third-party libraries {#third-party-libraries}
+
+Artifacts may depend on libraries developed outside of AndroidX; however, they
+must conform to the following guidelines:
+
+*   Prebuilt **must** be checked into Android Git with both Maven and Make
+    artifacts
+    *   `prebuilts/maven_repo` is recommended if this dependency is only
+        intended for use with AndroidX artifacts, otherwise please use
+        `external`
+*   Prebuilt directory **must** contains an OWNERS file identifying one or more
+    individual owners (e.g. NOT a group alias)
+*   Library **must** be approved by legal
diff --git a/docs/principles.md b/docs/principles.md
new file mode 100644
index 0000000..6dba721
--- /dev/null
+++ b/docs/principles.md
@@ -0,0 +1,135 @@
+# Jetpack Principles
+
+[TOC]
+
+## Ethos of Jetpack
+
+To create components, tools, and guidance that makes it quick and easy to build
+great Android apps, including contributions from Google and the open-source
+community.
+
+## Core Principles of a Jetpack Library
+
+Jetpack libraries provide the following guarantees to Android Developers:
+
+_formatted as “Jetpack libraries are…” with sub-points “Libraries should…”_
+
+### 1. Optimized for external client adoption
+
+-   Libraries should work for first-party clients and may even have optional
+    modules tailored specifically to first-party needs, but primary
+    functionality should target external developers.
+-   Measure success by 3p client adoption, followed by 1p client adoption.
+
+### 2. Designed to satisfy real-world use cases
+
+-   Meet developers where they are and solve the problems that they have
+    building apps -- not designed to just provide parity with existing platform
+    APIs and features
+-   Expose modules that are tightly-scoped to **developer pain points**
+    -   Smaller building blocks for external developers by scoping disjoint use
+        cases that are likely not to co-exist in a single app to individual
+        modules.
+-   Implement layered complexity, with **simple top-level APIs**
+    -   Complicated use case support must not be at the expense of increasing
+        API complexity for the most common simpler use cases.
+-   Have **backing data or a researched hypothesis** (research, demand etc) to
+    prove the library is necessary and sufficient.
+
+### 3. Aware of the existing developer ecosystem
+
+-   Avoid reinventing the wheel -- do not create a new library where one already
+    exists that is accepted by the community as a best practice
+
+### 4. Consistent with the rest of Jetpack
+
+-   Ensure that concepts learned in one component can be seen and understood in
+    other components
+-   Leverage Jetpack and community standards, for example:
+    -   For async work, uses Kotlin coroutines and/or Kotlin flow
+    -   For data persistence, uses Jetpack DataStore for simple and small data
+        and uses Room for more complicated Data
+
+### 5. Developed as open-source and compatible with AOSP Android
+
+-   Expose a unified developer-facing API surface across the Android ecosystem
+-   Avoid proprietary services or closed-source libraries for core
+    functionality, and instead provide integration points that allow a developer
+    to choose a proprietary service as the backing implementation
+-   Develop in AOSP to provide visibility into new features and bug fixes and
+    encourage external participation
+
+### 6. Written using language-idiomatic APIs
+
+-   Write APIs that feel natural for clients using both
+    [Kotlin](https://developer.android.com/kotlin/interop) and Java
+
+### 7. Compatible with a broad range of API levels
+
+-   Support older platforms and API levels according to client needs
+-   Provide continued maintenance to ensure compatibility with newer platforms
+-   Design with the expectation that every Jetpack API is **write-once,
+    run-everywhere** for Android with graceful degradation where necessary
+
+### 8. Integrated with best practices
+
+-   Guide developers toward using existing Jetpack best-practice libraries,
+    including Architecture Components
+
+### 9. Designed for tooling and testability
+
+-   Write adequate unit and integration tests for the library itself
+-   Provide developers with an accompanying testing story for integration
+    testing their own applications (ex. -testing artifacts that some libraries
+    expose)
+    -   Robolectric shouldn’t need to shadow the library classes
+    -   Ex. Room has in-memory testing support
+-   Build tooling concurrent with the library when possible, and with tooling in
+    mind otherwise
+
+### 10. Released using a clearly-defined process
+
+-   Follow Semantic Versioning and pre-release revision guidelines where each
+    library moves through alpha, beta, and rc revisions to gain feedback and
+    ensure stability
+
+### 11. Well-documented
+
+-   Provide developers with getting started and use case documentation on
+    d.android.com in addition to clear API reference documentation
+
+### 12. Supported for long-term use
+
+-   Plan for long-term support and maintenance
+
+### 13. Examples of modern development
+
+-   Where possible, targeting the latest languages, OS features, and tools. All
+    new libraries should be written in Kotlin first. Existing libraries
+    implemented in Java should add Kotlin extension libraries to improve the
+    interoperability of the Java APIs from Kotlin. New libraries written in Java
+    require a significant business reason on why a dependency in Kotlin cannot
+    be taken. The following is the order of preference, with each lower tier
+    requiring a business reason:
+    1.  Implemented in Kotlin that compiles to Java 8 bytecode
+    2.  Implemented in Java 8, with `-ktx` extensions for Kotlin
+        interoperability
+    3.  Implemented in Java 7, with `-ktx` extensions for Kotlin
+        interoperability
+
+### 14. High quality APIs and ownership
+
+-   All Jetpack libraries are expected to abide by Android and Jetpack API
+    Guidelines
+
+## Target Audience
+
+Jetpack libraries are used by a wide variety of external developers, from
+individuals working on their first Android app to huge corporations developing
+large-scale production apps. Generally, however, Jetpack libraries are designed
+to focus on small- to medium-sized app development teams.
+
+-   Note: If the library targets a niche set of apps, the developer pain
+    point(s) addressed by the Jetpack library must be significant enough to
+    justify its need.
+    -   Example : Jetpack Enterprise library
diff --git a/docs/testing.md b/docs/testing.md
new file mode 100644
index 0000000..b937bb9
--- /dev/null
+++ b/docs/testing.md
@@ -0,0 +1,246 @@
+# Testing
+
+[TOC]
+
+AndroidX contains unit and integration tests that are run automatically when a
+change is uploaded. It also contains a number of sample applications that are
+useful for demonstrating how to use features as well as performing manual
+testing.
+
+## Adding tests {#adding}
+
+For an example of how to set up simple unit and integration tests in a new
+module, see
+[aosp/1189799](https://android-review.googlesource.com/c/platform/frameworks/support/+/1189799).
+For an example of how to set up Espresso-powered integration tests, see the
+`preference` library's
+[`build.gradle`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:preference/preference/build.gradle)
+and
+[`EditTextPreferenceTest.java`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:preference/preference/src/androidTest/java/androidx/preference/tests/EditTextPreferenceTest.java)
+files.
+
+The currently allowed test runners for on-device tests are
+[`AndroidJUnitRunner`](https://developer.android.com/training/testing/junit-runner)
+and
+[`Parameterized`](https://junit.org/junit4/javadoc/4.12/org/junit/runners/Parameterized.html).
+
+### What gets tested, and when
+
+We use the
+[AffectedModuleDetector](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt)
+to determine what projects have changed since the last merge.
+
+In presubmit, "affected" modules will run all host and device tests regardless
+of size. Modules that _depend_ on affected modules will run all host tests, but
+will only run device tests annotated with `@SmallTest` or `@MediumTest`.
+
+When changes are made that can't be associated with a module, are in the root of
+the checkout, or are within `buildSrc`, then all host tests and all device tests
+annotated with `@SmallTest` or `@MediumTest` will be run for all modules.
+
+Presubmit tests represent only a subset of the devices on which our tests run.
+The remaining devices are tested only in postsubmit. In postsubmit, all host and
+device tests are run for all modules.
+
+### Test annotations
+
+#### Test size
+
+All device tests *should* be given a size annotation, which is one of:
+
+*   [`@SmallTest`](https://developer.android.com/reference/androidx/test/filters/SmallTest)
+*   [`@MediumTest`](https://developer.android.com/reference/androidx/test/filters/MediumTest)
+*   [`@LargeTest`](https://developer.android.com/reference/androidx/test/filters/LargeTest)
+
+If a device test is _not_ annotated with its size, it will be considered a
+`@LargeTest` by default. Host tests do not need to be annotated with their size,
+as all host tests are run regardless of size.
+
+This annotation can occur at either the class level or individual test level.
+After API level 27, timeouts are enforced based on this annotation.
+
+Annotation    | Max duration | Timeout after
+------------- | ------------ | -------------
+`@SmallTest`  | 200ms        | 300ms
+`@MediumTest` | 1000ms       | 1500ms
+`@LargeTest`  | 100000ms     | 120000ms
+
+Small tests should be less than 200ms, and the timeout is set to 300ms. Medium
+tests should be less than 1000ms, and the timeout is set to 1500ms. Large tests
+have a timeout of 120000ms, which should cover any remaining tests.
+
+The exception to this rule is when using a runner other than
+[`AndroidJUnitRunner`](https://developer.android.com/training/testing/junit-runner).
+Since these runners do not enforce timeouts, tests that use them must not use a
+size annotation. They will run whenever large tests would run.
+
+Currently the only allowed alternative is the
+[`Parameterized`](https://junit.org/junit4/javadoc/4.12/org/junit/runners/Parameterized.html)
+runner. If you need to use a different runner for some reason, please reach out
+to the team and we can review/approve the use.
+
+#### Disabling tests
+
+To disable a device-side test in presubmit testing only -- but still have it run
+in postsubmit -- use the
+[`@FlakyTest`](https://developer.android.com/reference/androidx/test/filters/FlakyTest)
+annotation. There is currently no support for presubmit-only disabling of
+host-side tests.
+
+If you need to stop a host- or device-side test from running entirely, use
+JUnit's [`@Ignore`](http://junit.sourceforge.net/javadoc/org/junit/Ignore.html)
+annotation. Do *not* use Android's `@Suppress` annotation, which only works with
+Android test runners and will *not* work for host-side tests.
+
+#### Filtering devices
+
+To restrict a test to a range of SDKs, use
+[`@SdkSuppress`](https://developer.android.com/reference/androidx/test/filters/SdkSuppress)
+which allows specifying a range with `minSdkVersion` and `maxSdkVersion`. This
+annotation also supports targeting a specific pre-release SDK with the
+`codeName` parameter.
+
+```java
+// Target SDKs 17 through 19, inclusive
+@SdkSuppress(minSdkVersion = 17, maxSdkVersion = 19)
+
+// Target pre-release SDK R only
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.R, isCodeName = "R")
+```
+
+You may also gate portions of test implementation code using `SDK_INT` or
+[`BuildCompat.isAtLeast`](https://developer.android.com/reference/androidx/core/os/BuildCompat)
+methods.
+
+To restrict to only phsyical devices, use
+[`@RequiresDevice`](https://developer.android.com/reference/androidx/test/filters/RequiresDevice).
+
+### Animations in tests
+
+Animations are disabled for tests by default. This helps avoid flakes due to
+timing and also makes tests faster.
+
+In rare cases, like testing the animations themselves, you may want to enable
+animations for a particular test or test class. For those cases, you can use the
+[`AnimationDurationScaleRule`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:testutils/testutils-runtime/src/main/java/androidx/testutils/AnimationDurationScaleRule.kt).
+
+## Using the emulator {#emulator}
+
+You can use the emulator or a real device to run tests. If you wish to use the
+emulator, you will need to access the AVD Manager (and your downloaded emulator
+images) using a separate "normal" instance of Android Studio. "Normal" means a
+non-Canary build of Studio that you would use for regular app development -- the
+important part being that it points to the Android SDK where your downloaded
+emulator images reside. You will need to open a project to get the Tools menu --
+do NOT open the AndroidX project in the "normal" instance of Android Studio;
+instead, open a normal app or create a blank project using the app wizard.
+
+## Debugging with platform SDK sources {#sources}
+
+The platform SDK sources that are checked into the development branch may not
+match up with the build of Android present on the emulator or your physical
+device. As a result, the line numbers reported by the debugger may not match up
+the actual code being run.
+
+If you have a copy of the sources for the build against which you are debugging,
+you can manually specify your platform SDK source path:
+
+1.  Click on a module (e.g. `appcompat`) in the `Project` view
+1.  Press `Ctrl-Shift-A` and type "Module Settings", then run the action
+1.  In the `Project Structure` dialog, navigate to `SDKs > Android API 29
+    Platform > Sourcepath`
+1.  Use the `-` button to remove any paths that are present, then use the `+`
+    button to add the desired source path, ex. `<android checkout
+    root>/frameworks/base` if you are debugging against a locally-built system
+    image
+
+NOTE The `Project Structure` dialog reachable via `File > Project Structure` is
+**not** the same as the `Project Structure` dialog that will allow you to
+specify the SDK source path. You must use the "Module Settings" action as
+directed above.
+
+## Running unit and integration tests {#running}
+
+From Android Studio, right-click can be used to run most test targets, including
+source files, classes within a file, or individual test methods but **not**
+entire modules. To run a supported test target, right-click on the test target
+and then click `Run <name of test target>`.
+
+To run tests for an entire module such as `appcompat`, use `Run -> Edit
+configurations...` and use the `+` button to create a new `Android Instrumented
+Tests` configuration. Specify the module to be tested, give it a reasonable name
+(not "All Tests") and click `OK`, then use the `Run` menu to run the
+configuration.
+
+![alt_text](onboarding_images/image2.png "screenshot of run menu")
+
+NOTE If you receive the error `JUnit version 3.8 or later expected` this means
+that Android Studio generated an Android JUnit configuration when you actually
+needed an Android Instrumented Tests configuration. Open the `Run -> Edit
+configurations...` dialog and delete the configuration from Android JUnit, then
+manually add a configuration in Android Instrumented Tests.
+
+### From the command line {#running-from-shell}
+
+Following a successful build, tests may be run against a particular AndroidX
+module using `gradlew`.
+
+To run all integration tests in a specific project, run the following from
+`framework/support`:
+
+```shell
+./gradlew <project-name>:connectedCheck --info --daemon
+```
+
+substituting the Gradle project name (ex. `core`).
+
+To run all integration tests in the specific project and test class you're
+working on, run
+
+```shell
+./gradlew <project-name>:connectedCheck --info --daemon \
+    -Pandroid.testInstrumentationRunnerArguments.class=<fully-qualified-class>[\#testName]
+```
+
+substituting the Gradle project name (ex. `viewpager`) and fully-qualified class
+name (ex. `androidx.viewpager.widget.ViewPagerTest`) of your test file,
+optionally followed by `\#testName` if you want to execute a single test in that
+file.
+
+If you see some weird compilation errors such as below, run `./gradlew clean`
+first:
+
+```
+Unknown source file : UNEXPECTED TOP-LEVEL EXCEPTION:
+Unknown source file : com.android.dex.DexException: Multiple dex files define Landroid/content/pm/ParceledListSlice$1;
+```
+
+## Test apps {#testapps}
+
+Library developers are strongly encouraged to write test apps that exercise
+their library's public API surface. Test apps serve multiple purposes:
+
+*   Integration testing and validation of API testability, when paired with
+    tests
+*   Validation of API usability and developer experience, when paired with a use
+    case or critical user journey
+*   Sample documentation, when embedded into API reference docs using the
+    [`@sample` and `@Sampled` annotations](api_guidelines.md#sample-usage)
+
+### Legacy test apps {#testapps-legacy}
+
+We have a set of legacy sample Android applications in projects suffixed with
+`-demos`. These applications do not have tests and should not be used as test
+apps for new APIs, but they may be useful for manual regression testing.
+
+1.  Click `Run/Debug Configuration` on the top of the window.
+1.  Select the app you want to run.
+1.  Click 'Run' button.
+
+![alt_text](onboarding_images/image3.png "screenshot of Run/Debug menu")
+
+## Benchmarking {#benchmarking}
+
+AndroidX supports benchmarking - locally with Studio/Gradle, and continuously in
+post-submit. For more information on how to create and run benchmarks, see
+[Benchmarking](benchmarking.md).
diff --git a/docs/truth_guide.md b/docs/truth_guide.md
new file mode 100644
index 0000000..fd9efc4
--- /dev/null
+++ b/docs/truth_guide.md
@@ -0,0 +1,147 @@
+# Adding custom Truth subjects
+
+[TOC]
+
+## Custom truth subjects
+
+Every subject class should extend
+[Subject](https://truth.dev/api/latest/com/google/common/truth/Subject.html) and
+follow the naming schema `[ClassUnderTest]Subject`. The Subject must also have a
+constructor that accepts
+[FailureMetadata](https://truth.dev/api/latest/com/google/common/truth/FailureMetadata.html)
+and a reference to the object under test, which are both passed to the
+superclass.
+
+```kotlin
+class NavControllerSubject private constructor(
+    metadata: FailureMetadata,
+    private val actual: NavController
+) : Subject(metadata, actual) { }
+```
+
+### Subject factory
+
+The Subject class should also contain two static fields; a
+[Subject Factory](https://truth.dev/api/latest/com/google/common/truth/Subject.Factory.html)
+and an`assertThat()` shortcut method.
+
+A subject Factory provides most of the functionality of the Subject by allowing
+users to perform all operations provided in the Subject class by passing this
+factory to `about()` methods. E.g:
+
+`assertAbout(navControllers()).that(navController).isGraph(x)` where `isGraph()`
+is a method defined in the Subject class.
+
+The assertThat() shortcut method simply provides a shorthand method for making
+assertions about the Subject without needing a reference to the subject factory.
+i.e., rather than using
+`assertAbout(navControllers()).that(navController).isGraph(x)` users can simply
+use`assertThat(navController).isGraph(x)`.
+
+```kotlin
+companion object {
+    fun navControllers(): Factory<NavControllerSubject, NavController> =
+        Factory<NavControllerSubject, NavController> { metadata, actual ->
+            NavControllerSubject(metadata, actual)
+        }
+
+    @JvmStatic
+    fun assertThat(actual: NavController): NavControllerSubject {
+        return assertAbout(navControllers()).that(actual)
+    }
+}
+```
+
+### Assertion methods
+
+When creating assertion methods for your custom Subject the names of these
+methods should follow the
+[Truth naming convention](https://truth.dev/faq#assertion-naming).
+
+To create assertion methods it is necessary to either delegate to an existing
+assertion method by using `Subject.check()` or to provide your own failure
+strategy by using`failWithActual()` or `failWithoutActual()`.
+
+When using `failWithActual()` the error message will display the`toString()`
+value of the Subject object. Additional information can be added to these error
+messages by using `fact(key, value)` or `simpleFact(value)` where `fact()` will
+be output as a colon separated key, value pair.
+
+```kotlin
+fun isGraph(@IdRes navGraph: Int) {
+    check("graph()").that(actual.graph.id).isEqualTo(navGraph)
+}
+
+// Sample Failure Message
+value of          : navController.graph()
+expected          : 29340
+but was           : 10394
+navController was : {actual.toString() value}
+```
+
+```kotlin
+fun isGraph(@IdRes navGraph: Int) {
+    val actualGraph = actual.graph.id
+    if (actualGraph != navGraph) {
+        failWithoutActual(
+            fact("expected id", navGraph.toString()),
+            fact("but was", actualGraph.toString()),
+            fact("current graph is", actual.graph)
+        )
+    }
+}
+
+// Sample Failure Message
+expected id      : 29340
+but was          : 10394
+current graph is : {actual.graph.toString() value}
+```
+
+## Testing
+
+When testing expected successful assertions it is enough to simply call the
+assertion with verified successful actual and expected values.
+
+```kotlin
+private lateinit var navController: NavController
+@Before
+fun setUp() {
+    navController = NavController(
+        ApplicationProvider.getApplicationContext()
+    ).apply {
+        navigationProvider += TestNavigator()
+        // R.navigation.testGraph == R.id.test_graph
+        setGraph(R.navigation.test_graph)
+    }
+}
+
+@Test
+fun testGraph() {
+    assertThat(navController).isGraph(R.id.test_graph)
+}
+```
+
+To test that expected failure cases you should use the
+[assertThrows](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:testutils/testutils-truth/src/main/java/androidx/testutils/assertions.kt)
+function from the AndroidX testutils module. The assertions.kt file contains two
+assertThrows functions. The second method, which specifically returns a
+TruthFailureSubject, should be used since it allows for validating additional
+information about the FailureSubject, particularly that it contains specific
+fact messages.
+
+```kotlin
+@Test
+fun testGraphFailure() {
+    with(assertThrows {
+        assertThat(navController).isGraph(R.id.second_test_graph)
+    }) {
+        factValue("expected id").isEqualTo(R.id.second_test_graph.toString())
+        factValue("but was").isEqualTo(navController.graph.id.toString())
+        factValue("current graph is").isEqualTo(navController.graph.toString())
+    }
+}
+```
+
+## Helpful resources
+
+[Truth extension points](https://truth.dev/extension.html)
diff --git a/docs/versioning.md b/docs/versioning.md
new file mode 100644
index 0000000..f1212a3
--- /dev/null
+++ b/docs/versioning.md
@@ -0,0 +1,319 @@
+# Versioning
+
+[TOC]
+
+## Semantic Versioning
+
+Artifacts follow strict semantic versioning. The version for a finalized release
+will follow the format `<major>.<minor>.<bugfix>` with an optional
+`-<alpha|beta><n>` suffix. Internal or nightly releases should use the
+`-SNAPSHOT` suffix to indicate that the release bits are subject to change.
+
+Also check out the [Versioning FAQ](faq.md#version).
+
+### Major (`x.0.0`) {#major}
+
+An artifact's major version indicates a guaranteed forward-compatibility window.
+For example, a developer could update an artifact versioned `2.0.0` to `2.7.3`
+without taking any additional action.
+
+#### When to increment
+
+An artifact *must* increment its major version number in response to breaking
+changes in binary or behavioral compatibility within the library itself _or_ in
+response to breaking changes within a dependency.
+
+For example, if an artifact updates a SemVer-type dependency from `1.0.0` to
+`2.0.0` then the artifact must also bump its own major version number.
+
+An artifact *may in rare cases* increment its major version number to indicate
+an important but non-breaking change in the library. Note, however, that the
+SemVer implications of incrementing the major version are the same as a breaking
+change -- dependent projects _must_ assume the major version change is breaking
+and update their dependency specifications.
+
+#### Ecosystem implications
+
+When an artifact increases its major version, _all_ artifacts that depended on
+the previous major version are no longer considered compatible and must
+explicitly migrate to depend on the new major version.
+
+As a result, if the library ecosystem is slow to adopt a new major version of an
+artifact then developers may end up in a situation where they cannot update an
+artifact because they depend on a library that has not yet adopted the new major
+version.
+
+For this reason, we *strongly* recommend against increasing the major version of
+a “core” artifact that is depended upon by other libraries. “Leaf” artifacts --
+those that apps depend upon directly and are not used by other libraries -- have
+a much easier time increasing their major version.
+
+#### Process requirements
+
+If the artifact has dependencies within Jetpack, owners *must* complete the
+assessment before implementing any breaking changes to binary or behavioral
+compatibility.
+
+Otherwise, owners are *strongly recommended* to complete the assessment before
+implementing any breaking changes to binary or behavioral compatibility, as such
+changes may negatively impact downstream clients in Android git or Google's
+repository. These clients are not part of our pre-submit workflow, but filling
+out the assessment will provide insight into how they will be affected by a
+major version change.
+
+### Minor (`1.x.0`) {#minor}
+
+Minor indicates compatible public API changes. This number is incremented when
+APIs are added, including the addition of `@Deprecated` annotations. Binary
+compatibility must be preserved between minor version changes.
+
+#### Moving between minor versions:
+
+*   A change in the minor revision indicates the addition of binary-compatible
+    APIs. Libraries **must** increment their minor revision when adding APIs.
+    Dependent libraries are not required to update their minimum required
+    version unless they depend on newly-added APIs.
+
+### Bugfix (`1.0.x`) {#bugfix}
+
+Bugfix indicates internal changes to address broken behavior. Care should be
+taken to ensure that existing clients are not broken, including clients that may
+have been working around long-standing broken behavior.
+
+#### Moving between bugfix versions:
+
+*   A change in the bugfix revision indicates changes in behavior to fix bugs.
+    The API surface does not change. Changes to the bugfix version may *only*
+    occur in a release branch. The bugfix revision must always be `.0` in a
+    development branch.
+
+### Pre-release suffixes {#pre-release-suffix}
+
+The pre-release suffix indicates stability and feature completeness of a
+release. A typical release will begin as alpha, move to beta after acting on
+feedback from internal and external clients, move to release candidate for final
+verification, and ultimately move to a finalized build.
+
+Alpha, beta, and release candidate releases are versioned sequentially using a
+leading zero (ex. alpha01, beta11, rc01) for compatibility with the
+lexicographic ordering of versions used by SemVer.
+
+### Snapshot {#snapshot}
+
+Snapshot releases are whatever exists at tip-of-tree. They are only subject to
+the constraints placed on the average commit. Depending on when it's cut, a
+snapshot may even be binary-identical to an alpha, beta, or stable release.
+
+Versioning policies are enforced by the following Gradle tasks:
+
+`checkApi`: ensures that changes to public API are intentional and tracked,
+asking the developer to explicitly run updateApi (see below) if any changes are
+detected
+
+`checkApiRelease`: verifies that API changes between previously released and
+currently under-development versions conform to semantic versioning guarantees
+
+`updateApi`: commits API changes to source control in such a way that they can
+be reviewed in pre-submit via Gerrit API+1 and reviewed in post-submit by API
+Council
+
+`SNAPSHOT`: is automatically added to the version string for all builds that
+occur outside the build server for release branches (ex. ub-androidx-release).
+Local release builds may be forced by passing -Prelease to the Gradle command
+line.
+
+## Picking the right version {#picking-the-right-version}
+
+AndroidX follows [Strict Semantic Versioning](https://semver.org), which means
+that the version code is strongly tied to the API surface. A full version
+consists of revision numbers for major, minor, and bugfix as well as a
+pre-release stage and revision. Correct versioning is, for the most part,
+automatically enforced; however, please check for the following:
+
+### Initial version {#initial-version}
+
+If your library is brand new, your version should start at 1.0.0, e.g.
+`1.0.0-alpha01`.
+
+The initial release within a new version always starts at `alpha01`. Note the
+two-digit revision code, which allows us to do up to 99 revisions within a
+pre-release stage.
+
+### Pre-release stages
+
+A single version will typically move through several revisions within each of
+the pre-release stages: alpha, beta, rc, and stable. Subsequent revisions within
+a stage (ex. alpha, beta) are incremented by 1, ex. `alpha01` is followed by
+`alpha02` with no gaps.
+
+### Moving between pre-release stages and revisions
+
+Libraries are expected to go through a number of pre-release stages within a
+version prior to release, with stricter requirements at each stage to ensure a
+high-quality stable release. The owner for a library should typically submit a
+CL to update the stage or revision when they are ready to perform a public
+release.
+
+Libraries are expected to allow >= 2 weeks per pre-release stage. This 'soaking
+period' gives developers time to try/use each version, find bugs, and ensure a
+quality stable release. Therefore, at minimum:
+
+-   An `alpha` version must be publically available for 2 weeks before releasing
+    a public `beta`
+-   A `beta` version must be publically available for 2 weeks before releasing
+    an public `rc`
+-   A `rc` version must be publically available fore 2 weeks before releasing a
+    public stable version
+
+Your library must meet the following criteria to move your public release to
+each stage:
+
+### Alpha {#alpha}
+
+Alpha releases are expected to be functionally stable, but may have unstable API
+surface or incomplete features. Typically, alphas have not gone through API
+Council review but are expected to have performed a minimum level of validation.
+
+#### Within the `alphaXX` cycle
+
+*   API surface
+    *   Prior to `alpha01` release, API tracking **must** be enabled (either
+        `publish=true` or create an `api` directory) and remain enabled
+    *   May add/remove APIs within `alpha` cycle, but deprecate/remove cycle is
+        strongly recommended.
+*   Testing
+    *   All changes **should** be accompanied by a `Test:` stanza
+    *   All pre-submit and post-submit tests are passing
+    *   Flaky or failing tests **must** be suppressed or fixed within one day
+        (if affecting pre-submit) or three days (if affecting post-submit)
+
+### Beta {#beta}
+
+Beta releases are ready for production use but may contain bugs. They are
+expected to be functionally stable and have highly-stable, feature-complete API
+surface. APIs should have been reviewed by API Council at this stage, and new
+APIs may only be added with approval by API Council. Tests must have 100%
+coverage of public API surface and translations must be 100% complete.
+
+#### Checklist for moving to `beta01`
+
+*   API surface
+    *   Entire API surface has been reviewed by API Council
+    *   All APIs from alpha undergoing deprecate/remove cycle must be removed
+*   Testing
+    *   All public APIs are tested
+    *   All pre-submit and post-submit tests are enabled (e.g. all suppressions
+        are removed) and passing
+    *   Your library passes `./gradlew library:checkReleaseReady`
+*   No experimental features (e.g. `@UseExperimental`) may be used
+*   All dependencies are `beta`, `rc`, or stable
+*   Be able to answer the question "How will developers test their apps against
+    your library?"
+    *   Ideally, this is an integration app with automated tests that cover the
+        main features of the library and/or a `-testing` artifact as seen in
+        other Jetpack libraries
+
+#### Within the `betaXX` cycle
+
+*   API surface
+    *   New APIs discouraged unless P0 or P1 (ship-blocking)
+    *   May not remove `@Experimental` from experimental APIs, see previous item
+        regarding new APIs
+    *   No API removals allowed
+
+### RC {#rc}
+
+Release candidates are expected to be nearly-identical to the final release, but
+may contain critical last-minute fixes for issues found during integration
+testing.
+
+#### Checklist for moving to `rc01`
+
+*   All previous checklists still apply
+*   Release branch, e.g. `androidx-<group_id>-release`, is created
+*   API surface
+    *   Any API changes from `beta` cycle are reviewed by API Council
+*   No **known** P0 or P1 (ship-blocking) issues
+*   All dependencies are `rc` or stable
+
+#### Within the `rcXX` cycle
+
+*   Ship-blocking bug fixes only
+*   All changes must have corresponding tests
+*   No API changes allowed
+
+### Stable {#stable}
+
+Final releases are well-tested, both by internal tests and external clients, and
+their API surface is reviewed and finalized. While APIs may be deprecated and
+removed in future versions, any APIs added at this stage must remain for at
+least a year.
+
+#### Checklist for moving to stable
+
+*   Identical to a previously released `rcXX` (note that this means any bugs
+    that result in a new release candidate will necessarily delay your stable
+    release by a minimum of two weeks)
+*   No changes of any kind allowed
+*   All dependencies are stable
+
+## Updating your version {#update}
+
+A few notes about version updates:
+
+-   The version of your library listed in `androidx-master-dev` should *always*
+    be higher than the version publically available on Google Maven. This allows
+    us to do proper version tracking and API tracking.
+
+-   Version increments must be done before the CL cutoff date (aka the build cut
+    date).
+
+-   **Increments to the next stability suffix** (like `alpha` to `beta`) should
+    be handled by the library owner, with the Jetpack TPM (nickanthony@) CC'd
+    for API+1.
+
+-   Version increments in release branches will need to follow the guide
+    [How to update your version on a release branch](release_branches.md#update-your-version)
+
+-   When you're ready for `rc01`, the increment to `rc01` should be done in
+    `androidx-master-dev` and then your release branch should be snapped to that
+    build. See the guide [Snap your release branch](release_branches.md#snap) on
+    how to do this. After the release branch is snapped to that build, you will
+    need to update your version in `androidx-master-dev` to `alpha01` of the
+    next minor (or major) version.
+
+### Bi-weekly batched releases (every 2 weeks)
+
+If you participate in a bi-weekly (every 2 weeks) batched release, the Jetpack
+TPM will increment versions for you the day after the build cut deadline. The
+increments are defaulted to increments within the same pre-release suffix.
+
+For example, if you are releasing `1.1.0-alpha04`, the day after the build cut,
+the TPM will increment the version to `1.1.0-alpha05` for the next release.
+
+### How to update your version
+
+1.  Update the version listed in
+    `frameworks/support/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt`
+1.  Run `./gradlew <your-lib>:updateApi`. This will create an API txt file for
+    the new version of your library.
+1.  Verify changes with `./gradlew checkApi verifyDependencyVersions`.
+1.  Commit these change as one commit.
+1.  Upload these changes to Gerrit for review.
+
+An example of a version bump can be found here:
+[aosp/833800](https://android-review.googlesource.com/c/platform/frameworks/support/+/833800)
+
+## `-ktx` Modules {#ktx}
+
+Kotlin Extension modules (`-ktx`) for regular Java modules follow the same
+requirements, but with one exception. They must match the version of the Java
+module that they extend.
+
+For example, let's say you are developing a java library
+`androidx.foo:foo-bar:1.1.0-alpha01` and you want to add a kotlin extension
+module `androidx.foo:foo-bar-ktx` module. Your new `androidx.foo:foo-bar-ktx`
+module will start at version `1.1.0-alpha01` instead of `1.0.0-alpha01`.
+
+If your `androidx.foo:foo-bar` module was in version `1.0.0-alpha06`, then the
+kotlin extension module would start in version `1.0.0-alpha06`.
diff --git a/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerCustomThemeTest.java b/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerCustomThemeTest.java
index 6b4033d..6b3c6b5 100644
--- a/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerCustomThemeTest.java
+++ b/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerCustomThemeTest.java
@@ -23,7 +23,6 @@
 import androidx.drawerlayout.test.R;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -31,9 +30,10 @@
 
 @RunWith(AndroidJUnit4.class)
 public class DrawerCustomThemeTest {
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<DrawerCustomThemeActivity> mActivityTestRule =
-            new ActivityTestRule<>(DrawerCustomThemeActivity.class);
+    public final androidx.test.rule.ActivityTestRule<DrawerCustomThemeActivity> mActivityTestRule =
+            new androidx.test.rule.ActivityTestRule<>(DrawerCustomThemeActivity.class);
 
     @Test
     @SmallTest
diff --git a/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerNoThemeTest.java b/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerNoThemeTest.java
index b44b5e9..745f8fe 100644
--- a/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerNoThemeTest.java
+++ b/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerNoThemeTest.java
@@ -23,7 +23,6 @@
 import androidx.drawerlayout.test.R;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -31,9 +30,10 @@
 
 @RunWith(AndroidJUnit4.class)
 public class DrawerNoThemeTest {
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<DrawerNoThemeActivity> mActivityTestRule =
-            new ActivityTestRule<>(DrawerNoThemeActivity.class);
+    public final androidx.test.rule.ActivityTestRule<DrawerNoThemeActivity> mActivityTestRule =
+            new androidx.test.rule.ActivityTestRule<>(DrawerNoThemeActivity.class);
 
     @Test
     @SmallTest
diff --git a/dynamic-animation/dynamic-animation/src/androidTest/java/androidx/dynamicanimation/tests/FlingTests.java b/dynamic-animation/dynamic-animation/src/androidTest/java/androidx/dynamicanimation/tests/FlingTests.java
index 2ad1ee0..75f6869 100644
--- a/dynamic-animation/dynamic-animation/src/androidTest/java/androidx/dynamicanimation/tests/FlingTests.java
+++ b/dynamic-animation/dynamic-animation/src/androidTest/java/androidx/dynamicanimation/tests/FlingTests.java
@@ -34,7 +34,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -44,19 +43,22 @@
 import org.mockito.internal.matchers.GreaterThan;
 import org.mockito.internal.matchers.LessThan;
 
+@SuppressWarnings("unchecked")
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class FlingTests {
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<AnimationActivity> mActivityTestRule;
+    public final androidx.test.rule.ActivityTestRule<AnimationActivity> mActivityTestRule;
     public View mView1;
     public View mView2;
 
     @Rule
     public ExpectedException mExpectedException = ExpectedException.none();
 
+    @SuppressWarnings("deprecation")
     public FlingTests() {
-        mActivityTestRule = new ActivityTestRule<>(AnimationActivity.class);
+        mActivityTestRule = new androidx.test.rule.ActivityTestRule<>(AnimationActivity.class);
     }
 
     @Before
diff --git a/dynamic-animation/dynamic-animation/src/androidTest/java/androidx/dynamicanimation/tests/SpringTests.java b/dynamic-animation/dynamic-animation/src/androidTest/java/androidx/dynamicanimation/tests/SpringTests.java
index b774708..d55d3ed 100644
--- a/dynamic-animation/dynamic-animation/src/androidTest/java/androidx/dynamicanimation/tests/SpringTests.java
+++ b/dynamic-animation/dynamic-animation/src/androidTest/java/androidx/dynamicanimation/tests/SpringTests.java
@@ -47,7 +47,6 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -55,18 +54,21 @@
 import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 
+@SuppressWarnings("unchecked")
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class SpringTests {
-    @Rule public final ActivityTestRule<AnimationActivity> mActivityTestRule;
+    @SuppressWarnings("deprecation")
+    @Rule public final androidx.test.rule.ActivityTestRule<AnimationActivity> mActivityTestRule;
     public View mView1;
     public View mView2;
 
     @Rule
     public ExpectedException mExpectedException = ExpectedException.none();
 
+    @SuppressWarnings("deprecation")
     public SpringTests() {
-        mActivityTestRule = new ActivityTestRule<>(AnimationActivity.class);
+        mActivityTestRule = new androidx.test.rule.ActivityTestRule<>(AnimationActivity.class);
     }
 
     @Before
diff --git a/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiKeyboardTest.java b/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiKeyboardTest.java
index 47142fd..0f0c808 100644
--- a/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiKeyboardTest.java
+++ b/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiKeyboardTest.java
@@ -36,7 +36,6 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.Suppress;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 import androidx.testutils.PollingCheck;
 
 import org.junit.Before;
@@ -50,9 +49,10 @@
 @Suppress
 public class EmojiKeyboardTest {
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
-            TestActivity.class);
+    public androidx.test.rule.ActivityTestRule<TestActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(TestActivity.class);
     private Instrumentation mInstrumentation;
 
     @BeforeClass
diff --git a/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiSpanInstrumentationTest.java b/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiSpanInstrumentationTest.java
index 4f4b856..15beb23 100644
--- a/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiSpanInstrumentationTest.java
+++ b/emoji/core/src/androidTest/java/androidx/emoji/text/EmojiSpanInstrumentationTest.java
@@ -35,7 +35,6 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -48,9 +47,10 @@
 @SdkSuppress(minSdkVersion = 19)
 public class EmojiSpanInstrumentationTest {
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
-            TestActivity.class);
+    public androidx.test.rule.ActivityTestRule<TestActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(TestActivity.class);
     private Instrumentation mInstrumentation;
 
     @BeforeClass
diff --git a/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiEditTextTest.java b/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiEditTextTest.java
index faa4ad1..a4d8db8 100644
--- a/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiEditTextTest.java
+++ b/emoji/core/src/androidTest/java/androidx/emoji/widget/EmojiEditTextTest.java
@@ -35,7 +35,6 @@
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -47,9 +46,10 @@
 @RunWith(AndroidJUnit4.class)
 public class EmojiEditTextTest {
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
-            TestActivity.class);
+    public androidx.test.rule.ActivityTestRule<TestActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(TestActivity.class);
     private Instrumentation mInstrumentation;
 
     @BeforeClass
diff --git a/emoji/core/src/androidTest/java/androidx/emoji/widget/SpannableBuilderTest.java b/emoji/core/src/androidTest/java/androidx/emoji/widget/SpannableBuilderTest.java
index 2e92a7c..25b29eb 100644
--- a/emoji/core/src/androidTest/java/androidx/emoji/widget/SpannableBuilderTest.java
+++ b/emoji/core/src/androidTest/java/androidx/emoji/widget/SpannableBuilderTest.java
@@ -151,6 +151,7 @@
         verify(mWatcher, times(1)).afterTextChanged(any(Editable.class));
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void testDoesNotBlockSpanCallbacks_forNonEmojiSpans() {
         final QuoteSpan span = mock(QuoteSpan.class);
diff --git a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporterTest.java b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporterTest.java
index 85484de..4bbfe9f 100644
--- a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporterTest.java
+++ b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporterTest.java
@@ -66,6 +66,7 @@
 import java.util.concurrent.Executor;
 
 /** Tests {@link DefaultKeyedAppStatesReporter}. */
+@SuppressWarnings("deprecation")
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 @Config(minSdk = 21)
diff --git a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStateTest.java b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStateTest.java
index 464fbb3..a481663 100644
--- a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStateTest.java
+++ b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStateTest.java
@@ -40,6 +40,7 @@
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests {@link KeyedAppState}. */
+@SuppressWarnings("deprecation")
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
 @Config(minSdk = 21)
diff --git a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/TestHandler.java b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/TestHandler.java
index f8d0945..d09aeb7 100644
--- a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/TestHandler.java
+++ b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/TestHandler.java
@@ -25,6 +25,7 @@
 
 /** Handler which stores the most recently handled message in a public field. */
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 class TestHandler extends Handler {
 
     @Nullable
diff --git a/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporterTest.java b/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporterTest.java
index fe34f6a..5685db7 100644
--- a/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporterTest.java
+++ b/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporterTest.java
@@ -82,6 +82,7 @@
         assertThat(mReporter.getOnDeviceKeyedAppStates()).containsExactly(KEYED_APP_STATE);
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void setStates_deprecated_isRecordedInOnDeviceKeyedAppStates() {
         mReporter.setStates(singletonList(KEYED_APP_STATE));
@@ -214,6 +215,7 @@
         assertThat(mReporter.getUploadedKeyedAppStates()).containsExactly(KEYED_APP_STATE);
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void setStatesImmediate_deprecated_isRecordedInUploadedKeyedAppStates() {
         mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE));
diff --git a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
index 77b8b66..ba44e3b 100644
--- a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
+++ b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
@@ -219,23 +219,23 @@
     private static final HashMap<Integer, Pair> FLIP_STATE_AND_ROTATION_DEGREES = new HashMap<>();
     static {
         FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_UNDEFINED, new Pair(false, 0));
+                ExifInterface.ORIENTATION_UNDEFINED, new Pair<>(false, 0));
         FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_NORMAL, new Pair(false, 0));
+                ExifInterface.ORIENTATION_NORMAL, new Pair<>(false, 0));
         FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_ROTATE_90, new Pair(false, 90));
+                ExifInterface.ORIENTATION_ROTATE_90, new Pair<>(false, 90));
         FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_ROTATE_180, new Pair(false, 180));
+                ExifInterface.ORIENTATION_ROTATE_180, new Pair<>(false, 180));
         FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_ROTATE_270, new Pair(false, 270));
+                ExifInterface.ORIENTATION_ROTATE_270, new Pair<>(false, 270));
         FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_FLIP_HORIZONTAL, new Pair(true, 0));
+                ExifInterface.ORIENTATION_FLIP_HORIZONTAL, new Pair<>(true, 0));
         FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_TRANSVERSE, new Pair(true, 90));
+                ExifInterface.ORIENTATION_TRANSVERSE, new Pair<>(true, 90));
         FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_FLIP_VERTICAL, new Pair(true, 180));
+                ExifInterface.ORIENTATION_FLIP_VERTICAL, new Pair<>(true, 180));
         FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_TRANSPOSE, new Pair(true, 270));
+                ExifInterface.ORIENTATION_TRANSPOSE, new Pair<>(true, 270));
     }
 
     private static final String[] EXIF_TAGS = {
@@ -684,6 +684,22 @@
         exif.saveAttributes();
         exif = new ExifInterface(imageFile.getAbsolutePath());
         assertEquals(currentTimeStamp - expectedDatetimeOffsetLongValue, (long) exif.getDateTime());
+
+        // Test that setting null throws NPE
+        try {
+            exif.setDateTime(null);
+            fail();
+        } catch (NullPointerException e) {
+            // Expected
+        }
+
+        // Test that setting negative value throws IAE
+        try {
+            exif.setDateTime(-1L);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
     }
 
     /**
@@ -757,29 +773,80 @@
         assertEquals(dateTimeOriginalValue, exif.getAttribute(ExifInterface.TAG_DATETIME));
     }
 
-    // TODO: Add tests for other variations (e.g. single/double digit number strings)
     @Test
     @LargeTest
-    public void testParsingSubsec() throws IOException {
+    public void testSubsec() throws IOException {
         File imageFile = getFileFromExternalDir(JPEG_WITH_DATETIME_TAG_PRIMARY_FORMAT);
         ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
-        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, /* 0ms */ "000000");
+
+        // Set initial value to 0
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, /* 0ms */ "000");
         exif.saveAttributes();
+        assertEquals("000", exif.getAttribute(ExifInterface.TAG_SUBSEC_TIME));
         long currentDateTimeValue = exif.getDateTime();
 
-        // Check that TAG_SUBSEC_TIME values starting with zero are supported.
-        // Note: getDateTime() supports only up to 1/1000th of a second.
-        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, /* 1ms */ "001000");
-        exif.saveAttributes();
-        assertEquals(currentDateTimeValue + 1, (long) exif.getDateTime());
-
-        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, /* 10ms */ "010000");
-        exif.saveAttributes();
-        assertEquals(currentDateTimeValue + 10, (long) exif.getDateTime());
-
-        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, /* 100ms */ "100000");
+        // Test that single and double-digit values are set properly.
+        // Note that since SubSecTime tag records fractions of a second, a single-digit value
+        // should be counted as the first decimal value, which is why "1" becomes 100ms and "11"
+        // becomes 110ms.
+        String oneDigitSubSec = "1";
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, oneDigitSubSec);
         exif.saveAttributes();
         assertEquals(currentDateTimeValue + 100, (long) exif.getDateTime());
+        assertEquals(oneDigitSubSec, exif.getAttribute(ExifInterface.TAG_SUBSEC_TIME));
+
+        String twoDigitSubSec1 = "01";
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, twoDigitSubSec1);
+        exif.saveAttributes();
+        assertEquals(currentDateTimeValue + 10, (long) exif.getDateTime());
+        assertEquals(twoDigitSubSec1, exif.getAttribute(ExifInterface.TAG_SUBSEC_TIME));
+
+        String twoDigitSubSec2 = "11";
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, twoDigitSubSec2);
+        exif.saveAttributes();
+        assertEquals(currentDateTimeValue + 110, (long) exif.getDateTime());
+        assertEquals(twoDigitSubSec2, exif.getAttribute(ExifInterface.TAG_SUBSEC_TIME));
+
+        // Test that 3-digit values are set properly.
+        String hundredMs = "100";
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, hundredMs);
+        exif.saveAttributes();
+        assertEquals(currentDateTimeValue + 100, (long) exif.getDateTime());
+        assertEquals(hundredMs, exif.getAttribute(ExifInterface.TAG_SUBSEC_TIME));
+
+        // Test that values starting with zero are also supported.
+        String oneMsStartingWithZeroes = "001";
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, oneMsStartingWithZeroes);
+        exif.saveAttributes();
+        assertEquals(currentDateTimeValue + 1, (long) exif.getDateTime());
+        assertEquals(oneMsStartingWithZeroes, exif.getAttribute(ExifInterface.TAG_SUBSEC_TIME));
+
+        String tenMsStartingWithZero = "010";
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, tenMsStartingWithZero);
+        exif.saveAttributes();
+        assertEquals(currentDateTimeValue + 10, (long) exif.getDateTime());
+        assertEquals(tenMsStartingWithZero, exif.getAttribute(ExifInterface.TAG_SUBSEC_TIME));
+
+        // Test that values with more than three digits are set properly. getAttribute() should
+        // return the whole string, but getDateTime() should only add the first three digits
+        // because it supports only up to 1/1000th of a second.
+        String fourDigitSubSec = "1234";
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, fourDigitSubSec);
+        exif.saveAttributes();
+        assertEquals(currentDateTimeValue + 123, (long) exif.getDateTime());
+        assertEquals(fourDigitSubSec, exif.getAttribute(ExifInterface.TAG_SUBSEC_TIME));
+
+        String fiveDigitSubSec = "23456";
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, fiveDigitSubSec);
+        exif.saveAttributes();
+        assertEquals(currentDateTimeValue + 234, (long) exif.getDateTime());
+        assertEquals(fiveDigitSubSec, exif.getAttribute(ExifInterface.TAG_SUBSEC_TIME));
+
+        String sixDigitSubSec = "345678";
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME, sixDigitSubSec);
+        exif.saveAttributes();
+        assertEquals(currentDateTimeValue + 345, (long) exif.getDateTime());
+        assertEquals(sixDigitSubSec, exif.getAttribute(ExifInterface.TAG_SUBSEC_TIME));
     }
 
     @Test
@@ -852,6 +919,7 @@
 
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     @SmallTest
     public void testInterchangeabilityBetweenTwoIsoSpeedTags() throws IOException {
diff --git a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
index 76d6f81..4099177 100644
--- a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
+++ b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
@@ -5151,9 +5151,21 @@
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     public void setDateTime(@NonNull Long timeStamp) {
-        long sub = timeStamp % 1000;
+        if (timeStamp == null) {
+            throw new NullPointerException("Timestamp should not be null.");
+        }
+
+        if (timeStamp < 0) {
+            throw new IllegalArgumentException("Timestamp should a positive value.");
+        }
+
+        long subsec = timeStamp % 1000;
+        String subsecString = Long.toString(subsec);
+        for (int i = subsecString.length(); i < 3; i++) {
+            subsecString = "0" + subsecString;
+        }
         setAttribute(TAG_DATETIME, sFormatterPrimary.format(new Date(timeStamp)));
-        setAttribute(TAG_SUBSEC_TIME, Long.toString(sub));
+        setAttribute(TAG_SUBSEC_TIME, subsecString);
     }
 
     /**
diff --git a/fragment/README.md b/fragment/README.md
index 413f993..7ad2c0a 100644
--- a/fragment/README.md
+++ b/fragment/README.md
@@ -6,7 +6,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/fragment)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/fragment/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/fragment/)
 
 [Reference documentation](https://developer.android.com/reference/androidx/fragment/app/package-summary)
 
diff --git a/fragment/fragment-testing/src/main/java/androidx/fragment/app/testing/FragmentScenario.kt b/fragment/fragment-testing/src/main/java/androidx/fragment/app/testing/FragmentScenario.kt
index 0674e06..a2735f9 100644
--- a/fragment/fragment-testing/src/main/java/androidx/fragment/app/testing/FragmentScenario.kt
+++ b/fragment/fragment-testing/src/main/java/androidx/fragment/app/testing/FragmentScenario.kt
@@ -228,7 +228,7 @@
  * If your testing Fragment has a dependency to specific theme such as `Theme.AppCompat`,
  * use the theme ID parameter in [launch] method.
  *
- * @param <F> The Fragment class being tested
+ * @param F The Fragment class being tested
  *
  * @see ActivityScenario a scenario API for Activity
  */
diff --git a/fragment/fragment/src/androidTest/AndroidManifest.xml b/fragment/fragment/src/androidTest/AndroidManifest.xml
index 5bac34a..97ee537 100644
--- a/fragment/fragment/src/androidTest/AndroidManifest.xml
+++ b/fragment/fragment/src/androidTest/AndroidManifest.xml
@@ -48,6 +48,7 @@
         <activity android:name="androidx.fragment.app.FragmentSavedStateActivity" />
         <activity android:name="androidx.fragment.app.FragmentFinishEarlyTestActivity" />
         <activity android:name="androidx.fragment.app.SimpleContainerActivity" />
+        <activity android:name="androidx.fragment.app.ReplaceInCreateActivity" />
         <activity android:name="androidx.fragment.app.DialogActivity"
                   android:theme="@style/DialogTheme"/>
         <activity android:name="androidx.fragment.app.TestAppCompatActivity"
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentFocusTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentFocusTest.kt
index d23e658..38b7570 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentFocusTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentFocusTest.kt
@@ -19,6 +19,7 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
+import android.os.Bundle
 import android.view.View
 import android.view.ViewGroup
 import android.widget.EditText
@@ -71,6 +72,31 @@
         }
     }
 
+    @Test
+    fun focusedViewRootView() {
+        with(ActivityScenario.launch(FragmentTestActivity::class.java)) {
+            val fragment = RequestViewFragment()
+
+            withActivity {
+                setContentView(R.layout.simple_container)
+
+                supportFragmentManager.beginTransaction()
+                    .setCustomAnimations(1, 0)
+                    .replace(R.id.fragmentContainer, fragment)
+                    .setReorderingAllowed(true)
+                    .commitNow()
+            }
+
+            assertThat(fragment.endAnimationCountDownLatch.await(1000, TimeUnit.MILLISECONDS))
+                .isTrue()
+
+            withActivity {
+                val view = fragment.requireView()
+                assertThat(view.isFocused).isTrue()
+            }
+        }
+    }
+
     class RemoveEditViewFragment : StrictViewFragment(R.layout.with_edit_text) {
         val endAnimationCountDownLatch = CountDownLatch(1)
         override fun onCreateAnimator(transit: Int, enter: Boolean, nextAnim: Int): Animator? {
@@ -95,4 +121,28 @@
             return animator
         }
     }
+
+    class RequestViewFragment : StrictViewFragment() {
+        val endAnimationCountDownLatch = CountDownLatch(1)
+        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+            super.onViewCreated(view, savedInstanceState)
+            view.isFocusable = true
+            view.isFocusableInTouchMode = true
+            view.requestFocus()
+        }
+
+        override fun onCreateAnimator(transit: Int, enter: Boolean, nextAnim: Int): Animator? {
+            if (nextAnim == 0) {
+                return null
+            }
+
+            val animator = ValueAnimator.ofFloat(0f, 1f).setDuration(1)
+            animator.addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator?) {
+                    endAnimationCountDownLatch.countDown()
+                }
+            })
+            return animator
+        }
+    }
 }
\ No newline at end of file
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReplaceTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReplaceTest.kt
index 4c155a7..f10ddfc 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReplaceTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReplaceTest.kt
@@ -15,60 +15,121 @@
  */
 package androidx.fragment.app
 
+import android.os.Bundle
 import android.view.View
 import androidx.fragment.app.test.FragmentTestActivity
 import androidx.fragment.test.R
+import androidx.test.core.app.ActivityScenario
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
+import androidx.test.filters.LargeTest
+import androidx.testutils.withActivity
 import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 /**
- * Test to prevent regressions in SupportFragmentManager fragment replace method. See b/24693644
+ * Test to prevent regressions in SupportFragmentManager fragment replace method.
  */
 @RunWith(AndroidJUnit4::class)
-@MediumTest
+@LargeTest
 class FragmentReplaceTest {
-    @Suppress("DEPRECATION")
-    @get:Rule
-    val activityRule = androidx.test.rule.ActivityTestRule(FragmentTestActivity::class.java)
 
     @Test
     fun testReplaceFragment() {
-        val activity = activityRule.activity
-        val fm = activity.supportFragmentManager
+        with(ActivityScenario.launch(FragmentTestActivity::class.java)) {
+            val fm = withActivity { supportFragmentManager }
 
-        fm.beginTransaction()
-            .add(R.id.content, StrictViewFragment(R.layout.fragment_a))
-            .addToBackStack(null)
-            .commit()
-        executePendingTransactions(fm)
-        assertThat(activity.findViewById<View>(R.id.textA)).isNotNull()
-        assertThat(activity.findViewById<View>(R.id.textB)).isNull()
-        assertThat(activity.findViewById<View>(R.id.textC)).isNull()
+            fm.beginTransaction()
+                .add(R.id.content, StrictViewFragment(R.layout.fragment_a))
+                .addToBackStack(null)
+                .commit()
+            executePendingTransactions()
+            withActivity {
+                assertThat(findViewById<View>(R.id.textA)).isNotNull()
+                assertThat(findViewById<View>(R.id.textB)).isNull()
+                assertThat(findViewById<View>(R.id.textC)).isNull()
+            }
 
-        fm.beginTransaction()
-            .add(R.id.content, StrictViewFragment(R.layout.fragment_b))
-            .addToBackStack(null)
-            .commit()
-        executePendingTransactions(fm)
-        assertThat(activity.findViewById<View>(R.id.textA)).isNotNull()
-        assertThat(activity.findViewById<View>(R.id.textB)).isNotNull()
-        assertThat(activity.findViewById<View>(R.id.textC)).isNull()
+            fm.beginTransaction()
+                .add(R.id.content, StrictViewFragment(R.layout.fragment_b))
+                .addToBackStack(null)
+                .commit()
+            executePendingTransactions()
+            withActivity {
+                assertThat(findViewById<View>(R.id.textA)).isNotNull()
+                assertThat(findViewById<View>(R.id.textB)).isNotNull()
+                assertThat(findViewById<View>(R.id.textC)).isNull()
+            }
 
-        activity.supportFragmentManager.beginTransaction()
-            .replace(R.id.content, StrictViewFragment(R.layout.fragment_c))
-            .addToBackStack(null)
-            .commit()
-        executePendingTransactions(fm)
-        assertThat(activity.findViewById<View>(R.id.textA)).isNull()
-        assertThat(activity.findViewById<View>(R.id.textB)).isNull()
-        assertThat(activity.findViewById<View>(R.id.textC)).isNotNull()
+            fm.beginTransaction()
+                .replace(R.id.content, StrictViewFragment(R.layout.fragment_c))
+                .addToBackStack(null)
+                .commit()
+            executePendingTransactions()
+            withActivity {
+                assertThat(findViewById<View>(R.id.textA)).isNull()
+                assertThat(findViewById<View>(R.id.textB)).isNull()
+                assertThat(findViewById<View>(R.id.textC)).isNotNull()
+            }
+        }
     }
 
-    private fun executePendingTransactions(fm: FragmentManager) {
-        activityRule.runOnUiThread { fm.executePendingTransactions() }
+    @Test
+    fun testReplaceFragmentInOnCreate() {
+        with(ActivityScenario.launch(ReplaceInCreateActivity::class.java)) {
+            val replaceInCreateFragment = withActivity { this.replaceInCreateFragment }
+
+            assertThat(replaceInCreateFragment.isAdded)
+                .isFalse()
+            withActivity {
+                assertThat(findViewById<View>(R.id.textA)).isNull()
+                assertThat(findViewById<View>(R.id.textB)).isNotNull()
+            }
+        }
+    }
+}
+
+class ReplaceInCreateActivity : FragmentActivity(R.layout.activity_content) {
+    private val parentFragment: ParentFragment
+        get() = supportFragmentManager.findFragmentById(R.id.content) as ParentFragment
+    val replaceInCreateFragment: ReplaceInCreateFragment
+        get() = parentFragment.replaceInCreateFragment
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        if (savedInstanceState == null) {
+            // This issue only appears for child fragments
+            // so add parent fragment that contains the ReplaceInCreateFragment
+            supportFragmentManager.beginTransaction()
+                .add(R.id.content, ParentFragment())
+                .setReorderingAllowed(true)
+                .commit()
+        }
+    }
+
+    class ParentFragment : StrictViewFragment(R.layout.simple_container) {
+        lateinit var replaceInCreateFragment: ReplaceInCreateFragment
+
+        override fun onCreate(savedInstanceState: Bundle?) {
+            super.onCreate(savedInstanceState)
+            if (savedInstanceState == null) {
+                replaceInCreateFragment = ReplaceInCreateFragment()
+                childFragmentManager.beginTransaction()
+                    .add(R.id.fragmentContainer, replaceInCreateFragment)
+                    .setReorderingAllowed(true)
+                    .commit()
+            }
+        }
+    }
+}
+
+class ReplaceInCreateFragment : StrictViewFragment(R.layout.fragment_a) {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        parentFragmentManager.beginTransaction()
+            .replace(R.id.fragmentContainer, StrictViewFragment(R.layout.fragment_b))
+            .setReorderingAllowed(true)
+            .commit()
     }
 }
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/OptionsMenuFragmentTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/OptionsMenuFragmentTest.kt
index 1775a46..1776faf 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/OptionsMenuFragmentTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/OptionsMenuFragmentTest.kt
@@ -42,7 +42,7 @@
     @Test
     fun fragmentWithOptionsMenu() {
         activityRule.setContentView(R.layout.simple_container)
-        val fragment = OptionsMenuFragment()
+        val fragment = MenuFragment()
         val fm = activityRule.activity.supportFragmentManager
         fm.beginTransaction()
             .add(R.id.fragmentContainer, fragment)
@@ -112,6 +112,30 @@
     }
 
     @Test
+    fun childFragmentWithPrepareOptionsMenuParentMenuVisibilityFalse() {
+        activityRule.setContentView(R.layout.simple_container)
+        val parent = ParentOptionsMenuFragment()
+        val fm = activityRule.activity.supportFragmentManager
+
+        parent.setMenuVisibility(false)
+        fm.beginTransaction()
+            .add(R.id.fragmentContainer, parent, "parent")
+            .commit()
+        activityRule.executePendingTransactions()
+
+        assertWithMessage("Fragment should not have an options menu")
+            .that(parent.hasOptionsMenu()).isFalse()
+        assertWithMessage("Child fragment should have an options menu")
+            .that(parent.childFragmentManager.checkForMenus()).isTrue()
+
+        activityRule.runOnUiThread {
+            assertWithMessage("child fragment onCreateOptions menu was not called")
+                .that(parent.childFragment.onPrepareOptionsMenuCountDownLatch.count)
+                .isEqualTo(1)
+        }
+    }
+
+    @Test
     fun parentAndChildFragmentWithOptionsMenu() {
         activityRule.setContentView(R.layout.simple_container)
         val parent = ParentOptionsMenuFragment(true)
@@ -165,7 +189,7 @@
         activityRule.executePendingTransactions()
 
         parent.childFragmentManager.beginTransaction()
-            .add(R.id.fragmentContainer2, OptionsMenuFragment())
+            .add(R.id.fragmentContainer2, MenuFragment())
             .commit()
         activityRule.executePendingTransactions()
 
@@ -175,8 +199,9 @@
             .that(parent.mChildFragmentManager.checkForMenus()).isTrue()
     }
 
-    class OptionsMenuFragment : StrictViewFragment(R.layout.fragment_a) {
+    class MenuFragment : StrictViewFragment(R.layout.fragment_a) {
         val onCreateOptionsMenuCountDownLatch = CountDownLatch(1)
+        val onPrepareOptionsMenuCountDownLatch = CountDownLatch(1)
 
         override fun onCreate(savedInstanceState: Bundle?) {
             super.onCreate(savedInstanceState)
@@ -188,12 +213,17 @@
             onCreateOptionsMenuCountDownLatch.countDown()
             inflater.inflate(R.menu.example_menu, menu)
         }
+
+        override fun onPrepareOptionsMenu(menu: Menu) {
+            super.onPrepareOptionsMenu(menu)
+            onPrepareOptionsMenuCountDownLatch.countDown()
+        }
     }
 
     class ParentOptionsMenuFragment(
         val createMenu: Boolean = false
     ) : StrictViewFragment(R.layout.double_container) {
-        val childFragment = OptionsMenuFragment()
+        val childFragment = MenuFragment()
 
         override fun onCreate(savedInstanceState: Bundle?) {
             super.onCreate(savedInstanceState)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt
index 0720e07..256a07b 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt
@@ -51,6 +51,7 @@
         checkGetActivity()
         checkActivityNotDestroyed()
         checkState("onViewCreated", State.CREATED)
+        currentState = State.VIEW_CREATED
         onViewCreatedCalled = true
     }
 
@@ -78,7 +79,8 @@
                 .isNotNull()
         }
         checkGetActivity()
-        checkState("onDestroyView", State.CREATED)
+        checkState("onDestroyView", State.CREATED, State.VIEW_CREATED, State.ACTIVITY_CREATED)
+        currentState = State.CREATED
         onDestroyViewCalled = true
     }
 
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java
index cd15968..84142fb 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java
@@ -369,7 +369,7 @@
      * @param v {@link View} that might be added to list of disappearing views
      */
     private void addDisappearingFragmentView(@NonNull View v) {
-        if (v.getAnimation() != null || (mTransitioningFragmentViews != null
+        if (v.getAnimation() != null && (mTransitioningFragmentViews != null
                 && mTransitioningFragmentViews.contains(v))) {
             if (mDisappearingFragmentChildren == null) {
                 mDisappearingFragmentChildren = new ArrayList<>();
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index 2cc8680..f3e9d37 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -3193,7 +3193,7 @@
         boolean show = false;
         for (Fragment f : mFragmentStore.getFragments()) {
             if (f != null) {
-                if (f.performPrepareOptionsMenu(menu)) {
+                if (isParentMenuVisible(f) && f.performPrepareOptionsMenu(menu)) {
                     show = true;
                 }
             }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
index 03c6948..4446391 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
@@ -247,6 +247,10 @@
         if (mFragment.mDeferStart && mFragment.mState < Fragment.STARTED) {
             maxState = Math.min(maxState, Fragment.ACTIVITY_CREATED);
         }
+        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+            Log.v(FragmentManager.TAG, "computeExpectedState() of " + maxState + " for "
+                    + mFragment);
+        }
         return maxState;
     }
 
@@ -335,10 +339,11 @@
                             mFragment.mState = Fragment.AWAITING_EXIT_EFFECTS;
                             break;
                         case Fragment.VIEW_CREATED:
-                            destroyFragmentView();
+                            mFragment.mInLayout = false;
                             mFragment.mState = Fragment.VIEW_CREATED;
                             break;
                         case Fragment.CREATED:
+                            destroyFragmentView();
                             mFragment.mState = Fragment.CREATED;
                             break;
                         case Fragment.ATTACHED:
@@ -604,6 +609,9 @@
     }
 
     private boolean isFragmentViewChild(@NonNull View view) {
+        if (view == mFragment.mView) {
+            return true;
+        }
         ViewParent parent = view.getParent();
         while (parent != null) {
             if (parent == mFragment.mView) {
@@ -722,6 +730,12 @@
         if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
             Log.d(TAG, "movefrom CREATE_VIEW: " + mFragment);
         }
+        // In cases where we never got up to AWAITING_EXIT_EFFECTS, we
+        // need to manually remove the view from the container to reverse
+        // what we did in createView()
+        if (mFragment.mContainer != null && mFragment.mView != null) {
+            mFragment.mContainer.removeView(mFragment.mView);
+        }
         mFragment.performDestroyView();
         mDispatcher.dispatchOnFragmentViewDestroyed(mFragment, false);
         mFragment.mContainer = null;
diff --git a/inspection/inspection-gradle-plugin/build.gradle b/inspection/inspection-gradle-plugin/build.gradle
index aff9915..b09f862 100644
--- a/inspection/inspection-gradle-plugin/build.gradle
+++ b/inspection/inspection-gradle-plugin/build.gradle
@@ -36,7 +36,7 @@
     implementation(AGP_STABLE)
     implementation(KOTLIN_STDLIB)
     implementation("gradle.plugin.com.google.protobuf:protobuf-gradle-plugin:0.8.13")
-    implementation("org.anarres.jarjar:jarjar-gradle:1.0.1")
+    implementation("com.github.jengelman.gradle.plugins:shadow:5.2.0")
 
     testImplementation(project(":internal-testutils-gradle-plugin"))
     testImplementation gradleTestKit()
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
index e8370d5..b10b84b 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
@@ -19,7 +19,6 @@
 import com.android.build.gradle.BaseExtension
 import com.android.build.gradle.api.BaseVariant
 import com.android.build.gradle.api.LibraryVariant
-import org.anarres.gradle.plugin.jarjar.JarjarTask
 import org.gradle.api.DefaultTask
 import org.gradle.api.Project
 import org.gradle.api.file.ConfigurableFileCollection
@@ -30,6 +29,7 @@
 import org.gradle.api.tasks.OutputFile
 import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.TaskProvider
+import org.gradle.api.tasks.bundling.Jar
 import java.io.File
 
 abstract class DexInspectorTask : DefaultTask() {
@@ -73,13 +73,13 @@
 fun Project.registerDexInspectorTask(
     variant: BaseVariant,
     extension: BaseExtension,
-    jarJar: TaskProvider<JarjarTask>
+    jar: TaskProvider<out Jar>
 ): TaskProvider<DexInspectorTask> {
     return tasks.register(variant.taskName("dexInspector"), DexInspectorTask::class.java) {
         it.setDx(extension.sdkDirectory, extension.buildToolsVersion)
-        it.jars.from(jarJar.get().destinationPath)
+        it.jars.from(jar.get().destinationDirectory)
         val out = File(taskWorkingDir(variant, "dexedInspector"), "${project.name}.jar")
         it.outputFile.set(out)
-        it.dependsOn(jarJar)
+        it.dependsOn(jar)
     }
 }
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
index 7751681..e5a1eba 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
@@ -52,8 +52,8 @@
                 if (variant.name == "release") {
                     foundReleaseVariant = true
                     val unzip = project.registerUnzipTask(variant)
-                    val jarJar = project.registerJarJarDependenciesTask(variant, unzip)
-                    dexTask = project.registerDexInspectorTask(variant, libExtension, jarJar)
+                    val shadowJar = project.registerShadowDependenciesTask(variant, unzip)
+                    dexTask = project.registerDexInspectorTask(variant, libExtension, shadowJar)
                 }
             }
             libExtension.sourceSets.findByName("main")!!.resources.srcDirs(
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/JarJarDependenciesTask.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/ShadowDependenciesTask.kt
similarity index 84%
rename from inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/JarJarDependenciesTask.kt
rename to inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/ShadowDependenciesTask.kt
index 33b6ca2b..14893cb 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/JarJarDependenciesTask.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/ShadowDependenciesTask.kt
@@ -17,7 +17,7 @@
 package androidx.inspection.gradle
 
 import com.android.build.gradle.api.BaseVariant
-import org.anarres.gradle.plugin.jarjar.JarjarTask
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
 import org.gradle.api.Project
 import org.gradle.api.tasks.Copy
 import org.gradle.api.tasks.TaskProvider
@@ -27,29 +27,29 @@
 
 // variant.taskName relies on @ExperimentalStdlibApi api
 @ExperimentalStdlibApi
-fun Project.registerJarJarDependenciesTask(
+fun Project.registerShadowDependenciesTask(
     variant: BaseVariant,
     zipTask: TaskProvider<Copy>
-): TaskProvider<JarjarTask> {
+): TaskProvider<ShadowJar> {
     val uberJar = registerUberJarTask(variant)
     return tasks.register(
-        variant.taskName("jarJarDependencies"),
-        JarjarTask::class.java
+        variant.taskName("shadowDependencies"),
+        ShadowJar::class.java
     ) {
         it.dependsOn(uberJar)
         val fileTree = project.fileTree(zipTask.get().destinationDir)
         fileTree.include("**/*.jar")
         it.from(fileTree)
-        it.destinationDir = taskWorkingDir(variant, "jarJar")
-        it.destinationName = "${project.name}-shadowed.jar"
+        it.destinationDirectory.set(taskWorkingDir(variant, "shadowedJar"))
+        it.archiveBaseName.set("${project.name}-shadowed")
         it.dependsOn(zipTask)
         val prefix = "deps.${project.name.replace('-', '.')}"
         it.doFirst {
-            val task = it as JarjarTask
+            val task = it as ShadowJar
             val input = uberJar.get().outputs.files
             task.from(input)
             input.extractPackageNames().forEach { packageName ->
-                task.classRename("$packageName.**", "$prefix.$packageName.@1")
+                task.relocate(packageName, "$prefix.$packageName")
             }
         }
     }
diff --git a/jetifier/jetifier/migration.config b/jetifier/jetifier/migration.config
index 6325057..0c7337e 100644
--- a/jetifier/jetifier/migration.config
+++ b/jetifier/jetifier/migration.config
@@ -2319,6 +2319,18 @@
     {
       "from": {
         "groupId": "androidx.wear",
+        "artifactId": "wear-remote-interactions",
+        "version": "{newSlVersion}"
+      },
+      "to": {
+        "groupId": "androidx.wear",
+        "artifactId": "wear-remote-interactions",
+        "version": "{newSlVersion}"
+      }
+    },
+    {
+      "from": {
+        "groupId": "androidx.wear",
         "artifactId": "wear-watchface",
         "version": "{newSlVersion}"
       },
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteInZipTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteInZipTest.kt
index df0666f..71dafe2 100644
--- a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteInZipTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteInZipTest.kt
@@ -64,9 +64,9 @@
 
         val inputFile = File(javaClass.getResource(inputZipPath).file)
 
-        @Suppress("DEPRECATION")
+        @Suppress("DEPRECATION") // b/174695914
         val tempDir = createTempDir()
-        @Suppress("DEPRECATION")
+        @Suppress("DEPRECATION") // b/174695914
         val expectedFile = File(createTempDir(), "test.zip")
 
         @Suppress("deprecation")
@@ -103,9 +103,9 @@
 
         val inputFile = File(javaClass.getResource(inputZipPath).file)
 
-        @Suppress("DEPRECATION")
+        @Suppress("DEPRECATION") // b/174695914
         val tempDir = createTempDir()
-        @Suppress("DEPRECATION")
+        @Suppress("DEPRECATION") // b/174695914
         val expectedFile = File(createTempDir(), "test.zip")
 
         @Suppress("deprecation")
diff --git a/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/Main.kt b/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/Main.kt
index 9e7ec3d..af71151 100644
--- a/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/Main.kt
+++ b/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/Main.kt
@@ -166,7 +166,7 @@
 
         val fileMappings = mutableSetOf<FileMapping>()
         if (rebuildTopOfTree) {
-            @Suppress("DEPRECATION")
+            @Suppress("DEPRECATION") // b/174695914
             val tempFile = createTempFile(suffix = "zip")
             fileMappings.add(FileMapping(input, tempFile))
         } else {
diff --git a/leanback/leanback-paging/build.gradle b/leanback/leanback-paging/build.gradle
index 7a7e046..63486ac 100644
--- a/leanback/leanback-paging/build.gradle
+++ b/leanback/leanback-paging/build.gradle
@@ -12,8 +12,8 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api(project(":leanback:leanback"))
-    api("androidx.paging:paging-runtime:3.0.0-alpha09")
+    api("androidx.leanback:leanback:1.1.0-beta01")
+    api(project(":paging:paging-runtime"))
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
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 85e2137..7e4b192 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
@@ -19,14 +19,12 @@
 import androidx.paging.CombinedLoadStates
 import androidx.paging.ExperimentalPagingApi
 import androidx.paging.LoadState
-import androidx.paging.LoadType
 import androidx.paging.Pager
 import androidx.paging.PagingConfig
 import androidx.paging.PagingData
 import androidx.paging.TestPagingSource
 import androidx.paging.assertEvents
 import androidx.paging.localLoadStatesOf
-import androidx.paging.toCombinedLoadStatesLocal
 import androidx.recyclerview.widget.DiffUtil
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -139,15 +137,18 @@
         // empty previous list.
         assertEvents(
             listOf(
-                LoadType.REFRESH to LoadState.Loading,
-                LoadType.REFRESH to LoadState.NotLoading(endOfPaginationReached = false)
-            ).toCombinedLoadStatesLocal(),
+                localLoadStatesOf(),
+                localLoadStatesOf(refreshLocal = LoadState.Loading),
+                localLoadStatesOf(
+                    refreshLocal = LoadState.NotLoading(endOfPaginationReached = false)
+                ),
+            ),
             loadEvents
         )
         loadEvents.clear()
         job.cancel()
 
-        pagingDataAdapter.submitData(TestLifecycleOwner().lifecycle, PagingData.empty<Int>())
+        pagingDataAdapter.submitData(TestLifecycleOwner().lifecycle, PagingData.empty())
         advanceUntilIdle()
         // Assert that all load state updates are sent, even when differ enters fast path for
         // empty next list.
diff --git a/leanback/leanback-preference/build.gradle b/leanback/leanback-preference/build.gradle
index caedc44..81faf4c 100644
--- a/leanback/leanback-preference/build.gradle
+++ b/leanback/leanback-preference/build.gradle
@@ -13,7 +13,7 @@
     api("androidx.appcompat:appcompat:1.0.0")
     api("androidx.recyclerview:recyclerview:1.0.0")
     api("androidx.preference:preference:1.1.0")
-    api(project(":leanback:leanback"))
+    api("androidx.leanback:leanback:1.1.0-beta01")
 }
 
 android {
diff --git a/leanback/leanback-tab/build.gradle b/leanback/leanback-tab/build.gradle
index d58b65a..4481a37 100644
--- a/leanback/leanback-tab/build.gradle
+++ b/leanback/leanback-tab/build.gradle
@@ -46,7 +46,7 @@
 androidx {
     name = "AndroidX Leanback Tab"
     publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.LEANBACK
+    mavenVersion = LibraryVersions.LEANBACK_TAB
     mavenGroup = LibraryGroups.LEANBACK
     inceptionYear = "2020"
     description = "This library adds top tab navigation component to be used in TV"
diff --git a/leanback/leanback-tab/src/androidTest/java/androidx/leanback/tab/app/PagerAdapter.java b/leanback/leanback-tab/src/androidTest/java/androidx/leanback/tab/app/PagerAdapter.java
index 6f6634b..90ce54e 100644
--- a/leanback/leanback-tab/src/androidTest/java/androidx/leanback/tab/app/PagerAdapter.java
+++ b/leanback/leanback-tab/src/androidTest/java/androidx/leanback/tab/app/PagerAdapter.java
@@ -20,6 +20,7 @@
 import androidx.fragment.app.FragmentManager;
 import androidx.fragment.app.FragmentStatePagerAdapter;
 
+@SuppressWarnings("deprecation")
 public class PagerAdapter extends FragmentStatePagerAdapter {
 
     int mNumOfTabs;
diff --git a/leanback/leanback/api/1.1.0-beta01.txt b/leanback/leanback/api/1.1.0-beta01.txt
index 808452b..d92b77a 100644
--- a/leanback/leanback/api/1.1.0-beta01.txt
+++ b/leanback/leanback/api/1.1.0-beta01.txt
@@ -883,6 +883,7 @@
     method @Deprecated public void onCreate(android.os.Bundle!);
     method @Deprecated public android.view.View! onCreateView(android.view.LayoutInflater!, android.view.ViewGroup!, android.os.Bundle!);
     method @Deprecated public void onDestroy();
+    method @Deprecated public void onDestroyView();
     method @Deprecated public void onPause();
     method @Deprecated public void onRequestPermissionsResult(int, String![]!, int[]!);
     method @Deprecated public void onResume();
diff --git a/leanback/leanback/api/current.txt b/leanback/leanback/api/current.txt
index 808452b..d92b77a 100644
--- a/leanback/leanback/api/current.txt
+++ b/leanback/leanback/api/current.txt
@@ -883,6 +883,7 @@
     method @Deprecated public void onCreate(android.os.Bundle!);
     method @Deprecated public android.view.View! onCreateView(android.view.LayoutInflater!, android.view.ViewGroup!, android.os.Bundle!);
     method @Deprecated public void onDestroy();
+    method @Deprecated public void onDestroyView();
     method @Deprecated public void onPause();
     method @Deprecated public void onRequestPermissionsResult(int, String![]!, int[]!);
     method @Deprecated public void onResume();
diff --git a/leanback/leanback/api/public_plus_experimental_1.1.0-beta01.txt b/leanback/leanback/api/public_plus_experimental_1.1.0-beta01.txt
index 808452b..d92b77a 100644
--- a/leanback/leanback/api/public_plus_experimental_1.1.0-beta01.txt
+++ b/leanback/leanback/api/public_plus_experimental_1.1.0-beta01.txt
@@ -883,6 +883,7 @@
     method @Deprecated public void onCreate(android.os.Bundle!);
     method @Deprecated public android.view.View! onCreateView(android.view.LayoutInflater!, android.view.ViewGroup!, android.os.Bundle!);
     method @Deprecated public void onDestroy();
+    method @Deprecated public void onDestroyView();
     method @Deprecated public void onPause();
     method @Deprecated public void onRequestPermissionsResult(int, String![]!, int[]!);
     method @Deprecated public void onResume();
diff --git a/leanback/leanback/api/public_plus_experimental_current.txt b/leanback/leanback/api/public_plus_experimental_current.txt
index 808452b..d92b77a 100644
--- a/leanback/leanback/api/public_plus_experimental_current.txt
+++ b/leanback/leanback/api/public_plus_experimental_current.txt
@@ -883,6 +883,7 @@
     method @Deprecated public void onCreate(android.os.Bundle!);
     method @Deprecated public android.view.View! onCreateView(android.view.LayoutInflater!, android.view.ViewGroup!, android.os.Bundle!);
     method @Deprecated public void onDestroy();
+    method @Deprecated public void onDestroyView();
     method @Deprecated public void onPause();
     method @Deprecated public void onRequestPermissionsResult(int, String![]!, int[]!);
     method @Deprecated public void onResume();
diff --git a/leanback/leanback/api/restricted_1.1.0-beta01.txt b/leanback/leanback/api/restricted_1.1.0-beta01.txt
index 981b38c..65d5941 100644
--- a/leanback/leanback/api/restricted_1.1.0-beta01.txt
+++ b/leanback/leanback/api/restricted_1.1.0-beta01.txt
@@ -924,6 +924,7 @@
     method @Deprecated public void onCreate(android.os.Bundle!);
     method @Deprecated public android.view.View! onCreateView(android.view.LayoutInflater!, android.view.ViewGroup!, android.os.Bundle!);
     method @Deprecated public void onDestroy();
+    method @Deprecated public void onDestroyView();
     method @Deprecated public void onPause();
     method @Deprecated public void onRequestPermissionsResult(int, String![]!, int[]!);
     method @Deprecated public void onResume();
diff --git a/leanback/leanback/api/restricted_current.txt b/leanback/leanback/api/restricted_current.txt
index 981b38c..65d5941 100644
--- a/leanback/leanback/api/restricted_current.txt
+++ b/leanback/leanback/api/restricted_current.txt
@@ -924,6 +924,7 @@
     method @Deprecated public void onCreate(android.os.Bundle!);
     method @Deprecated public android.view.View! onCreateView(android.view.LayoutInflater!, android.view.ViewGroup!, android.os.Bundle!);
     method @Deprecated public void onDestroy();
+    method @Deprecated public void onDestroyView();
     method @Deprecated public void onPause();
     method @Deprecated public void onRequestPermissionsResult(int, String![]!, int[]!);
     method @Deprecated public void onResume();
diff --git a/leanback/leanback/build.gradle b/leanback/leanback/build.gradle
index df6b059f..3cbeb52 100644
--- a/leanback/leanback/build.gradle
+++ b/leanback/leanback/build.gradle
@@ -15,9 +15,10 @@
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.media:media:1.0.0")
     api("androidx.fragment:fragment:1.0.0")
-    api project(":recyclerview:recyclerview")
+    api("androidx.recyclerview:recyclerview:1.2.0-beta01")
     api("androidx.appcompat:appcompat:1.0.0")
 
+    androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
diff --git a/leanback/leanback/src/androidTest/AndroidManifest.xml b/leanback/leanback/src/androidTest/AndroidManifest.xml
index 41194c1..2f345f9 100644
--- a/leanback/leanback/src/androidTest/AndroidManifest.xml
+++ b/leanback/leanback/src/androidTest/AndroidManifest.xml
@@ -16,6 +16,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="androidx.leanback.test">
 
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
     <application
         android:supportsRtl="true">
         <activity android:name="androidx.leanback.widget.GridActivity"
diff --git a/leanback/leanback/src/androidTest/generatev4.py b/leanback/leanback/src/androidTest/generatev4.py
index c540b2a..d05788b 100755
--- a/leanback/leanback/src/androidTest/generatev4.py
+++ b/leanback/leanback/src/androidTest/generatev4.py
@@ -75,7 +75,8 @@
 
 ####### generate XXXFragmentTest classes #######
 
-testcls = ['Browse', 'GuidedStep', 'VerticalGrid', 'Playback', 'Video', 'Details', 'Rows', 'Headers']
+testcls = ['Browse', 'GuidedStep', 'VerticalGrid', 'Playback', 'Video', 'Details', 'Rows',
+'Headers', 'Search']
 
 for w in testcls:
     print "copy {}SupporFragmentTest to {}FragmentTest".format(w, w)
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseFragmentTest.java
index 058c193..02e6d11 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseFragmentTest.java
@@ -206,8 +206,8 @@
 
     public static class MyFragment extends Fragment implements
             BrowseFragment.MainFragmentAdapterProvider {
-        BrowseFragment.MainFragmentAdapter mMainFragmentAdapter = new BrowseFragment
-                .MainFragmentAdapter(this);
+        BrowseFragment.MainFragmentAdapter<MyFragment> mMainFragmentAdapter =
+                new BrowseFragment.MainFragmentAdapter<>(this);
 
         @Nullable
         @Override
@@ -217,7 +217,7 @@
         }
 
         @Override
-        public BrowseFragment.MainFragmentAdapter getMainFragmentAdapter() {
+        public BrowseFragment.MainFragmentAdapter<MyFragment> getMainFragmentAdapter() {
             return mMainFragmentAdapter;
         }
     }
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseSupportFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseSupportFragmentTest.java
index 1a36149..fb87cc6 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseSupportFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/BrowseSupportFragmentTest.java
@@ -203,8 +203,8 @@
 
     public static class MyFragment extends Fragment implements
             BrowseSupportFragment.MainFragmentAdapterProvider {
-        BrowseSupportFragment.MainFragmentAdapter mMainFragmentAdapter = new BrowseSupportFragment
-                .MainFragmentAdapter(this);
+        BrowseSupportFragment.MainFragmentAdapter<MyFragment> mMainFragmentAdapter =
+                new BrowseSupportFragment.MainFragmentAdapter<>(this);
 
         @Nullable
         @Override
@@ -214,7 +214,7 @@
         }
 
         @Override
-        public BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter() {
+        public BrowseSupportFragment.MainFragmentAdapter<MyFragment> getMainFragmentAdapter() {
             return mMainFragmentAdapter;
         }
     }
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/GuidedStepFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/GuidedStepFragmentTest.java
index 747c63f..a6e6a24 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/GuidedStepFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/GuidedStepFragmentTest.java
@@ -54,6 +54,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+@SuppressWarnings("unchecked")
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class GuidedStepFragmentTest extends GuidedStepFragmentTestBase {
@@ -133,8 +134,6 @@
 
         sendKey(KeyEvent.KEYCODE_BACK);
         PollingCheck.waitFor(new PollingCheck.ActivityStop(activity));
-        verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
-        assertTrue(activity.isDestroyed());
     }
 
     @Test
@@ -371,7 +370,6 @@
 
         sendKey(KeyEvent.KEYCODE_BACK);
         PollingCheck.waitFor(new PollingCheck.ActivityStop(activity));
-        verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
     }
 
     @Test
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/GuidedStepSupportFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/GuidedStepSupportFragmentTest.java
index eef8c0a..97ff132 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/GuidedStepSupportFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/GuidedStepSupportFragmentTest.java
@@ -51,6 +51,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+@SuppressWarnings("unchecked")
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class GuidedStepSupportFragmentTest extends GuidedStepSupportFragmentTestBase {
@@ -130,8 +131,6 @@
 
         sendKey(KeyEvent.KEYCODE_BACK);
         PollingCheck.waitFor(new PollingCheck.ActivityStop(activity));
-        verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
-        assertTrue(activity.isDestroyed());
     }
 
     @Test
@@ -368,7 +367,6 @@
 
         sendKey(KeyEvent.KEYCODE_BACK);
         PollingCheck.waitFor(new PollingCheck.ActivityStop(activity));
-        verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
     }
 
     @Test
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/RowsFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/RowsFragmentTest.java
index 0bd7956..cecbfc8 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/RowsFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/RowsFragmentTest.java
@@ -400,7 +400,7 @@
     }
 
     static class StableIdAdapter extends ObjectAdapter {
-        ArrayList<Integer> mList = new ArrayList();
+        ArrayList<Integer> mList = new ArrayList<>();
 
         @Override
         public long getId(int position) {
@@ -526,8 +526,8 @@
 
     public static class F_Base extends BrowseFragment {
 
-        List<Long> mEntranceTransitionStartTS = new ArrayList();
-        List<Long> mEntranceTransitionEndTS = new ArrayList();
+        List<Long> mEntranceTransitionStartTS = new ArrayList<>();
+        List<Long> mEntranceTransitionEndTS = new ArrayList<>();
 
         @Override
         protected void onEntranceTransitionStart() {
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/RowsSupportFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/RowsSupportFragmentTest.java
index fae7d87..cfaf5c2 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/RowsSupportFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/RowsSupportFragmentTest.java
@@ -397,7 +397,7 @@
     }
 
     static class StableIdAdapter extends ObjectAdapter {
-        ArrayList<Integer> mList = new ArrayList();
+        ArrayList<Integer> mList = new ArrayList<>();
 
         @Override
         public long getId(int position) {
@@ -523,8 +523,8 @@
 
     public static class F_Base extends BrowseSupportFragment {
 
-        List<Long> mEntranceTransitionStartTS = new ArrayList();
-        List<Long> mEntranceTransitionEndTS = new ArrayList();
+        List<Long> mEntranceTransitionStartTS = new ArrayList<>();
+        List<Long> mEntranceTransitionEndTS = new ArrayList<>();
 
         @Override
         protected void onEntranceTransitionStart() {
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchFragmentTest.java
new file mode 100644
index 0000000..cd4bca8
--- /dev/null
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchFragmentTest.java
@@ -0,0 +1,279 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from SearchSupportFragmentTest.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 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.leanback.app;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.speech.SpeechRecognizer;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import android.app.Fragment;
+import androidx.leanback.test.R;
+import androidx.leanback.testutils.LeakDetector;
+import androidx.leanback.testutils.PollingCheck;
+import androidx.leanback.widget.ArrayObjectAdapter;
+import androidx.leanback.widget.HeaderItem;
+import androidx.leanback.widget.ListRow;
+import androidx.leanback.widget.ListRowPresenter;
+import androidx.leanback.widget.ObjectAdapter;
+import androidx.leanback.widget.VerticalGridView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.testutils.AnimationTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+
+@LargeTest
+@AnimationTest
+@RunWith(AndroidJUnit4.class)
+public class SearchFragmentTest extends SingleFragmentTestBase {
+
+    static final StringPresenter CARD_PRESENTER = new StringPresenter();
+
+    static void loadData(ArrayObjectAdapter adapter, int numRows, int repeatPerRow) {
+        for (int i = 0; i < numRows; ++i) {
+            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(CARD_PRESENTER);
+            int index = 0;
+            for (int j = 0; j < repeatPerRow; ++j) {
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("This is a test-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("GuidedStepFragment-" + (index++));
+            }
+            HeaderItem header = new HeaderItem(i, "Row " + i);
+            adapter.add(new ListRow(header, listRowAdapter));
+        }
+    }
+
+    public static final class F_LeakFragment extends SearchFragment
+            implements SearchFragment.SearchResultProvider {
+        ArrayObjectAdapter mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            loadData(mRowsAdapter, 10, 1);
+        }
+
+        @Override
+        public ObjectAdapter getResultsAdapter() {
+            return mRowsAdapter;
+        }
+
+        @Override
+        public boolean onQueryTextChange(String newQuery) {
+            return true;
+        }
+
+        @Override
+        public boolean onQueryTextSubmit(String query) {
+            return true;
+        }
+    }
+
+    public static final class EmptyFragment extends Fragment {
+        EditText mEditText;
+
+        @Override
+        public View onCreateView(
+                final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+            return mEditText = new EditText(container.getContext());
+        }
+
+        @Override
+        public void onStart() {
+            super.onStart();
+            // focus IME on the new fragment because there is a memory leak that IME remembers
+            // last editable view, which will cause a false reporting of leaking View.
+            InputMethodManager imm =
+                    (InputMethodManager) getActivity()
+                            .getSystemService(Context.INPUT_METHOD_SERVICE);
+            mEditText.requestFocus();
+            imm.showSoftInput(mEditText, 0);
+        }
+
+        @Override
+        public void onDestroyView() {
+            mEditText = null;
+            super.onDestroyView();
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP) // API 17 retains local Variable
+    @Test
+    public void viewLeakTest() throws Throwable {
+        SingleFragmentTestActivity activity = launchAndWaitActivity(F_LeakFragment.class,
+                1000);
+
+        VerticalGridView gridView = ((SearchFragment) activity.getTestFragment())
+                .getRowsFragment().getVerticalGridView();
+        LeakDetector leakDetector = new LeakDetector();
+        leakDetector.observeObject(gridView);
+        leakDetector.observeObject(gridView.getRecycledViewPool());
+        for (int i = 0; i < gridView.getChildCount(); i++) {
+            leakDetector.observeObject(gridView.getChildAt(i));
+        }
+        gridView = null;
+        EmptyFragment emptyFragment = new EmptyFragment();
+        activity.getFragmentManager().beginTransaction()
+                .replace(R.id.main_frame, emptyFragment)
+                .addToBackStack("BK")
+                .commit();
+
+        PollingCheck.waitFor(1000, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return emptyFragment.isResumed();
+            }
+        });
+        leakDetector.assertNoLeak();
+    }
+
+    @Test
+    public void testFocusWithSpeechRecognizerDisabled() {
+        SingleFragmentTestActivity activity = launchAndWaitActivity(
+                SpeechRecognizerDisabledFragment.class, 1000);
+
+        assertTrue(activity.findViewById(R.id.lb_search_text_editor).hasFocus());
+
+        sendKeys(KeyEvent.KEYCODE_A);
+        sendKeys(KeyEvent.KEYCODE_ENTER);
+
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return ((SearchFragment) activity.getTestFragment())
+                        .getRowsFragment().getVerticalGridView().hasFocus();
+            }
+        });
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return activity.findViewById(R.id.lb_search_text_editor).hasFocus();
+            }
+        });
+    }
+
+    @Test
+    public void testFocusWithSpeechRecognizerEnabled() throws Exception {
+
+        // Skip the test for devices which do not have SpeechRecognizer
+        if (!SpeechRecognizer.isRecognitionAvailable(
+                InstrumentationRegistry.getInstrumentation().getContext())) {
+            return;
+        }
+
+        SingleFragmentTestActivity activity = launchAndWaitActivity(
+                SpeechRecognizerEnabledFragment.class, 1000);
+
+        assertTrue(activity.findViewById(R.id.lb_search_bar_speech_orb).hasFocus());
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+
+        assertTrue(activity.findViewById(R.id.lb_search_text_editor).hasFocus());
+
+        sendKeys(KeyEvent.KEYCODE_A);
+        sendKeys(KeyEvent.KEYCODE_ENTER);
+
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return ((SearchFragment) activity.getTestFragment())
+                        .getRowsFragment().getVerticalGridView().hasFocus();
+            }
+        });
+
+        Thread.sleep(1000);
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
+        assertTrue(activity.findViewById(R.id.lb_search_bar_speech_orb).hasFocus());
+    }
+
+    static class SearchSupportTestFragment extends SearchFragment
+            implements SearchFragment.SearchResultProvider {
+        ArrayObjectAdapter mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+        String mPreviousQuery;
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            setSearchResultProvider(this);
+        }
+
+        @Override
+        public ObjectAdapter getResultsAdapter() {
+            return mRowsAdapter;
+        }
+
+        @Override
+        public boolean onQueryTextChange(String newQuery) {
+            if (!Objects.equals(mPreviousQuery, newQuery)) {
+                mRowsAdapter.clear();
+                loadData(mRowsAdapter, 10, 1);
+                mPreviousQuery = newQuery;
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean onQueryTextSubmit(String query) {
+            if (!Objects.equals(mPreviousQuery, query)) {
+                mRowsAdapter.clear();
+                loadData(mRowsAdapter, 10, 1);
+                mPreviousQuery = query;
+                return true;
+            }
+            return false;
+        }
+    }
+
+    public static final class SpeechRecognizerDisabledFragment extends SearchSupportTestFragment {
+        @Override
+        boolean isSpeechRecognizerAvailable() {
+            return false;
+        }
+    }
+
+    public static final class SpeechRecognizerEnabledFragment extends SearchSupportTestFragment {
+        @Override
+        boolean isSpeechRecognizerAvailable() {
+            return true;
+        }
+    }
+}
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchSupportFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchSupportFragmentTest.java
new file mode 100644
index 0000000..804ffa3
--- /dev/null
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/SearchSupportFragmentTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2016 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.leanback.app;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.speech.SpeechRecognizer;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import androidx.fragment.app.Fragment;
+import androidx.leanback.test.R;
+import androidx.leanback.testutils.LeakDetector;
+import androidx.leanback.testutils.PollingCheck;
+import androidx.leanback.widget.ArrayObjectAdapter;
+import androidx.leanback.widget.HeaderItem;
+import androidx.leanback.widget.ListRow;
+import androidx.leanback.widget.ListRowPresenter;
+import androidx.leanback.widget.ObjectAdapter;
+import androidx.leanback.widget.VerticalGridView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.testutils.AnimationTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+
+@LargeTest
+@AnimationTest
+@RunWith(AndroidJUnit4.class)
+public class SearchSupportFragmentTest extends SingleSupportFragmentTestBase {
+
+    static final StringPresenter CARD_PRESENTER = new StringPresenter();
+
+    static void loadData(ArrayObjectAdapter adapter, int numRows, int repeatPerRow) {
+        for (int i = 0; i < numRows; ++i) {
+            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(CARD_PRESENTER);
+            int index = 0;
+            for (int j = 0; j < repeatPerRow; ++j) {
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("This is a test-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("GuidedStepSupportFragment-" + (index++));
+            }
+            HeaderItem header = new HeaderItem(i, "Row " + i);
+            adapter.add(new ListRow(header, listRowAdapter));
+        }
+    }
+
+    public static final class F_LeakFragment extends SearchSupportFragment
+            implements SearchSupportFragment.SearchResultProvider {
+        ArrayObjectAdapter mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            loadData(mRowsAdapter, 10, 1);
+        }
+
+        @Override
+        public ObjectAdapter getResultsAdapter() {
+            return mRowsAdapter;
+        }
+
+        @Override
+        public boolean onQueryTextChange(String newQuery) {
+            return true;
+        }
+
+        @Override
+        public boolean onQueryTextSubmit(String query) {
+            return true;
+        }
+    }
+
+    public static final class EmptyFragment extends Fragment {
+        EditText mEditText;
+
+        @Override
+        public View onCreateView(
+                final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+            return mEditText = new EditText(container.getContext());
+        }
+
+        @Override
+        public void onStart() {
+            super.onStart();
+            // focus IME on the new fragment because there is a memory leak that IME remembers
+            // last editable view, which will cause a false reporting of leaking View.
+            InputMethodManager imm =
+                    (InputMethodManager) getActivity()
+                            .getSystemService(Context.INPUT_METHOD_SERVICE);
+            mEditText.requestFocus();
+            imm.showSoftInput(mEditText, 0);
+        }
+
+        @Override
+        public void onDestroyView() {
+            mEditText = null;
+            super.onDestroyView();
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP) // API 17 retains local Variable
+    @Test
+    public void viewLeakTest() throws Throwable {
+        SingleSupportFragmentTestActivity activity = launchAndWaitActivity(F_LeakFragment.class,
+                1000);
+
+        VerticalGridView gridView = ((SearchSupportFragment) activity.getTestFragment())
+                .getRowsSupportFragment().getVerticalGridView();
+        LeakDetector leakDetector = new LeakDetector();
+        leakDetector.observeObject(gridView);
+        leakDetector.observeObject(gridView.getRecycledViewPool());
+        for (int i = 0; i < gridView.getChildCount(); i++) {
+            leakDetector.observeObject(gridView.getChildAt(i));
+        }
+        gridView = null;
+        EmptyFragment emptyFragment = new EmptyFragment();
+        activity.getSupportFragmentManager().beginTransaction()
+                .replace(R.id.main_frame, emptyFragment)
+                .addToBackStack("BK")
+                .commit();
+
+        PollingCheck.waitFor(1000, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return emptyFragment.isResumed();
+            }
+        });
+        leakDetector.assertNoLeak();
+    }
+
+    @Test
+    public void testFocusWithSpeechRecognizerDisabled() {
+        SingleSupportFragmentTestActivity activity = launchAndWaitActivity(
+                SpeechRecognizerDisabledFragment.class, 1000);
+
+        assertTrue(activity.findViewById(R.id.lb_search_text_editor).hasFocus());
+
+        sendKeys(KeyEvent.KEYCODE_A);
+        sendKeys(KeyEvent.KEYCODE_ENTER);
+
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return ((SearchSupportFragment) activity.getTestFragment())
+                        .getRowsSupportFragment().getVerticalGridView().hasFocus();
+            }
+        });
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return activity.findViewById(R.id.lb_search_text_editor).hasFocus();
+            }
+        });
+    }
+
+    @Test
+    public void testFocusWithSpeechRecognizerEnabled() throws Exception {
+
+        // Skip the test for devices which do not have SpeechRecognizer
+        if (!SpeechRecognizer.isRecognitionAvailable(
+                InstrumentationRegistry.getInstrumentation().getContext())) {
+            return;
+        }
+
+        SingleSupportFragmentTestActivity activity = launchAndWaitActivity(
+                SpeechRecognizerEnabledFragment.class, 1000);
+
+        assertTrue(activity.findViewById(R.id.lb_search_bar_speech_orb).hasFocus());
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+
+        assertTrue(activity.findViewById(R.id.lb_search_text_editor).hasFocus());
+
+        sendKeys(KeyEvent.KEYCODE_A);
+        sendKeys(KeyEvent.KEYCODE_ENTER);
+
+        PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return ((SearchSupportFragment) activity.getTestFragment())
+                        .getRowsSupportFragment().getVerticalGridView().hasFocus();
+            }
+        });
+
+        Thread.sleep(1000);
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
+        assertTrue(activity.findViewById(R.id.lb_search_bar_speech_orb).hasFocus());
+    }
+
+    static class SearchSupportTestFragment extends SearchSupportFragment
+            implements SearchSupportFragment.SearchResultProvider {
+        ArrayObjectAdapter mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+        String mPreviousQuery;
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            setSearchResultProvider(this);
+        }
+
+        @Override
+        public ObjectAdapter getResultsAdapter() {
+            return mRowsAdapter;
+        }
+
+        @Override
+        public boolean onQueryTextChange(String newQuery) {
+            if (!Objects.equals(mPreviousQuery, newQuery)) {
+                mRowsAdapter.clear();
+                loadData(mRowsAdapter, 10, 1);
+                mPreviousQuery = newQuery;
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean onQueryTextSubmit(String query) {
+            if (!Objects.equals(mPreviousQuery, query)) {
+                mRowsAdapter.clear();
+                loadData(mRowsAdapter, 10, 1);
+                mPreviousQuery = query;
+                return true;
+            }
+            return false;
+        }
+    }
+
+    public static final class SpeechRecognizerDisabledFragment extends SearchSupportTestFragment {
+        @Override
+        boolean isSpeechRecognizerAvailable() {
+            return false;
+        }
+    }
+
+    public static final class SpeechRecognizerEnabledFragment extends SearchSupportTestFragment {
+        @Override
+        boolean isSpeechRecognizerAvailable() {
+            return true;
+        }
+    }
+}
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/wizard/GuidedStepAttributesTestFragment.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/wizard/GuidedStepAttributesTestFragment.java
index fb4ed92..5dbad85 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/wizard/GuidedStepAttributesTestFragment.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/wizard/GuidedStepAttributesTestFragment.java
@@ -31,7 +31,7 @@
         }
     }
 
-    static HashMap<Long, Callback> sCallbacks = new HashMap();
+    static HashMap<Long, Callback> sCallbacks = new HashMap<>();
     public static GuidanceStylist.Guidance GUIDANCE = null;
     public static List<GuidedAction> ACTION_LIST = null;
     public static long LAST_CLICKED_ACTION_ID = -1;
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/media/PlaybackBannerControlGlueTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/media/PlaybackBannerControlGlueTest.java
index 8e7d608..a16c430 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/media/PlaybackBannerControlGlueTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/media/PlaybackBannerControlGlueTest.java
@@ -54,7 +54,7 @@
     }
 
     public static class PlaybackBannerControlGlueImpl
-            extends PlaybackBannerControlGlue {
+            extends PlaybackBannerControlGlue<PlayerAdapter> {
         public PlaybackBannerControlGlueImpl(Context context) {
             super(context, new int[] {1, 2 , 3, 4, 5}, new PlayerAdapterSample());
         }
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/media/PlaybackTransportControlGlueTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/media/PlaybackTransportControlGlueTest.java
index d3cfa19..080365e 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/media/PlaybackTransportControlGlueTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/media/PlaybackTransportControlGlueTest.java
@@ -55,6 +55,7 @@
         }
     }
 
+    @SuppressWarnings("unchecked")
     public static class PlaybackTransportControlGlueImpl
             extends PlaybackTransportControlGlue {
         public PlaybackTransportControlGlueImpl(Context context) {
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ControlBarTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ControlBarTest.java
index d72bbcc..87a16c1 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ControlBarTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ControlBarTest.java
@@ -118,7 +118,7 @@
         final TextView v3 = new Button(context);
         bar.addView(v3, 100, 100);
 
-        ArrayList<View> focusables = new ArrayList();
+        ArrayList<View> focusables = new ArrayList<>();
         bar.addFocusables(focusables, View.FOCUS_DOWN);
         assertEquals(1, focusables.size());
         assertSame(focusables.get(0), v2);
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridActivity.java b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridActivity.java
index 693546d..48a59c7 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridActivity.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridActivity.java
@@ -132,6 +132,7 @@
         return view;
     }
 
+    @SuppressWarnings("unchecked")
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         Intent intent = getIntent();
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
index 82e1323..5f7a59a 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
@@ -1168,7 +1168,7 @@
             }
         });
         waitForScrollIdle();
-        final ArrayList<RecyclerView.ViewHolder> moveViewHolders = new ArrayList();
+        final ArrayList<RecyclerView.ViewHolder> moveViewHolders = new ArrayList<>();
         for (int i = 51;; i++) {
             RecyclerView.ViewHolder vh = mGridView.findViewHolderForAdapterPosition(i);
             if (vh == null) {
@@ -1233,7 +1233,7 @@
                 mActivity.moveItem(51, 1000, true);
             }
         });
-        final ArrayList<View> moveInViewHolders = new ArrayList();
+        final ArrayList<View> moveInViewHolders = new ArrayList<>();
         waitForItemAnimationStart();
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -1297,7 +1297,7 @@
                 mActivity.moveItem(1499, 1, true);
             }
         });
-        final ArrayList<View> moveInViewHolders = new ArrayList();
+        final ArrayList<View> moveInViewHolders = new ArrayList<>();
         waitForItemAnimationStart();
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -6129,7 +6129,7 @@
 
         initActivity(intent);
 
-        final HashSet<View> moveAnimationViews = new HashSet();
+        final HashSet<View> moveAnimationViews = new HashSet<>();
         mActivity.mImportantForAccessibilityListener =
                 new GridActivity.ImportantForAccessibilityListener() {
             RecyclerView.LayoutManager mLM = mGridView.getLayoutManager();
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ItemBridgeAdapterTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ItemBridgeAdapterTest.java
index ac5ce09..2f6ed29 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ItemBridgeAdapterTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ItemBridgeAdapterTest.java
@@ -91,8 +91,8 @@
         mRecyclerView.setHasFixedSize(false); // force layout items in layout pass
     }
 
-    List populateData() {
-        List data = new ArrayList();
+    List<Integer> populateData() {
+        List<Integer> data = new ArrayList<>();
         for (int i = 0; i < 10000; i++) {
             data.add(i);
         }
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ObjectAdapterTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ObjectAdapterTest.java
index 50e78e7..43d2aaf 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ObjectAdapterTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/ObjectAdapterTest.java
@@ -46,6 +46,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+@SuppressWarnings("unchecked")
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ObjectAdapterTest {
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/PlaybackTransportRowPresenterTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/PlaybackTransportRowPresenterTest.java
index 080fd44..ab90c40 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/PlaybackTransportRowPresenterTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/PlaybackTransportRowPresenterTest.java
@@ -51,7 +51,7 @@
 public class PlaybackTransportRowPresenterTest {
 
     Context mContext;
-    PlaybackTransportControlGlue mGlue;
+    PlaybackTransportControlGlue<PlayerAdapter> mGlue;
     PlaybackGlueHostImplWithViewHolder mHost;
     PlayerAdapter mImpl;
     PlaybackTransportRowPresenter.ViewHolder mViewHolder;
@@ -68,7 +68,7 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mGlue = new PlaybackTransportControlGlue(mContext, mImpl) {
+                mGlue = new PlaybackTransportControlGlue<PlayerAdapter>(mContext, mImpl) {
                     @Override
                     protected void onCreatePrimaryActions(ArrayObjectAdapter
                             primaryActionsAdapter) {
diff --git a/leanback/leanback/src/main/java/androidx/leanback/app/SearchFragment.java b/leanback/leanback/src/main/java/androidx/leanback/app/SearchFragment.java
index 21cc610..38bcc52 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/app/SearchFragment.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/app/SearchFragment.java
@@ -240,6 +240,8 @@
         }
     };
 
+    boolean mSpeechRecognizerEnabled;
+
     @Override
     public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                            int[] grantResults) {
@@ -370,7 +372,11 @@
                 if (mRowsFragment != null && mRowsFragment.getView() != null
                         && mRowsFragment.getView().hasFocus()) {
                     if (direction == View.FOCUS_UP) {
-                        return mSearchBar.findViewById(R.id.lb_search_bar_speech_orb);
+                        if (mSpeechRecognizerEnabled) {
+                            return mSearchBar.findViewById(R.id.lb_search_bar_speech_orb);
+                        } else {
+                            return mSearchBar;
+                        }
                     }
                 } else if (mSearchBar.hasFocus() && direction == View.FOCUS_DOWN) {
                     if (mRowsFragment.getView() != null
@@ -381,6 +387,15 @@
                 return null;
             }
         });
+
+        if (!isSpeechRecognizerAvailable()) {
+            if(mSearchBar.hasFocus()) {
+                mSearchBar.findViewById(R.id.lb_search_text_editor).requestFocus();
+            }
+            mSearchBar.findViewById(R.id.lb_search_bar_speech_orb).setFocusable(false);
+        } else {
+            mSpeechRecognizerEnabled = true;
+        }
         return root;
     }
 
@@ -402,7 +417,8 @@
     public void onResume() {
         super.onResume();
         mIsPaused = false;
-        if (mSpeechRecognitionCallback == null && null == mSpeechRecognizer) {
+        if (mSpeechRecognitionCallback == null && null == mSpeechRecognizer
+                && mSpeechRecognizerEnabled) {
             mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(
                     FragmentUtil.getContext(SearchFragment.this));
             mSearchBar.setSpeechRecognizer(mSpeechRecognizer);
@@ -424,6 +440,13 @@
     }
 
     @Override
+    public void onDestroyView() {
+        mSearchBar = null;
+        mRowsFragment = null;
+        super.onDestroyView();
+    }
+
+    @Override
     public void onDestroy() {
         releaseAdapter();
         super.onDestroy();
@@ -759,6 +782,11 @@
         mSearchBar.setSearchQuery(query);
     }
 
+    boolean isSpeechRecognizerAvailable() {
+        return SpeechRecognizer.isRecognitionAvailable(
+                FragmentUtil.getContext(SearchFragment.this));
+    }
+
     static class ExternalQuery {
         String mQuery;
         boolean mSubmit;
diff --git a/leanback/leanback/src/main/java/androidx/leanback/app/SearchSupportFragment.java b/leanback/leanback/src/main/java/androidx/leanback/app/SearchSupportFragment.java
index b28c4ec..3c3a515 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/app/SearchSupportFragment.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/app/SearchSupportFragment.java
@@ -235,6 +235,8 @@
         }
     };
 
+    boolean mSpeechRecognizerEnabled;
+
     @Override
     public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                            int[] grantResults) {
@@ -365,7 +367,11 @@
                 if (mRowsSupportFragment != null && mRowsSupportFragment.getView() != null
                         && mRowsSupportFragment.getView().hasFocus()) {
                     if (direction == View.FOCUS_UP) {
-                        return mSearchBar.findViewById(R.id.lb_search_bar_speech_orb);
+                        if (mSpeechRecognizerEnabled) {
+                            return mSearchBar.findViewById(R.id.lb_search_bar_speech_orb);
+                        } else {
+                            return mSearchBar;
+                        }
                     }
                 } else if (mSearchBar.hasFocus() && direction == View.FOCUS_DOWN) {
                     if (mRowsSupportFragment.getView() != null
@@ -376,6 +382,15 @@
                 return null;
             }
         });
+
+        if (!isSpeechRecognizerAvailable()) {
+            if (mSearchBar.hasFocus()) {
+                mSearchBar.findViewById(R.id.lb_search_text_editor).requestFocus();
+            }
+            mSearchBar.findViewById(R.id.lb_search_bar_speech_orb).setFocusable(false);
+        } else {
+            mSpeechRecognizerEnabled = true;
+        }
         return root;
     }
 
@@ -397,9 +412,9 @@
     public void onResume() {
         super.onResume();
         mIsPaused = false;
-        if (mSpeechRecognitionCallback == null && null == mSpeechRecognizer) {
-            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(
-                    getContext());
+        if (mSpeechRecognitionCallback == null && null == mSpeechRecognizer
+                && mSpeechRecognizerEnabled) {
+            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(getContext());
             mSearchBar.setSpeechRecognizer(mSpeechRecognizer);
         }
         if (mPendingStartRecognitionWhenPaused) {
@@ -419,6 +434,13 @@
     }
 
     @Override
+    public void onDestroyView() {
+        mSearchBar = null;
+        mRowsSupportFragment = null;
+        super.onDestroyView();
+    }
+
+    @Override
     public void onDestroy() {
         releaseAdapter();
         super.onDestroy();
@@ -754,6 +776,10 @@
         mSearchBar.setSearchQuery(query);
     }
 
+    boolean isSpeechRecognizerAvailable() {
+        return SpeechRecognizer.isRecognitionAvailable(getContext());
+    }
+
     static class ExternalQuery {
         String mQuery;
         boolean mSubmit;
diff --git a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycling.java b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycling.java
index f5eda7f..f34e124 100644
--- a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycling.java
+++ b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycling.java
@@ -112,6 +112,7 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     @Nullable
     private static Constructor<? extends GeneratedAdapter> generatedConstructor(Class<?> klass) {
         try {
diff --git a/lifecycle/lifecycle-process/src/test/java/androidx/lifecycle/DispatcherActivityCallbackTest.java b/lifecycle/lifecycle-process/src/test/java/androidx/lifecycle/DispatcherActivityCallbackTest.java
index c723dab..50c86f1 100644
--- a/lifecycle/lifecycle-process/src/test/java/androidx/lifecycle/DispatcherActivityCallbackTest.java
+++ b/lifecycle/lifecycle-process/src/test/java/androidx/lifecycle/DispatcherActivityCallbackTest.java
@@ -24,8 +24,6 @@
 
 import android.annotation.SuppressLint;
 import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
 import android.os.Bundle;
 
 import org.junit.Test;
@@ -42,14 +40,15 @@
         checkReportFragment(callback, activity);
     }
 
+    @SuppressWarnings("deprecation")
     @SuppressLint("CommitTransaction")
     private void checkReportFragment(LifecycleDispatcher.DispatcherActivityCallback callback,
             Activity activity) {
         android.app.FragmentManager fm = mock(android.app.FragmentManager.class);
-        FragmentTransaction transaction = mock(FragmentTransaction.class);
+        android.app.FragmentTransaction transaction = mock(android.app.FragmentTransaction.class);
         when(activity.getFragmentManager()).thenReturn(fm);
         when(fm.beginTransaction()).thenReturn(transaction);
-        when(transaction.add(any(Fragment.class), anyString())).thenReturn(transaction);
+        when(transaction.add(any(android.app.Fragment.class), anyString())).thenReturn(transaction);
         callback.onActivityCreated(activity, mock(Bundle.class));
         verify(activity).getFragmentManager();
         verify(fm).beginTransaction();
diff --git a/lifecycle/lifecycle-service/build.gradle b/lifecycle/lifecycle-service/build.gradle
index 1f4f5dff..2b81a01 100644
--- a/lifecycle/lifecycle-service/build.gradle
+++ b/lifecycle/lifecycle-service/build.gradle
@@ -31,7 +31,7 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation("androidx.legacy:legacy-support-core-utils:1.0.0")
+    androidTestImplementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateHandleTest.kt b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateHandleTest.kt
index 314443b..8e3a3fb 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateHandleTest.kt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateHandleTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.lifecycle.viewmodel.savedstate
 
+import android.os.Bundle
 import androidx.annotation.MainThread
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.SavedStateHandle
@@ -169,9 +170,11 @@
     fun testKeySet() {
         val accessor = SavedStateHandle()
         accessor.set("s", "pb")
+        accessor.getLiveData<String>("no value ld")
         accessor.getLiveData<String>("ld").value = "a"
-        assertThat(accessor.keys().size).isEqualTo(2)
-        assertThat(accessor.keys()).containsExactly("s", "ld")
+        accessor.setSavedStateProvider("provider") { Bundle() }
+        assertThat(accessor.keys().size).isEqualTo(4)
+        assertThat(accessor.keys()).containsExactly("s", "ld", "provider", "no value ld")
     }
 
     @MainThread
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandle.java b/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandle.java
index 146eb52..af17aa1 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandle.java
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandle.java
@@ -32,8 +32,8 @@
 
 import java.io.Serializable;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -217,11 +217,17 @@
 
     /**
      * Returns all keys contained in this {@link SavedStateHandle}
+     * <p>
+     * Returned set contains all keys: keys used to get LiveData-s, to set SavedStateProviders and
+     * keys used in regular {@link #set(String, Object)}.
      */
     @MainThread
     @NonNull
     public Set<String> keys() {
-        return Collections.unmodifiableSet(mRegular.keySet());
+        HashSet<String> allKeys = new HashSet<>(mRegular.keySet());
+        allKeys.addAll(mSavedStateProviders.keySet());
+        allKeys.addAll(mLiveDatas.keySet());
+        return allKeys;
     }
 
     /**
diff --git a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java
index 0d5783c..7e20deb3 100644
--- a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java
+++ b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java
@@ -106,6 +106,7 @@
             }
         };
         ViewModelProvider.KeyedFactory keyed = new ViewModelProvider.KeyedFactory() {
+            @SuppressWarnings("unchecked")
             @NonNull
             @Override
             public <T extends ViewModel> T create(@NonNull String key,
diff --git a/media/media/api/current.txt b/media/media/api/current.txt
index 983f283..ff8752c 100644
--- a/media/media/api/current.txt
+++ b/media/media/api/current.txt
@@ -446,6 +446,7 @@
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
+    field public static final long ACTION_SET_PLAYBACK_SPEED = 4194304L; // 0x400000L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
     field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
     field public static final long ACTION_SET_SHUFFLE_MODE = 2097152L; // 0x200000L
diff --git a/media/media/api/public_plus_experimental_current.txt b/media/media/api/public_plus_experimental_current.txt
index 77263b5..a6eaa11 100644
--- a/media/media/api/public_plus_experimental_current.txt
+++ b/media/media/api/public_plus_experimental_current.txt
@@ -446,6 +446,7 @@
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
+    field public static final long ACTION_SET_PLAYBACK_SPEED = 4194304L; // 0x400000L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
     field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
     field public static final long ACTION_SET_SHUFFLE_MODE = 2097152L; // 0x200000L
diff --git a/media/media/api/restricted_current.txt b/media/media/api/restricted_current.txt
index b22a5f6..052b25c 100644
--- a/media/media/api/restricted_current.txt
+++ b/media/media/api/restricted_current.txt
@@ -457,6 +457,7 @@
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
+    field public static final long ACTION_SET_PLAYBACK_SPEED = 4194304L; // 0x400000L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
     field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
     field public static final long ACTION_SET_SHUFFLE_MODE = 2097152L; // 0x200000L
@@ -502,7 +503,7 @@
     field public static final int STATE_STOPPED = 1; // 0x1
   }
 
-  @LongDef(flag=true, value={android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP, android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY, android.support.v4.media.session.PlaybackStateCompat.ACTION_REWIND, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT, android.support.v4.media.session.PlaybackStateCompat.ACTION_FAST_FORWARD, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_RATING, android.support.v4.media.session.PlaybackStateCompat.ACTION_SEEK_TO, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_URI, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_URI, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_REPEAT_MODE, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_CAPTIONING_ENABLED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PlaybackStateCompat.Actions {
+  @LongDef(flag=true, value={android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP, android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY, android.support.v4.media.session.PlaybackStateCompat.ACTION_REWIND, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT, android.support.v4.media.session.PlaybackStateCompat.ACTION_FAST_FORWARD, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_RATING, android.support.v4.media.session.PlaybackStateCompat.ACTION_SEEK_TO, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_URI, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_URI, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_REPEAT_MODE, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_CAPTIONING_ENABLED, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_PLAYBACK_SPEED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PlaybackStateCompat.Actions {
   }
 
   public static final class PlaybackStateCompat.Builder {
diff --git a/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java b/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
index 40d565a..d51a003 100644
--- a/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
@@ -55,7 +55,8 @@
             ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
             ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE,
             ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI,
-            ACTION_SET_REPEAT_MODE, ACTION_SET_SHUFFLE_MODE, ACTION_SET_CAPTIONING_ENABLED})
+            ACTION_SET_REPEAT_MODE, ACTION_SET_SHUFFLE_MODE, ACTION_SET_CAPTIONING_ENABLED,
+            ACTION_SET_PLAYBACK_SPEED})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Actions {}
 
@@ -225,6 +226,13 @@
     public static final long ACTION_SET_SHUFFLE_MODE = 1 << 21;
 
     /**
+     * Indicates this session supports the set playback speed command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_SET_PLAYBACK_SPEED = 1 << 22;
+
+    /**
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX) // used by media2-session
@@ -717,6 +725,7 @@
      * <li> {@link PlaybackStateCompat#ACTION_SET_REPEAT_MODE}</li>
      * <li> {@link PlaybackStateCompat#ACTION_SET_SHUFFLE_MODE}</li>
      * <li> {@link PlaybackStateCompat#ACTION_SET_CAPTIONING_ENABLED}</li>
+     * <li> {@link PlaybackStateCompat#ACTION_SET_PLAYBACK_SPEED}</li>
      * </ul>
      */
     @Actions
@@ -1255,6 +1264,7 @@
          * <li> {@link PlaybackStateCompat#ACTION_SET_REPEAT_MODE}</li>
          * <li> {@link PlaybackStateCompat#ACTION_SET_SHUFFLE_MODE}</li>
          * <li> {@link PlaybackStateCompat#ACTION_SET_CAPTIONING_ENABLED}</li>
+         * <li> {@link PlaybackStateCompat#ACTION_SET_PLAYBACK_SPEED}</li>
          * </ul>
          *
          * @return this
diff --git a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaControllerCompatCallbackTest.java b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaControllerCompatCallbackTest.java
index b856e04..b54cc59 100644
--- a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaControllerCompatCallbackTest.java
+++ b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/MediaControllerCompatCallbackTest.java
@@ -202,6 +202,7 @@
     /**
      * Tests {@link MediaSessionCompat#setFlags}.
      */
+    @SuppressWarnings("deprecation")
     @Test
     @SmallTest
     public void testSetFlags() throws Exception {
diff --git a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/PlaybackStateCompatTest.java b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/PlaybackStateCompatTest.java
index 6cb6393..353cadc 100644
--- a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/PlaybackStateCompatTest.java
+++ b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/PlaybackStateCompatTest.java
@@ -284,6 +284,42 @@
         parcel.recycle();
     }
 
+    /**
+     * Tests that each ACTION_* constant does not overlap.
+     */
+    @Test
+    @SmallTest
+    public void testActionConstantDoesNotOverlap() {
+        long[] actionConstants = new long[] {
+                PlaybackStateCompat.ACTION_STOP, PlaybackStateCompat.ACTION_PAUSE,
+                PlaybackStateCompat.ACTION_PLAY, PlaybackStateCompat.ACTION_REWIND,
+                PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS,
+                PlaybackStateCompat.ACTION_SKIP_TO_NEXT,
+                PlaybackStateCompat.ACTION_FAST_FORWARD,
+                PlaybackStateCompat.ACTION_SET_RATING,
+                PlaybackStateCompat.ACTION_SEEK_TO,
+                PlaybackStateCompat.ACTION_PLAY_PAUSE,
+                PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID,
+                PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH,
+                PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM,
+                PlaybackStateCompat.ACTION_PLAY_FROM_URI,
+                PlaybackStateCompat.ACTION_PREPARE,
+                PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID,
+                PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH,
+                PlaybackStateCompat.ACTION_PREPARE_FROM_URI,
+                PlaybackStateCompat.ACTION_SET_REPEAT_MODE,
+                PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE,
+                PlaybackStateCompat.ACTION_SET_CAPTIONING_ENABLED,
+                PlaybackStateCompat.ACTION_SET_PLAYBACK_SPEED};
+
+        // Check that the values are not overlapped.
+        for (int i = 0; i < actionConstants.length; i++) {
+            for (int j = i + 1; j < actionConstants.length; j++) {
+                assertEquals(0, actionConstants[i] & actionConstants[j]);
+            }
+        }
+    }
+
     private void assertCustomActionEquals(PlaybackStateCompat.CustomAction action1,
             PlaybackStateCompat.CustomAction action2) {
         assertEquals(action1.getAction(), action2.getAction());
diff --git a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java
index 7a1d085..b0fdbcb 100644
--- a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java
+++ b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java
@@ -77,6 +77,7 @@
     private MediaControllerCompat mMediaController;
     private ControllerCallback mMediaControllerCallback;
 
+    @SuppressWarnings("deprecation")
     @Before
     public void setUp() throws InterruptedException {
         mServiceVersion = getArguments().getString(KEY_SERVICE_VERSION, "");
diff --git a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerTest.java b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerTest.java
index c01aeb2..1f3976f 100644
--- a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerTest.java
+++ b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerTest.java
@@ -78,6 +78,7 @@
     private MediaController mMediaController;
     private ControllerCallback mMediaControllerCallback;
 
+    @SuppressWarnings("deprecation")
     @Before
     public void setUp() throws InterruptedException {
         mServiceVersion = getArguments().getString(KEY_SERVICE_VERSION, "");
diff --git a/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java b/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
index 9953185..e53d656 100644
--- a/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
+++ b/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
@@ -175,6 +175,7 @@
     /**
      * Tests that a session can be created and that all the fields are initialized correctly.
      */
+    @SuppressWarnings("deprecation")
     @Test
     @SmallTest
     public void testCreateSession() throws Exception {
diff --git a/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/StubMediaBrowserServiceCompat.java b/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/StubMediaBrowserServiceCompat.java
index 76314be..814ac7c 100644
--- a/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/StubMediaBrowserServiceCompat.java
+++ b/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/StubMediaBrowserServiceCompat.java
@@ -81,6 +81,7 @@
     private String mExpectedCallerPackageName;
     private int mExpectedCallerUid;
 
+    @SuppressWarnings("deprecation")
     @Override
     public void onCreate() {
         super.onCreate();
@@ -420,6 +421,7 @@
             notifyCurrentControllerInfo("onRemoveQueueItem");
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         public void onRemoveQueueItemAt(int index) {
             notifyCurrentControllerInfo("onRemoveQueueItemAt");
diff --git a/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java b/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java
index 49e3478..0567306 100644
--- a/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java
+++ b/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java
@@ -78,6 +78,7 @@
     private MediaControllerCompat mMediaController;
     private ControllerCallback mMediaControllerCallback;
 
+    @SuppressWarnings("deprecation")
     @Before
     public void setUp() throws InterruptedException, RemoteException {
         mServiceVersion = getArguments().getString(KEY_SERVICE_VERSION, "");
diff --git a/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerTest.java b/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerTest.java
index 464f21e..16fe1cc 100644
--- a/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerTest.java
+++ b/media/version-compat-tests/previous/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerTest.java
@@ -78,6 +78,7 @@
     private MediaController mMediaController;
     private ControllerCallback mMediaControllerCallback;
 
+    @SuppressWarnings("deprecation")
     @Before
     public void setUp() throws InterruptedException {
         mServiceVersion = getArguments().getString(KEY_SERVICE_VERSION, "");
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaNotificationHandler.java b/media2/session/src/main/java/androidx/media2/session/MediaNotificationHandler.java
index de2971e..07df689 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaNotificationHandler.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaNotificationHandler.java
@@ -122,6 +122,34 @@
         mServiceInstance.startForeground(id, notification);
     }
 
+    /**
+     * Updates the notification when needed.
+     * This will be called when the current media item is changed.
+     */
+    @Override
+    public void onNotificationUpdateNeeded(MediaSession session) {
+        MediaSessionService.MediaNotification mediaNotification =
+                mServiceInstance.onUpdateNotification(session);
+        if (mediaNotification == null) {
+            // The service implementation doesn't want to use the automatic start/stopForeground
+            // feature.
+            return;
+        }
+
+        int id = mediaNotification.getNotificationId();
+        Notification notification = mediaNotification.getNotification();
+
+        if (Build.VERSION.SDK_INT >= 21) {
+            // Call Notification.MediaStyle#setMediaSession() indirectly.
+            android.media.session.MediaSession.Token fwkToken =
+                    (android.media.session.MediaSession.Token)
+                            session.getSessionCompat().getSessionToken().getToken();
+            notification.extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, fwkToken);
+        }
+
+        mNotificationManager.notify(id, notification);
+    }
+
     @Override
     public void onSessionClosed(MediaSession session) {
         mServiceInstance.removeSession(session);
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSession.java b/media2/session/src/main/java/androidx/media2/session/MediaSession.java
index f6447861..5ba8b5d 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSession.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSession.java
@@ -787,6 +787,12 @@
             }
         }
 
+        final void onCurrentMediaItemChanged(MediaSession session) {
+            if (mForegroundServiceEventCallback != null) {
+                mForegroundServiceEventCallback.onNotificationUpdateNeeded(session);
+            }
+        }
+
         final void onSessionClosed(MediaSession session) {
             if (mForegroundServiceEventCallback != null) {
                 mForegroundServiceEventCallback.onSessionClosed(session);
@@ -799,6 +805,7 @@
 
         abstract static class ForegroundServiceEventCallback {
             public void onPlayerStateChanged(MediaSession session, @PlayerState int state) {}
+            public void onNotificationUpdateNeeded(MediaSession session) {}
             public void onSessionClosed(MediaSession session) {}
         }
     }
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java b/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
index d1db6dd..51caf49 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
@@ -1389,6 +1389,7 @@
                 item.addOnMetadataChangedListener(session.mCallbackExecutor, this);
             }
             mMediaItem = item;
+            session.getCallback().onCurrentMediaItemChanged(session.getInstance());
 
             boolean notifyingPended = false;
             if (item != null) {
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSessionService.java b/media2/session/src/main/java/androidx/media2/session/MediaSessionService.java
index cfed886..d8a459e 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSessionService.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSessionService.java
@@ -222,7 +222,7 @@
      * notification UI.
      * <p>
      * This would be called on {@link MediaSession}'s callback executor when player state is
-     * changed.
+     * changed, or when the current media item of the session is changed.
      * <p>
      * With the notification returned here, the service becomes foreground service when the playback
      * is started. Apps targeting API {@link android.os.Build.VERSION_CODES#P} or later must request
diff --git a/media2/session/version-compat-tests/current/client/build.gradle b/media2/session/version-compat-tests/current/client/build.gradle
index a71c527..4dbf70c 100644
--- a/media2/session/version-compat-tests/current/client/build.gradle
+++ b/media2/session/version-compat-tests/current/client/build.gradle
@@ -36,3 +36,7 @@
         minSdkVersion 16
     }
 }
+
+androidx {
+    failOnDeprecationWarnings = false
+}
\ No newline at end of file
diff --git a/media2/session/version-compat-tests/current/service/build.gradle b/media2/session/version-compat-tests/current/service/build.gradle
index cebe889..8dabfb8 100644
--- a/media2/session/version-compat-tests/current/service/build.gradle
+++ b/media2/session/version-compat-tests/current/service/build.gradle
@@ -35,3 +35,7 @@
         minSdkVersion 16
     }
 }
+
+androidx {
+    failOnDeprecationWarnings = false
+}
\ No newline at end of file
diff --git a/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java b/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
index 76136c5..662fefd 100644
--- a/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
+++ b/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
@@ -348,7 +348,7 @@
     @NonNull
     public ListenableFuture<PlayerResult> setMediaItem(@NonNull MediaItem item) {
         mItem = item;
-        ArrayList list = new ArrayList<>();
+        ArrayList<MediaItem> list = new ArrayList<>();
         list.add(item);
         return setPlaylist(list, null);
     }
diff --git a/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionServiceNotificationTest.java b/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionServiceNotificationTest.java
index e0fb863..b9520b8 100644
--- a/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionServiceNotificationTest.java
+++ b/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionServiceNotificationTest.java
@@ -101,11 +101,10 @@
                 new SessionToken(mContext, MOCK_MEDIA2_SESSION_SERVICE), true, null);
 
         // Set current media item.
-        final String mediaId = "testMediaId";
         Bitmap albumArt = BitmapFactory.decodeResource(mContext.getResources(),
                 androidx.media2.test.service.R.drawable.big_buck_bunny);
         MediaMetadata metadata = new MediaMetadata.Builder()
-                .putText(METADATA_KEY_MEDIA_ID, mediaId)
+                .putText(METADATA_KEY_MEDIA_ID, "testMediaId")
                 .putText(METADATA_KEY_DISPLAY_TITLE, "Test Song Name")
                 .putText(METADATA_KEY_ARTIST, "Test Artist Name")
                 .putBitmap(METADATA_KEY_ALBUM_ART, albumArt)
@@ -122,4 +121,63 @@
         mPlayer.notifyPlayerStateChanged(SessionPlayer.PLAYER_STATE_PLAYING);
         Thread.sleep(NOTIFICATION_SHOW_TIME_MS);
     }
+
+    @Test
+    @Ignore("Comment out this line and manually run the test.")
+    public void notificationUpdatedWhenCurrentMediaItemChanged() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final MediaLibrarySessionCallback sessionCallback = new MediaLibrarySessionCallback() {
+            @Override
+            public SessionCommandGroup onConnect(@NonNull MediaSession session,
+                    @NonNull ControllerInfo controller) {
+                if (CLIENT_PACKAGE_NAME.equals(controller.getPackageName())) {
+                    mSession = session;
+                    // Change the player and playlist agent with ours.
+                    session.updatePlayer(mPlayer);
+                    latch.countDown();
+                }
+                return super.onConnect(session, controller);
+            }
+        };
+        TestServiceRegistry.getInstance().setSessionCallback(sessionCallback);
+
+        // Create a controller to start the service.
+        RemoteMediaController controller = createRemoteController(
+                new SessionToken(mContext, MOCK_MEDIA2_SESSION_SERVICE), true, null);
+
+        // Set current media item.
+        Bitmap albumArt = BitmapFactory.decodeResource(mContext.getResources(),
+                androidx.media2.test.service.R.drawable.big_buck_bunny);
+        MediaMetadata metadata = new MediaMetadata.Builder()
+                .putText(METADATA_KEY_MEDIA_ID, "testMediaId")
+                .putText(METADATA_KEY_DISPLAY_TITLE, "Test Song Name")
+                .putText(METADATA_KEY_ARTIST, "Test Artist Name")
+                .putBitmap(METADATA_KEY_ALBUM_ART, albumArt)
+                .putLong(METADATA_KEY_BROWSABLE, BROWSABLE_TYPE_NONE)
+                .putLong(METADATA_KEY_PLAYABLE, 1)
+                .build();
+        mPlayer.mCurrentMediaItem = new MediaItem.Builder()
+                .setMetadata(metadata)
+                .build();
+
+        mPlayer.notifyPlayerStateChanged(SessionPlayer.PLAYER_STATE_PLAYING);
+        // At this point, the notification should be shown.
+        Thread.sleep(NOTIFICATION_SHOW_TIME_MS);
+
+        // Set a new media item. (current media item is changed)
+        MediaMetadata newMetadata = new MediaMetadata.Builder()
+                .putText(METADATA_KEY_MEDIA_ID, "New media ID")
+                .putText(METADATA_KEY_DISPLAY_TITLE, "New Song Name")
+                .putText(METADATA_KEY_ARTIST, "New Artist Name")
+                .putLong(METADATA_KEY_BROWSABLE, BROWSABLE_TYPE_NONE)
+                .putLong(METADATA_KEY_PLAYABLE, 1)
+                .build();
+
+        MediaItem newItem = new MediaItem.Builder().setMetadata(newMetadata).build();
+        mPlayer.mCurrentMediaItem = newItem;
+
+        // Calling this should update the notification with the new metadata.
+        mPlayer.notifyCurrentMediaItemChanged(newItem);
+        Thread.sleep(NOTIFICATION_SHOW_TIME_MS);
+    }
 }
diff --git a/media2/session/version-compat-tests/previous/client/build.gradle b/media2/session/version-compat-tests/previous/client/build.gradle
index 3fd0d17..ae04edb8 100644
--- a/media2/session/version-compat-tests/previous/client/build.gradle
+++ b/media2/session/version-compat-tests/previous/client/build.gradle
@@ -36,3 +36,7 @@
         minSdkVersion 19
     }
 }
+
+androidx {
+    failOnDeprecationWarnings = false
+}
\ No newline at end of file
diff --git a/media2/session/version-compat-tests/previous/service/build.gradle b/media2/session/version-compat-tests/previous/service/build.gradle
index aa5cba0..0a1091e 100644
--- a/media2/session/version-compat-tests/previous/service/build.gradle
+++ b/media2/session/version-compat-tests/previous/service/build.gradle
@@ -35,3 +35,7 @@
         minSdkVersion 19
     }
 }
+
+androidx {
+    failOnDeprecationWarnings = false
+}
\ No newline at end of file
diff --git a/media2/session/version-compat-tests/previous/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java b/media2/session/version-compat-tests/previous/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
index 46c9dbc..cbbeb33 100644
--- a/media2/session/version-compat-tests/previous/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
+++ b/media2/session/version-compat-tests/previous/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
@@ -337,7 +337,7 @@
     @Override
     public ListenableFuture<PlayerResult> setMediaItem(MediaItem item) {
         mItem = item;
-        ArrayList list = new ArrayList<>();
+        ArrayList<MediaItem> list = new ArrayList<>();
         list.add(item);
         return setPlaylist(list, null);
     }
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
index 43d18dd..c3d0127 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
@@ -45,7 +45,6 @@
 import androidx.media2.common.SessionPlayer.TrackInfo;
 import androidx.media2.session.MediaController;
 import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.After;
 import org.junit.Before;
@@ -78,9 +77,10 @@
     private MediaControlView mMediaControlView;
     private MediaItem mFileSchemeMediaItem;
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<MediaControlViewTestActivity> mActivityRule =
-            new ActivityTestRule<>(MediaControlViewTestActivity.class);
+    public androidx.test.rule.ActivityTestRule<MediaControlViewTestActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(MediaControlViewTestActivity.class);
 
     public MediaControlView_WithPlayerTest(String playerType) {
         mPlayerType = playerType;
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithoutPlayerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithoutPlayerTest.java
index d2ec15e..8c94b8c 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithoutPlayerTest.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithoutPlayerTest.java
@@ -32,7 +32,6 @@
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -52,9 +51,10 @@
     private MediaControlViewTestActivity mActivity;
     private MediaControlView mMediaControlView;
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<MediaControlViewTestActivity> mActivityRule =
-            new ActivityTestRule<>(MediaControlViewTestActivity.class);
+    public androidx.test.rule.ActivityTestRule<MediaControlViewTestActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(MediaControlViewTestActivity.class);
 
     @Before
     public void setup() throws Throwable {
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaTestBase.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaTestBase.java
index 03ad52d..bac7e6c 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaTestBase.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaTestBase.java
@@ -32,6 +32,7 @@
     @BeforeClass
     public static void setupMainLooper() {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @SuppressWarnings("deprecation")
             @Override
             public void run() {
                 // Prepare the main looper if it hasn't.
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/TestUtils.java b/media2/widget/src/androidTest/java/androidx/media2/widget/TestUtils.java
index 3d2ba52..ffeae29 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/TestUtils.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/TestUtils.java
@@ -24,6 +24,7 @@
 import android.view.WindowManager;
 
 final class TestUtils {
+    @SuppressWarnings("deprecation")
     static void setKeepScreenOn(Activity activity) {
         if (Build.VERSION.SDK_INT >= 27) {
             activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java
index 14e9f40..5fae061 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java
@@ -51,7 +51,6 @@
 import androidx.media2.session.MediaController;
 import androidx.media2.widget.test.R;
 import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.After;
 import org.junit.Before;
@@ -82,9 +81,10 @@
     private MediaItem mMediaItem;
     private SynchronousPixelCopy mPixelCopyHelper;
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<VideoViewTestActivity> mActivityRule =
-            new ActivityTestRule<>(VideoViewTestActivity.class);
+    public androidx.test.rule.ActivityTestRule<VideoViewTestActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(VideoViewTestActivity.class);
 
     public VideoView_WithPlayerTest(String playerType) {
         mPlayerType = playerType;
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithoutPlayerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithoutPlayerTest.java
index 9bbfd04..03a18c0 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithoutPlayerTest.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithoutPlayerTest.java
@@ -22,7 +22,6 @@
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -39,9 +38,10 @@
     private Activity mActivity;
     private VideoView mVideoView;
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<VideoViewTestActivity> mActivityRule =
-            new ActivityTestRule<>(VideoViewTestActivity.class);
+    public androidx.test.rule.ActivityTestRule<VideoViewTestActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(VideoViewTestActivity.class);
 
     @Before
     public void setup() throws Throwable {
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
index 6a64d84..e0469ed 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
@@ -23,7 +23,7 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants
+import androidx.compose.material.ButtonDefaults
 import androidx.compose.material.Divider
 import androidx.compose.material.Text
 import androidx.compose.material.TextField
@@ -74,7 +74,7 @@
         Divider(color = Color.Black)
         Button(
             onClick = { navController.navigate(Uri.parse(uri + state.value)) },
-            colors = ButtonConstants.defaultButtonColors(backgroundColor = Color.LightGray),
+            colors = ButtonDefaults.buttonColors(backgroundColor = Color.LightGray),
             modifier = Modifier.fillMaxWidth()
         ) {
             Text(text = "Navigate By DeepLink")
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavPopUpToDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavPopUpToDemo.kt
index fd00b02..a195480a 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavPopUpToDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavPopUpToDemo.kt
@@ -21,7 +21,7 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants
+import androidx.compose.material.ButtonDefaults
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
@@ -53,7 +53,7 @@
         if (number < 5) {
             Button(
                 onClick = { navController.navigate("$next") },
-                colors = ButtonConstants.defaultButtonColors(backgroundColor = Color.LightGray),
+                colors = ButtonDefaults.buttonColors(backgroundColor = Color.LightGray),
                 modifier = Modifier.fillMaxWidth()
             ) {
                 Text(text = "Navigate to Screen $next")
@@ -63,7 +63,7 @@
         if (navController.previousBackStackEntry != null) {
             Button(
                 onClick = { navController.navigate("1") { popUpTo("1") { inclusive = true } } },
-                colors = ButtonConstants.defaultButtonColors(backgroundColor = Color.LightGray),
+                colors = ButtonDefaults.buttonColors(backgroundColor = Color.LightGray),
                 modifier = Modifier.fillMaxWidth()
             ) {
                 Text(text = "PopUpTo Screen 1")
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 4da4784..89c6817 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
@@ -25,7 +25,7 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material.Button
-import androidx.compose.material.ButtonConstants
+import androidx.compose.material.ButtonDefaults
 import androidx.compose.material.Divider
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
@@ -133,7 +133,7 @@
 ) {
     Button(
         onClick = listener,
-        colors = ButtonConstants.defaultButtonColors(backgroundColor = LightGray),
+        colors = ButtonDefaults.buttonColors(backgroundColor = LightGray),
         modifier = Modifier.fillMaxWidth()
     ) {
         Text(text = "Navigate to $text")
@@ -145,7 +145,7 @@
     if (navController.previousBackStackEntry != null) {
         Button(
             onClick = { navController.popBackStack() },
-            colors = ButtonConstants.defaultButtonColors(backgroundColor = LightGray),
+            colors = ButtonDefaults.buttonColors(backgroundColor = LightGray),
             modifier = Modifier.fillMaxWidth()
         ) {
             Text(text = "Go to Previous screen")
diff --git a/navigation/navigation-dynamic-features-fragment/api/current.txt b/navigation/navigation-dynamic-features-fragment/api/current.txt
index 74621d5..73124fc8 100644
--- a/navigation/navigation-dynamic-features-fragment/api/current.txt
+++ b/navigation/navigation-dynamic-features-fragment/api/current.txt
@@ -30,7 +30,15 @@
 
   public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
     ctor public DynamicNavHostFragment();
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
     method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+    field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+  }
+
+  public static final class DynamicNavHostFragment.Companion {
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
   }
 
 }
diff --git a/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_current.txt b/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_current.txt
index 74621d5..73124fc8 100644
--- a/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_current.txt
@@ -30,7 +30,15 @@
 
   public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
     ctor public DynamicNavHostFragment();
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
     method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+    field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+  }
+
+  public static final class DynamicNavHostFragment.Companion {
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
   }
 
 }
diff --git a/navigation/navigation-dynamic-features-fragment/api/restricted_current.txt b/navigation/navigation-dynamic-features-fragment/api/restricted_current.txt
index 74621d5..73124fc8 100644
--- a/navigation/navigation-dynamic-features-fragment/api/restricted_current.txt
+++ b/navigation/navigation-dynamic-features-fragment/api/restricted_current.txt
@@ -30,7 +30,15 @@
 
   public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
     ctor public DynamicNavHostFragment();
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
     method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+    field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+  }
+
+  public static final class DynamicNavHostFragment.Companion {
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
   }
 
 }
diff --git a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt
index 7c33183..ee291df 100644
--- a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt
+++ b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt
@@ -21,8 +21,10 @@
 import androidx.navigation.dynamicfeatures.createGraph
 import androidx.navigation.get
 import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import androidx.testutils.withActivity
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Rule
 import org.junit.Test
@@ -30,14 +32,17 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-class DynamicFragmentNavigatorDestinationBuilderTest {
+public class DynamicFragmentNavigatorDestinationBuilderTest {
     @Suppress("DEPRECATION")
     @get:Rule
-    val activityRule = androidx.test.rule.ActivityTestRule<TestActivity>(TestActivity::class.java)
-    private val fragmentManager get() = activityRule.activity.supportFragmentManager
+    public val rule: ActivityScenarioRule<TestActivity> = ActivityScenarioRule(
+        TestActivity::class.java
+    )
+    private val fragmentManager get() = rule.scenario.withActivity { supportFragmentManager }
 
     @UiThreadTest
-    @Test fun reified() {
+    @Test
+    public fun reified() {
         val navHostFragment = DynamicNavHostFragment()
         fragmentManager.beginTransaction()
             .add(android.R.id.content, navHostFragment)
@@ -52,7 +57,8 @@
     }
 
     @UiThreadTest
-    @Test fun moduleName() {
+    @Test
+    public fun moduleName() {
         val navHostFragment = DynamicNavHostFragment()
         fragmentManager.beginTransaction()
             .add(android.R.id.content, navHostFragment)
@@ -72,7 +78,8 @@
     }
 
     @UiThreadTest
-    @Test fun no_moduleName() {
+    @Test
+    public fun no_moduleName() {
         val navHostFragment = DynamicNavHostFragment()
         fragmentManager.beginTransaction()
             .add(android.R.id.content, navHostFragment)
@@ -94,5 +101,5 @@
 private const val MODULE_NAME = "module"
 private const val FRAGMENT_CLASS_NAME = "androidx.navigation.dynamicfeatures.fragment.TestFragment"
 
-class TestActivity : FragmentActivity()
-class TestFragment : Fragment()
+public class TestActivity : FragmentActivity()
+private class TestFragment : Fragment()
diff --git a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt
index 59e061f..e1fa686 100644
--- a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt
+++ b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt
@@ -20,9 +20,11 @@
 import androidx.fragment.app.FragmentActivity
 import androidx.navigation.dynamicfeatures.fragment.test.R
 import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.testutils.withActivity
+import com.google.android.play.core.splitinstall.SplitInstallManager
 import org.junit.Assert.assertEquals
 import org.junit.Rule
 import org.junit.Test
@@ -30,14 +32,16 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-class DynamicNavHostFragmentTest {
+public class DynamicNavHostFragmentTest {
 
     @Suppress("DEPRECATION")
     @get:Rule
-    val activityTestRule = androidx.test.rule.ActivityTestRule(NavigationActivity::class.java)
+    public val activityTestRule: ActivityScenarioRule<NavigationActivity> = ActivityScenarioRule(
+        NavigationActivity::class.java
+    )
 
     @Test
-    fun createSplitInstallManager() {
+    public fun createSplitInstallManager() {
         val fragment = TestDynamicNavHostFragment()
         with(ActivityScenario.launch(NavigationActivity::class.java)) {
             withActivity {
@@ -49,15 +53,33 @@
         }
         assertEquals(fragment.createSplitInstallManager(), fragment.createSplitInstallManager())
     }
+
+    @Test
+    public fun create_noArgs() {
+        val fragment = DynamicNavHostFragment.create(R.id.nav_host)
+        assertEquals(fragment.arguments!!.size(), 1)
+    }
+
+    @Test
+    public fun create_withArgs() {
+        val fragment = DynamicNavHostFragment.create(
+            R.id.nav_host,
+            Bundle().apply {
+                putInt("Test", 1)
+            }
+        )
+        assertEquals(fragment.arguments!!.size(), 2)
+    }
 }
 
-class NavigationActivity : FragmentActivity() {
+public class NavigationActivity : FragmentActivity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         setContentView(R.layout.dynamic_activity_layout)
         super.onCreate(savedInstanceState)
     }
 }
 
-class TestDynamicNavHostFragment : DynamicNavHostFragment() {
-    public override fun createSplitInstallManager() = super.createSplitInstallManager()
+public class TestDynamicNavHostFragment : DynamicNavHostFragment() {
+    public override fun createSplitInstallManager(): SplitInstallManager =
+        super.createSplitInstallManager()
 }
diff --git a/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragment.kt b/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragment.kt
index 35b8ac7..b062ef3 100644
--- a/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragment.kt
+++ b/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragment.kt
@@ -16,6 +16,8 @@
 
 package androidx.navigation.dynamicfeatures.fragment
 
+import android.os.Bundle
+import androidx.annotation.NavigationRes
 import androidx.navigation.NavHostController
 import androidx.navigation.dynamicfeatures.DynamicActivityNavigator
 import androidx.navigation.dynamicfeatures.DynamicGraphNavigator
@@ -69,4 +71,35 @@
      */
     protected open fun createSplitInstallManager(): SplitInstallManager =
         SplitInstallManagerFactory.create(requireContext())
+
+    /** Companion object for DynamicNavHostFragment */
+    public companion object {
+
+        /**
+         * Create a new [DynamicNavHostFragment] instance with an inflated {@link NavGraph} resource.
+         *
+         * @param graphResId Resource id of the navigation graph to inflate.
+         * @param startDestinationArgs Arguments to send to the start destination of the graph.
+         * @return A new DynamicNavHostFragment instance.
+         */
+        @JvmStatic
+        @JvmOverloads
+        public fun create(
+            @NavigationRes graphResId: Int,
+            startDestinationArgs: Bundle? = null
+        ): DynamicNavHostFragment {
+            return DynamicNavHostFragment().apply {
+                arguments = if (graphResId != 0 || startDestinationArgs != null) {
+                    Bundle().apply {
+                        if (graphResId != 0) {
+                            putInt(KEY_GRAPH_ID, graphResId)
+                        }
+                        if (startDestinationArgs != null) {
+                            putBundle(KEY_START_DESTINATION_ARGS, startDestinationArgs)
+                        }
+                    }
+                } else null
+            }
+        }
+    }
 }
diff --git a/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/ui/DefaultProgressFragment.kt b/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/ui/DefaultProgressFragment.kt
index ff2888c..5a2754d 100644
--- a/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/ui/DefaultProgressFragment.kt
+++ b/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/ui/DefaultProgressFragment.kt
@@ -51,6 +51,7 @@
     private var action: Button? = null
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
         with(view) {
             title = findViewById(R.id.progress_title)
             progressBar = findViewById(R.id.installation_progress)
diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java
index 2d0bbea..e76bed7 100644
--- a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java
+++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java
@@ -31,6 +31,7 @@
 import androidx.annotation.NavigationRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
 import androidx.fragment.app.DialogFragment;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentContainerView;
@@ -80,8 +81,12 @@
  * coupling to the navigation host.</p>
  */
 public class NavHostFragment extends Fragment implements NavHost {
-    private static final String KEY_GRAPH_ID = "android-support-nav:fragment:graphId";
-    private static final String KEY_START_DESTINATION_ARGS =
+    /** @hide */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    protected static final String KEY_GRAPH_ID = "android-support-nav:fragment:graphId";
+    /** @hide */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    protected static final String KEY_START_DESTINATION_ARGS =
             "android-support-nav:fragment:startDestinationArgs";
     private static final String KEY_NAV_CONTROLLER_STATE =
             "android-support-nav:fragment:navControllerState";
@@ -156,9 +161,9 @@
     /**
      * Create a new NavHostFragment instance with an inflated {@link NavGraph} resource.
      *
-     * @param graphResId resource id of the navigation graph to inflate
-     * @param startDestinationArgs arguments to send to the start destination of the graph
-     * @return a new NavHostFragment instance
+     * @param graphResId Resource id of the navigation graph to inflate.
+     * @param startDestinationArgs Arguments to send to the start destination of the graph.
+     * @return A new NavHostFragment instance.
      */
     @NonNull
     public static NavHostFragment create(@NavigationRes int graphResId,
diff --git a/paging/common/api/current.txt b/paging/common/api/current.txt
index d72a0d9a..c0867a3 100644
--- a/paging/common/api/current.txt
+++ b/paging/common/api/current.txt
@@ -9,10 +9,7 @@
   }
 
   public final class CombinedLoadStates {
-    ctor public CombinedLoadStates(androidx.paging.LoadStates source, androidx.paging.LoadStates? mediator);
-    method public androidx.paging.LoadStates component1();
-    method public androidx.paging.LoadStates? component2();
-    method public androidx.paging.CombinedLoadStates copy(androidx.paging.LoadStates source, androidx.paging.LoadStates? mediator);
+    ctor public CombinedLoadStates(androidx.paging.LoadState refresh, androidx.paging.LoadState prepend, androidx.paging.LoadState append, androidx.paging.LoadStates source, androidx.paging.LoadStates? mediator);
     method public androidx.paging.LoadState getAppend();
     method public androidx.paging.LoadStates? getMediator();
     method public androidx.paging.LoadState getPrepend();
@@ -265,7 +262,7 @@
   }
 
   public final class Pager<Key, Value> {
-    ctor public Pager(androidx.paging.PagingConfig config, Key? initialKey, androidx.paging.RemoteMediator<Key,Value>? remoteMediator, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+    ctor @androidx.paging.ExperimentalPagingApi public Pager(androidx.paging.PagingConfig config, Key? initialKey, androidx.paging.RemoteMediator<Key,Value>? remoteMediator, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
     ctor public Pager(androidx.paging.PagingConfig config, Key? initialKey, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
     ctor public Pager(androidx.paging.PagingConfig config, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
     method public kotlinx.coroutines.flow.Flow<androidx.paging.PagingData<Value>> getFlow();
@@ -318,8 +315,8 @@
     method public final boolean getInvalid();
     method public boolean getJumpingSupported();
     method public boolean getKeyReuseSupported();
-    method @androidx.paging.ExperimentalPagingApi public Key? getRefreshKey(androidx.paging.PagingState<Key,Value> state);
-    method public void invalidate();
+    method public Key? getRefreshKey(androidx.paging.PagingState<Key,Value> state);
+    method public final void invalidate();
     method public abstract suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
     method public final void registerInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
     method public final void unregisterInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
diff --git a/paging/common/api/public_plus_experimental_current.txt b/paging/common/api/public_plus_experimental_current.txt
index f286ae0..9641951 100644
--- a/paging/common/api/public_plus_experimental_current.txt
+++ b/paging/common/api/public_plus_experimental_current.txt
@@ -9,11 +9,7 @@
   }
 
   public final class CombinedLoadStates {
-    ctor public CombinedLoadStates(androidx.paging.LoadStates source, androidx.paging.LoadStates? mediator);
-    method public androidx.paging.LoadStates component1();
-    method public androidx.paging.LoadStates? component2();
-    method public androidx.paging.CombinedLoadStates copy(androidx.paging.LoadStates source, androidx.paging.LoadStates? mediator);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public inline void forEach(kotlin.jvm.functions.Function3<? super androidx.paging.LoadType,? super java.lang.Boolean,? super androidx.paging.LoadState,kotlin.Unit> op);
+    ctor public CombinedLoadStates(androidx.paging.LoadState refresh, androidx.paging.LoadState prepend, androidx.paging.LoadState append, androidx.paging.LoadStates source, androidx.paging.LoadStates? mediator);
     method public androidx.paging.LoadState getAppend();
     method public androidx.paging.LoadStates? getMediator();
     method public androidx.paging.LoadState getPrepend();
@@ -267,7 +263,7 @@
   }
 
   public final class Pager<Key, Value> {
-    ctor public Pager(androidx.paging.PagingConfig config, Key? initialKey, androidx.paging.RemoteMediator<Key,Value>? remoteMediator, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+    ctor @androidx.paging.ExperimentalPagingApi public Pager(androidx.paging.PagingConfig config, Key? initialKey, androidx.paging.RemoteMediator<Key,Value>? remoteMediator, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
     ctor public Pager(androidx.paging.PagingConfig config, Key? initialKey, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
     ctor public Pager(androidx.paging.PagingConfig config, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
     method public kotlinx.coroutines.flow.Flow<androidx.paging.PagingData<Value>> getFlow();
@@ -320,8 +316,8 @@
     method public final boolean getInvalid();
     method public boolean getJumpingSupported();
     method public boolean getKeyReuseSupported();
-    method @androidx.paging.ExperimentalPagingApi public Key? getRefreshKey(androidx.paging.PagingState<Key,Value> state);
-    method public void invalidate();
+    method public Key? getRefreshKey(androidx.paging.PagingState<Key,Value> state);
+    method public final void invalidate();
     method public abstract suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
     method public final void registerInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
     method public final void unregisterInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index d72a0d9a..c0867a3 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -9,10 +9,7 @@
   }
 
   public final class CombinedLoadStates {
-    ctor public CombinedLoadStates(androidx.paging.LoadStates source, androidx.paging.LoadStates? mediator);
-    method public androidx.paging.LoadStates component1();
-    method public androidx.paging.LoadStates? component2();
-    method public androidx.paging.CombinedLoadStates copy(androidx.paging.LoadStates source, androidx.paging.LoadStates? mediator);
+    ctor public CombinedLoadStates(androidx.paging.LoadState refresh, androidx.paging.LoadState prepend, androidx.paging.LoadState append, androidx.paging.LoadStates source, androidx.paging.LoadStates? mediator);
     method public androidx.paging.LoadState getAppend();
     method public androidx.paging.LoadStates? getMediator();
     method public androidx.paging.LoadState getPrepend();
@@ -265,7 +262,7 @@
   }
 
   public final class Pager<Key, Value> {
-    ctor public Pager(androidx.paging.PagingConfig config, Key? initialKey, androidx.paging.RemoteMediator<Key,Value>? remoteMediator, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+    ctor @androidx.paging.ExperimentalPagingApi public Pager(androidx.paging.PagingConfig config, Key? initialKey, androidx.paging.RemoteMediator<Key,Value>? remoteMediator, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
     ctor public Pager(androidx.paging.PagingConfig config, Key? initialKey, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
     ctor public Pager(androidx.paging.PagingConfig config, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
     method public kotlinx.coroutines.flow.Flow<androidx.paging.PagingData<Value>> getFlow();
@@ -318,8 +315,8 @@
     method public final boolean getInvalid();
     method public boolean getJumpingSupported();
     method public boolean getKeyReuseSupported();
-    method @androidx.paging.ExperimentalPagingApi public Key? getRefreshKey(androidx.paging.PagingState<Key,Value> state);
-    method public void invalidate();
+    method public Key? getRefreshKey(androidx.paging.PagingState<Key,Value> state);
+    method public final void invalidate();
     method public abstract suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
     method public final void registerInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
     method public final void unregisterInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
diff --git a/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt b/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
index 2b4bee4..de3a633 100644
--- a/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
@@ -42,7 +42,6 @@
  * An intermediate flow producer that flattens previous page events and gives any new downstream
  * just those events instead of the full history.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 internal class CachedPageEventFlow<T : Any>(
     src: Flow<PageEvent<T>>,
     scope: CoroutineScope
@@ -81,6 +80,7 @@
         multicastedSrc.close()
     }
 
+    @OptIn(ExperimentalCoroutinesApi::class)
     val downstreamFlow = channelFlow {
         // get a new snapshot. this will immediately hook us to the upstream channel
         val snapshot = pageController.createTemporaryDownstream()
@@ -141,7 +141,6 @@
      */
     private val historyChannel: Channel<IndexedValue<PageEvent<T>>> = Channel(Channel.UNLIMITED)
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     fun consumeHistory() = historyChannel.consumeAsFlow()
 
     /**
diff --git a/paging/common/src/main/kotlin/androidx/paging/CachedPagingData.kt b/paging/common/src/main/kotlin/androidx/paging/CachedPagingData.kt
index 828d8a0..c6c813a 100644
--- a/paging/common/src/main/kotlin/androidx/paging/CachedPagingData.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/CachedPagingData.kt
@@ -22,15 +22,12 @@
 import androidx.paging.ActiveFlowTracker.FlowType.PAGE_EVENT_FLOW
 import androidx.paging.multicast.Multicaster
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.scan
 
-@OptIn(ExperimentalCoroutinesApi::class)
 private class MulticastedPagingData<T : Any>(
     val scope: CoroutineScope,
     val parent: PagingData<T>,
@@ -83,22 +80,25 @@
     scope: CoroutineScope
 ) = cachedIn(scope, null)
 
-@OptIn(ExperimentalCoroutinesApi::class)
 internal fun <T : Any> Flow<PagingData<T>>.cachedIn(
     scope: CoroutineScope,
     // used in tests
     tracker: ActiveFlowTracker? = null
 ): Flow<PagingData<T>> {
+    // This variable is a replacement for lack of non-experimental `scan` operator.
+    // It holds onto the previous MulticastedPagingData to be able to close it when a new
+    // MulticastedPagingData is received.
+    var prev: MulticastedPagingData<T>? = null
     val multicastedFlow = this.map {
         MulticastedPagingData(
             scope = scope,
             parent = it
         )
-    }.scan(null as MulticastedPagingData<T>?) { prev, next ->
+    }.onEach {
         prev?.close()
-        next
-    }.mapNotNull {
-        it?.asPagingData()
+        prev = it
+    }.map {
+        it.asPagingData()
     }.onStart {
         tracker?.onStart(PAGED_DATA_FLOW)
     }.onCompletion {
@@ -125,4 +125,4 @@
         PAGED_DATA_FLOW,
         PAGE_EVENT_FLOW
     }
-}
\ No newline at end of file
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/CombinedLoadStates.kt b/paging/common/src/main/kotlin/androidx/paging/CombinedLoadStates.kt
index 386b4ba..19efffd 100644
--- a/paging/common/src/main/kotlin/androidx/paging/CombinedLoadStates.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/CombinedLoadStates.kt
@@ -16,12 +16,43 @@
 
 package androidx.paging
 
-import androidx.annotation.RestrictTo
-
 /**
  * Collection of pagination [LoadState]s for both a [PagingSource], and [RemoteMediator].
  */
-data class CombinedLoadStates(
+class CombinedLoadStates(
+    /**
+     * Convenience for combined behavior of [REFRESH][LoadType.REFRESH] [LoadState], which
+     * generally defers to [mediator] if it exists, but if previously was [LoadState.Loading],
+     * awaits for both [source] and [mediator] to become [LoadState.NotLoading] to ensure the
+     * remote load was applied.
+     *
+     * For use cases that require reacting to [LoadState] of [source] and [mediator]
+     * specifically, e.g., showing cached data when network loads via [mediator] fail,
+     * [LoadStates] exposed via [source] and [mediator] should be used directly.
+     */
+    val refresh: LoadState,
+    /**
+     * Convenience for combined behavior of [PREPEND][LoadType.REFRESH] [LoadState], which
+     * generally defers to [mediator] if it exists, but if previously was [LoadState.Loading],
+     * awaits for both [source] and [mediator] to become [LoadState.NotLoading] to ensure the
+     * remote load was applied.
+     *
+     * For use cases that require reacting to [LoadState] of [source] and [mediator]
+     * specifically, e.g., showing cached data when network loads via [mediator] fail,
+     * [LoadStates] exposed via [source] and [mediator] should be used directly.
+     */
+    val prepend: LoadState,
+    /**
+     * Convenience for combined behavior of [APPEND][LoadType.REFRESH] [LoadState], which
+     * generally defers to [mediator] if it exists, but if previously was [LoadState.Loading],
+     * awaits for both [source] and [mediator] to become [LoadState.NotLoading] to ensure the
+     * remote load was applied.
+     *
+     * For use cases that require reacting to [LoadState] of [source] and [mediator]
+     * specifically, e.g., showing cached data when network loads via [mediator] fail,
+     * [LoadStates] exposed via [source] and [mediator] should be used directly.
+     */
+    val append: LoadState,
     /**
      * [LoadStates] corresponding to loads from a [PagingSource].
      */
@@ -31,40 +62,39 @@
      * [LoadStates] corresponding to loads from a [RemoteMediator], or `null` if [RemoteMediator]
      * not present.
      */
-    val mediator: LoadStates? = null
+    val mediator: LoadStates? = null,
 ) {
-    /**
-     * Convenience for accessing [REFRESH][LoadType.REFRESH] [LoadState], which always defers to
-     * [LoadState] of [mediator] if it exists, otherwise equivalent to [LoadState] of [source].
-     *
-     * For use cases that require reacting to [LoadState] of [source] and [mediator]
-     * specifically, e.g., showing cached data when network loads via [mediator] fail,
-     * [LoadStates] exposed via [source] and [mediator] should be used directly.
-     */
-    val refresh: LoadState = (mediator ?: source).refresh
 
-    /**
-     * Convenience for accessing [PREPEND][LoadType.PREPEND] [LoadState], which always defers to
-     * [LoadState] of [mediator] if it exists, otherwise equivalent to [LoadState] of [source].
-     *
-     * For use cases that require reacting to [LoadState] of [source] and [mediator]
-     * specifically, e.g., showing cached data when network loads via [mediator] fail,
-     * [LoadStates] exposed via [source] and [mediator] should be used directly.
-     */
-    val prepend: LoadState = (mediator ?: source).prepend
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
 
-    /**
-     * Convenience for accessing [APPEND][LoadType.APPEND] [LoadState], which always defers to
-     * [LoadState] of [mediator] if it exists, otherwise equivalent to [LoadState] of [source].
-     *
-     * For use cases that require reacting to [LoadState] of [source] and [mediator]
-     * specifically, e.g., showing cached data when network loads via [mediator] fail,
-     * [LoadStates] exposed via [source] and [mediator] should be used directly.
-     */
-    val append: LoadState = (mediator ?: source).append
+        other as CombinedLoadStates
 
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    inline fun forEach(op: (LoadType, Boolean, LoadState) -> Unit) {
+        if (refresh != other.refresh) return false
+        if (prepend != other.prepend) return false
+        if (append != other.append) return false
+        if (source != other.source) return false
+        if (mediator != other.mediator) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = refresh.hashCode()
+        result = 31 * result + prepend.hashCode()
+        result = 31 * result + append.hashCode()
+        result = 31 * result + source.hashCode()
+        result = 31 * result + (mediator?.hashCode() ?: 0)
+        return result
+    }
+
+    override fun toString(): String {
+        return "CombinedLoadStates(refresh=$refresh, prepend=$prepend, append=$append, " +
+            "source=$source, mediator=$mediator)"
+    }
+
+    internal fun forEach(op: (LoadType, Boolean, LoadState) -> Unit) {
         source.forEach { type, state ->
             op(type, false, state)
         }
@@ -75,11 +105,10 @@
 
     internal companion object {
         val IDLE_SOURCE = CombinedLoadStates(
+            refresh = LoadState.NotLoading.Incomplete,
+            prepend = LoadState.NotLoading.Incomplete,
+            append = LoadState.NotLoading.Incomplete,
             source = LoadStates.IDLE
         )
-        val IDLE_MEDIATOR = CombinedLoadStates(
-            source = LoadStates.IDLE,
-            mediator = LoadStates.IDLE
-        )
     }
 }
diff --git a/paging/common/src/main/kotlin/androidx/paging/ConflatedEventBus.kt b/paging/common/src/main/kotlin/androidx/paging/ConflatedEventBus.kt
index d896993..3679a19 100644
--- a/paging/common/src/main/kotlin/androidx/paging/ConflatedEventBus.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/ConflatedEventBus.kt
@@ -16,7 +16,6 @@
 
 package androidx.paging
 
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.mapNotNull
 
@@ -25,7 +24,6 @@
  * * It allows not setting an initial value
  * * Sending duplicate values is allowed
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 internal class ConflatedEventBus<T : Any>(initialValue: T? = null) {
     private val state = MutableStateFlow(Pair(Integer.MIN_VALUE, initialValue))
 
diff --git a/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
index 055f941..e7d9382 100644
--- a/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
@@ -97,7 +97,6 @@
     @Suppress("UNCHECKED_CAST")
     override val lastKey: K?
         get() {
-            @OptIn(ExperimentalPagingApi::class)
             return (storage.getRefreshKeyInfo(config) as PagingState<K, V>?)
                 ?.let { pagingSource.getRefreshKey(it) }
                 ?: initialLastKey
diff --git a/paging/common/src/main/kotlin/androidx/paging/DataSource.kt b/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
index 26e0270..cbceeae 100644
--- a/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
@@ -233,9 +233,12 @@
         @JvmOverloads
         fun asPagingSourceFactory(
             fetchDispatcher: CoroutineDispatcher = Dispatchers.IO
-        ): () -> PagingSource<Key, Value> = {
-            LegacyPagingSource(fetchDispatcher) { create() }
-        }
+        ): () -> PagingSource<Key, Value> = SuspendingPagingSourceFactory(
+            delegate = {
+                LegacyPagingSource(fetchDispatcher, create())
+            },
+            dispatcher = fetchDispatcher
+        )
     }
 
     /**
diff --git a/paging/common/src/main/kotlin/androidx/paging/DirectDispatcher.kt b/paging/common/src/main/kotlin/androidx/paging/DirectDispatcher.kt
deleted file mode 100644
index 5d65446..0000000
--- a/paging/common/src/main/kotlin/androidx/paging/DirectDispatcher.kt
+++ /dev/null
@@ -1,29 +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.paging
-
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlin.coroutines.CoroutineContext
-
-/**
- * [CoroutineDispatcher] which immediately runs new jobs on the current thread.
- */
-internal object DirectDispatcher : CoroutineDispatcher() {
-    override fun dispatch(context: CoroutineContext, block: Runnable) {
-        block.run()
-    }
-}
\ No newline at end of file
diff --git a/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
index 5d24516..7014826 100644
--- a/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
@@ -17,6 +17,7 @@
 package androidx.paging
 
 import androidx.annotation.RestrictTo
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 
 /**
@@ -31,15 +32,17 @@
 class InitialPagedList<K : Any, V : Any>(
     pagingSource: PagingSource<K, V>,
     coroutineScope: CoroutineScope,
+    notifyDispatcher: CoroutineDispatcher,
+    backgroundDispatcher: CoroutineDispatcher,
     config: Config,
     initialLastKey: K?
 ) : ContiguousPagedList<K, V>(
-    pagingSource,
-    coroutineScope,
-    DirectDispatcher,
-    DirectDispatcher,
-    null,
-    config,
-    PagingSource.LoadResult.Page.empty(),
-    initialLastKey
+    pagingSource = pagingSource,
+    coroutineScope = coroutineScope,
+    notifyDispatcher = notifyDispatcher,
+    backgroundDispatcher = backgroundDispatcher,
+    boundaryCallback = null,
+    config = config,
+    initialPage = PagingSource.LoadResult.Page.empty(),
+    initialLastKey = initialLastKey
 )
diff --git a/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt b/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
index 43283af..c5d7845 100644
--- a/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
@@ -30,24 +30,25 @@
  * A wrapper around [DataSource] which adapts it to the [PagingSource] API.
  */
 internal class LegacyPagingSource<Key : Any, Value : Any>(
-    private val fetchDispatcher: CoroutineDispatcher = DirectDispatcher,
-    internal val dataSourceFactory: () -> DataSource<Key, Value>
+    private val fetchDispatcher: CoroutineDispatcher,
+    internal val dataSource: DataSource<Key, Value>
 ) : PagingSource<Key, Value>() {
     private var pageSize: Int = PAGE_SIZE_NOT_SET
-    // Lazily initialize because it must be created on fetchDispatcher, but PagingSourceFactory
-    // passed to Pager is a non-suspending method.
-    internal val dataSource by lazy {
-        dataSourceFactory().also { dataSource ->
-            dataSource.addInvalidatedCallback(::invalidate)
-            // LegacyPagingSource registers invalidate callback after DataSource is created, so we
-            // need to check for race condition here. If DataSource is already invalid, simply
-            // propagate invalidation manually.
-            if (dataSource.isInvalid && !invalid) {
-                dataSource.removeInvalidatedCallback(::invalidate)
-                // Note: Calling this.invalidate directly will re-evaluate dataSource's by lazy
-                // init block, since we haven't returned a value for dataSource yet.
-                super.invalidate()
-            }
+
+    init {
+        dataSource.addInvalidatedCallback(::invalidate)
+        // technically, there is a possibly race where data source might call back our invalidate.
+        // in practice, it is fine because all fields are initialized at this point.
+        registerInvalidatedCallback {
+            dataSource.removeInvalidatedCallback(::invalidate)
+            dataSource.invalidate()
+        }
+
+        // LegacyPagingSource registers invalidate callback after DataSource is created, so we
+        // need to check for race condition here. If DataSource is already invalid, simply
+        // propagate invalidation manually.
+        if (!invalid && dataSource.isInvalid) {
+            invalidate()
         }
     }
 
@@ -118,12 +119,6 @@
         }
     }
 
-    override fun invalidate() {
-        super.invalidate()
-        dataSource.invalidate()
-    }
-
-    @OptIn(ExperimentalPagingApi::class)
     @Suppress("UNCHECKED_CAST")
     override fun getRefreshKey(state: PagingState<Key, Value>): Key? {
         return when (dataSource.type) {
diff --git a/paging/common/src/main/kotlin/androidx/paging/MutableLoadStateCollection.kt b/paging/common/src/main/kotlin/androidx/paging/MutableLoadStateCollection.kt
index baaf577..31ed7eb 100644
--- a/paging/common/src/main/kotlin/androidx/paging/MutableLoadStateCollection.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/MutableLoadStateCollection.kt
@@ -16,27 +16,45 @@
 
 package androidx.paging
 
-import androidx.annotation.RestrictTo
+import androidx.paging.LoadState.Error
+import androidx.paging.LoadState.Loading
+import androidx.paging.LoadState.NotLoading
 
 /**
- * TODO: Remove this once [PageEvent.LoadStateUpdate] contained [CombinedLoadStates].
- *
- * @hide
+ * Helper to construct [CombinedLoadStates] that accounts for previous state to set the convenience
+ * properties correctly.
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class MutableLoadStateCollection {
+internal class MutableLoadStateCollection {
+    private var refresh: LoadState = NotLoading.Incomplete
+    private var prepend: LoadState = NotLoading.Incomplete
+    private var append: LoadState = NotLoading.Incomplete
     private var source: LoadStates = LoadStates.IDLE
     private var mediator: LoadStates? = null
 
-    fun snapshot() = CombinedLoadStates(source, mediator)
+    fun snapshot() = CombinedLoadStates(
+        refresh = refresh,
+        prepend = prepend,
+        append = append,
+        source = source,
+        mediator = mediator,
+    )
 
     fun set(combinedLoadStates: CombinedLoadStates) {
+        refresh = combinedLoadStates.refresh
+        prepend = combinedLoadStates.prepend
+        append = combinedLoadStates.append
         source = combinedLoadStates.source
         mediator = combinedLoadStates.mediator
     }
 
+    fun set(sourceLoadStates: LoadStates, remoteLoadStates: LoadStates?) {
+        source = sourceLoadStates
+        mediator = remoteLoadStates
+        updateHelperStates()
+    }
+
     fun set(type: LoadType, remote: Boolean, state: LoadState): Boolean {
-        return if (remote) {
+        val didChange = if (remote) {
             val lastMediator = mediator
             mediator = (mediator ?: LoadStates.IDLE).modifyState(type, state)
             mediator != lastMediator
@@ -45,12 +63,61 @@
             source = source.modifyState(type, state)
             source != lastSource
         }
+
+        updateHelperStates()
+        return didChange
     }
 
     fun get(type: LoadType, remote: Boolean): LoadState? {
         return (if (remote) mediator else source)?.get(type)
     }
 
+    private fun updateHelperStates() {
+        refresh = computeHelperState(
+            previousState = refresh,
+            sourceRefreshState = source.refresh,
+            sourceState = source.refresh,
+            remoteState = mediator?.refresh
+        )
+        prepend = computeHelperState(
+            previousState = prepend,
+            sourceRefreshState = source.refresh,
+            sourceState = source.prepend,
+            remoteState = mediator?.prepend
+        )
+        append = computeHelperState(
+            previousState = append,
+            sourceRefreshState = source.refresh,
+            sourceState = source.append,
+            remoteState = mediator?.append
+        )
+    }
+
+    /**
+     * Computes the next value for the convenience helpers in [CombinedLoadStates], which
+     * generally defers to remote state, but waits for both source and remote states to become
+     * [NotLoading] before moving to that state. This provides a reasonable default for the common
+     * use-case where you generally want to wait for both RemoteMediator to return and for the
+     * update to get applied before signaling to UI that a network fetch has "finished".
+     */
+    private fun computeHelperState(
+        previousState: LoadState,
+        sourceRefreshState: LoadState,
+        sourceState: LoadState,
+        remoteState: LoadState?
+    ): LoadState {
+        if (remoteState == null) return sourceState
+
+        return when (previousState) {
+            is Loading -> when {
+                sourceRefreshState is NotLoading && remoteState is NotLoading -> remoteState
+                remoteState is Error -> remoteState
+                else -> previousState
+            }
+            else -> remoteState
+        }
+    }
+
     internal inline fun forEach(op: (LoadType, Boolean, LoadState) -> Unit) {
         source.forEach { type, state ->
             op(type, false, state)
@@ -59,4 +126,9 @@
             op(type, true, state)
         }
     }
+
+    internal fun terminates(loadType: LoadType): Boolean {
+        return get(loadType, false)!!.endOfPaginationReached &&
+            get(loadType, true)?.endOfPaginationReached != false
+    }
 }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt b/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt
index 827e631..f44eb824 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt
@@ -39,11 +39,11 @@
     ) : PageEvent<T>() {
         init {
             require(loadType == APPEND || placeholdersBefore >= 0) {
-                "Append state defining placeholdersBefore must be > 0, but was" +
+                "Prepend insert defining placeholdersBefore must be > 0, but was" +
                     " $placeholdersBefore"
             }
             require(loadType == PREPEND || placeholdersAfter >= 0) {
-                "Prepend state defining placeholdersAfter must be > 0, but was" +
+                "Append insert defining placeholdersAfter must be > 0, but was" +
                     " $placeholdersAfter"
             }
             require(loadType != REFRESH || pages.isNotEmpty()) {
@@ -150,11 +150,14 @@
                 placeholdersBefore = 0,
                 placeholdersAfter = 0,
                 combinedLoadStates = CombinedLoadStates(
+                    refresh = LoadState.NotLoading.Incomplete,
+                    prepend = LoadState.NotLoading.Complete,
+                    append = LoadState.NotLoading.Complete,
                     source = LoadStates(
                         refresh = LoadState.NotLoading.Incomplete,
                         prepend = LoadState.NotLoading.Complete,
-                        append = LoadState.NotLoading.Complete
-                    )
+                        append = LoadState.NotLoading.Complete,
+                    ),
                 )
             )
         }
@@ -212,8 +215,7 @@
              * This prevents multiple related RV animations from happening simultaneously
              */
             internal fun canDispatchWithoutInsert(loadState: LoadState, fromMediator: Boolean) =
-                loadState is LoadState.Loading || loadState is LoadState.Error ||
-                    (loadState.endOfPaginationReached && fromMediator)
+                loadState is LoadState.Loading || loadState is LoadState.Error || fromMediator
         }
     }
 
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt b/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt
index 35a9b12..06d6d7a 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt
@@ -31,9 +31,9 @@
 import kotlinx.coroutines.flow.scan
 import kotlinx.coroutines.launch
 
-@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
+@OptIn(FlowPreview::class)
 internal class PageFetcher<Key : Any, Value : Any>(
-    private val pagingSourceFactory: () -> PagingSource<Key, Value>,
+    private val pagingSourceFactory: suspend () -> PagingSource<Key, Value>,
     private val initialKey: Key?,
     private val config: PagingConfig,
     @OptIn(ExperimentalPagingApi::class)
@@ -53,6 +53,7 @@
 
     // The object built by paging builder can maintain the scope so that on rotation we don't stop
     // the paging.
+    @OptIn(ExperimentalCoroutinesApi::class)
     val flow: Flow<PagingData<Value>> = channelFlow {
         val remoteMediatorAccessor = remoteMediator?.let {
             RemoteMediatorAccessor(this, it)
@@ -87,7 +88,6 @@
                     previousPagingState = previousGeneration.state
                 }
 
-                @OptIn(ExperimentalPagingApi::class)
                 val initialKey: Key? = previousPagingState?.let { pagingSource.getRefreshKey(it) }
                     ?: initialKey
 
@@ -110,11 +110,10 @@
             }
             .filterNotNull()
             .mapLatest { generation ->
-                val downstreamFlow = if (remoteMediatorAccessor == null) {
-                    generation.snapshot.pageEventFlow
-                } else {
-                    generation.snapshot.injectRemoteEvents(remoteMediatorAccessor)
-                }
+                val downstreamFlow = generation.snapshot
+                    .injectRemoteEvents(remoteMediatorAccessor)
+                // .mapRemoteCompleteAsTrailingInsertForSeparators()
+
                 PagingData(
                     flow = downstreamFlow,
                     receiver = PagerUiReceiver(generation.snapshot, retryEvents)
@@ -124,47 +123,79 @@
     }
 
     private fun PageFetcherSnapshot<Key, Value>.injectRemoteEvents(
-        accessor: RemoteMediatorAccessor<Key, Value>
-    ): Flow<PageEvent<Value>> = channelFlow {
-        suspend fun dispatchIfValid(type: LoadType, state: LoadState) {
-            // not loading events are sent w/ insert-drop events.
-            if (PageEvent.LoadStateUpdate.canDispatchWithoutInsert(state, fromMediator = true)) {
-                send(
-                    PageEvent.LoadStateUpdate<Value>(type, true, state)
-                )
-            } else {
-                // ignore. Some invalidation will happened and we'll send the event there instead
-            }
-        }
-        launch {
-            var prev = LoadStates.IDLE
-            accessor.state.collect {
-                if (prev.refresh != it.refresh) {
-                    dispatchIfValid(REFRESH, it.refresh)
-                }
-                if (prev.prepend != it.prepend) {
-                    dispatchIfValid(PREPEND, it.prepend)
-                }
-                if (prev.append != it.append) {
-                    dispatchIfValid(APPEND, it.append)
-                }
-                prev = it
-            }
-        }
+        accessor: RemoteMediatorAccessor<Key, Value>?
+    ): Flow<PageEvent<Value>> {
+        if (accessor == null) return pageEventFlow
 
-        this@injectRemoteEvents.pageEventFlow.collect {
-            // only insert events have combinedLoadStates.
-            if (it is PageEvent.Insert<Value>) {
-                send(
-                    it.copy(
-                        combinedLoadStates = CombinedLoadStates(
-                            it.combinedLoadStates.source,
-                            accessor.state.value
+        @OptIn(ExperimentalCoroutinesApi::class)
+        return channelFlow {
+            val loadStates = MutableLoadStateCollection()
+
+            suspend fun dispatchIfValid(type: LoadType, state: LoadState) {
+                // not loading events are sent w/ insert-drop events.
+                if (PageEvent.LoadStateUpdate.canDispatchWithoutInsert(
+                        state,
+                        fromMediator = true
+                    )
+                ) {
+                    send(
+                        PageEvent.LoadStateUpdate<Value>(
+                            loadType = type,
+                            fromMediator = true,
+                            loadState = state
                         )
                     )
-                )
-            } else {
-                send(it)
+                } else {
+                    // Wait for invalidation to set state to NotLoading via Insert to prevent any
+                    // potential for flickering.
+                }
+            }
+
+            launch {
+                var prev = LoadStates.IDLE
+                accessor.state.collect {
+                    if (prev.refresh != it.refresh) {
+                        loadStates.set(REFRESH, true, it.refresh)
+                        dispatchIfValid(REFRESH, it.refresh)
+                    }
+                    if (prev.prepend != it.prepend) {
+                        loadStates.set(PREPEND, true, it.prepend)
+                        dispatchIfValid(PREPEND, it.prepend)
+                    }
+                    if (prev.append != it.append) {
+                        loadStates.set(APPEND, true, it.append)
+                        dispatchIfValid(APPEND, it.append)
+                    }
+                    prev = it
+                }
+            }
+
+            this@injectRemoteEvents.pageEventFlow.collect { event ->
+                when (event) {
+                    is PageEvent.Insert -> {
+                        loadStates.set(
+                            sourceLoadStates = event.combinedLoadStates.source,
+                            remoteLoadStates = accessor.state.value
+                        )
+                        send(event.copy(combinedLoadStates = loadStates.snapshot()))
+                    }
+                    is PageEvent.Drop -> {
+                        loadStates.set(
+                            type = event.loadType,
+                            remote = false,
+                            state = LoadState.NotLoading.Incomplete
+                        )
+                        send(event)
+                    }
+                    is PageEvent.LoadStateUpdate -> {
+                        loadStates.set(
+                            type = event.loadType,
+                            remote = event.fromMediator,
+                            state = event.loadState
+                        )
+                        send(event)
+                    }
+                }
             }
         }
     }
@@ -177,7 +208,7 @@
         refreshEvents.send(false)
     }
 
-    private fun generateNewPagingSource(
+    private suspend fun generateNewPagingSource(
         previousPagingSource: PagingSource<Key, Value>?
     ): PagingSource<Key, Value> {
         val pagingSource = pagingSourceFactory()
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshot.kt b/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshot.kt
index 0d2dee2..020ae17 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshot.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshot.kt
@@ -303,19 +303,13 @@
                     if (result.prevKey == null) {
                         state.setSourceLoadState(
                             type = PREPEND,
-                            newState = when (remoteMediatorConnection) {
-                                null -> NotLoading.Complete
-                                else -> NotLoading.Incomplete
-                            }
+                            newState = NotLoading.Complete
                         )
                     }
                     if (result.nextKey == null) {
                         state.setSourceLoadState(
                             type = APPEND,
-                            newState = when (remoteMediatorConnection) {
-                                null -> NotLoading.Complete
-                                else -> NotLoading.Incomplete
-                            }
+                            newState = NotLoading.Complete
                         )
                     }
                 }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshotState.kt b/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshotState.kt
index 147618c..36c0421 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshotState.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshotState.kt
@@ -27,7 +27,6 @@
 import androidx.paging.PagingConfig.Companion.MAX_SIZE_UNBOUNDED
 import androidx.paging.PagingSource.LoadResult.Page
 import androidx.paging.PagingSource.LoadResult.Page.Companion.COUNT_UNDEFINED
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.consumeAsFlow
@@ -109,13 +108,11 @@
     internal var sourceLoadStates = LoadStates.IDLE
         private set
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     fun consumePrependGenerationIdAsFlow(): Flow<Int> {
         return prependGenerationIdCh.consumeAsFlow()
             .onStart { prependGenerationIdCh.offer(prependGenerationId) }
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     fun consumeAppendGenerationIdAsFlow(): Flow<Int> {
         return appendGenerationIdCh.consumeAsFlow()
             .onStart { appendGenerationIdCh.offer(appendGenerationId) }
@@ -152,24 +149,33 @@
                 placeholdersBefore = placeholdersBefore,
                 placeholdersAfter = placeholdersAfter,
                 combinedLoadStates = CombinedLoadStates(
+                    refresh = sourceLoadStates.refresh,
+                    prepend = sourceLoadStates.prepend,
+                    append = sourceLoadStates.append,
                     source = sourceLoadStates,
-                    mediator = null
+                    mediator = null,
                 )
             )
             PREPEND -> Prepend(
                 pages = pages,
                 placeholdersBefore = placeholdersBefore,
                 combinedLoadStates = CombinedLoadStates(
+                    refresh = sourceLoadStates.refresh,
+                    prepend = sourceLoadStates.prepend,
+                    append = sourceLoadStates.append,
                     source = sourceLoadStates,
-                    mediator = null
+                    mediator = null,
                 )
             )
             APPEND -> Append(
                 pages = pages,
                 placeholdersAfter = placeholdersAfter,
                 combinedLoadStates = CombinedLoadStates(
+                    refresh = sourceLoadStates.refresh,
+                    prepend = sourceLoadStates.prepend,
+                    append = sourceLoadStates.append,
                     source = sourceLoadStates,
-                    mediator = null
+                    mediator = null,
                 )
             )
         }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
index f54c929..2b22813 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
@@ -26,7 +26,6 @@
 import kotlinx.coroutines.asCoroutineDispatcher
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withContext
 import java.lang.ref.WeakReference
 import java.util.AbstractList
 import java.util.concurrent.Executor
@@ -181,9 +180,7 @@
                         config.enablePlaceholders,
                     )
                     runBlocking {
-                        val initialResult = withContext(DirectDispatcher) {
-                            pagingSource.load(params)
-                        }
+                        val initialResult = pagingSource.load(params)
                         when (initialResult) {
                             is PagingSource.LoadResult.Page -> initialResult
                             is PagingSource.LoadResult.Error -> throw initialResult.throwable
@@ -479,8 +476,11 @@
         @Suppress("DEPRECATION")
         fun build(): PagedList<Value> {
             val fetchDispatcher = fetchDispatcher ?: Dispatchers.IO
-            val pagingSource = pagingSource ?: dataSource?.let {
-                LegacyPagingSource { it }.also {
+            val pagingSource = pagingSource ?: dataSource?.let { dataSource ->
+                LegacyPagingSource(
+                    fetchDispatcher = fetchDispatcher,
+                    dataSource = dataSource
+                ).also {
                     it.setPageSize(config.pageSize)
                 }
             }
diff --git a/paging/common/src/main/kotlin/androidx/paging/Pager.kt b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
index ed3cfda..8f2483d 100644
--- a/paging/common/src/main/kotlin/androidx/paging/Pager.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
@@ -38,22 +38,41 @@
  * `androidx.paging:paging-rxjava2` artifact.
  */
 class Pager<Key : Any, Value : Any>
-@JvmOverloads constructor(
+// Experimental usage is propagated to public API via constructor argument.
+@ExperimentalPagingApi constructor(
     config: PagingConfig,
     initialKey: Key? = null,
-    @OptIn(ExperimentalPagingApi::class)
-    remoteMediator: RemoteMediator<Key, Value>? = null,
+    remoteMediator: RemoteMediator<Key, Value>?,
     pagingSourceFactory: () -> PagingSource<Key, Value>
 ) {
+    // Experimental usage is internal, so opt-in is allowed here.
+    @JvmOverloads
+    @OptIn(ExperimentalPagingApi::class)
+    constructor(
+        config: PagingConfig,
+        initialKey: Key? = null,
+        pagingSourceFactory: () -> PagingSource<Key, Value>
+    ) : this(config, initialKey, null, pagingSourceFactory)
+
     /**
      * A cold [Flow] of [PagingData], which emits new instances of [PagingData] once they become
      * invalidated by [PagingSource.invalidate] or calls to [AsyncPagingDataDiffer.refresh] or
      * [PagingDataAdapter.refresh].
      */
     val flow: Flow<PagingData<Value>> = PageFetcher(
-        pagingSourceFactory,
-        initialKey,
-        config,
-        remoteMediator
+        pagingSourceFactory = if (
+            pagingSourceFactory is SuspendingPagingSourceFactory<Key, Value>
+        ) {
+            pagingSourceFactory::create
+        } else {
+            // cannot pass it as is since it is not a suspend function. Hence, we wrap it in {}
+            // which means we are calling the original factory inside a suspend function
+            {
+                pagingSourceFactory()
+            }
+        },
+        initialKey = initialKey,
+        config = config,
+        remoteMediator = remoteMediator
     ).flow
 }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagingData.kt b/paging/common/src/main/kotlin/androidx/paging/PagingData.kt
index 2f842b7..61548a2 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagingData.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagingData.kt
@@ -143,13 +143,15 @@
                     placeholdersBefore = 0,
                     placeholdersAfter = 0,
                     combinedLoadStates = CombinedLoadStates(
+                        refresh = LoadState.NotLoading.Incomplete,
+                        prepend = LoadState.NotLoading.Complete,
+                        append = LoadState.NotLoading.Complete,
                         source = LoadStates(
                             refresh = LoadState.NotLoading.Incomplete,
                             prepend = LoadState.NotLoading.Complete,
                             append = LoadState.NotLoading.Complete
                         )
                     )
-
                 )
             ),
             receiver = NOOP_RECEIVER
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt b/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
index 90bb25e..3f93b05 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
@@ -24,7 +24,6 @@
 import androidx.paging.PagePresenter.ProcessPageEventCallback
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -103,6 +102,13 @@
     }
 
     /**
+     * @param onListPresentable Call this synchronously right before dispatching updates to signal
+     * that this [PagingDataDiffer] should now consider [newList] as the presented list for
+     * presenter-level APIs such as [snapshot] and [peek]. This should be called before notifying
+     * any callbacks that the user would expect to be synchronous with presenter updates, such as
+     * `ListUpdateCallback`, in case it's desirable to inspect presenter state within those
+     * callbacks.
+     *
      * @return Transformed result of [lastAccessedIndex] as an index of [newList] using the diff
      * result between [previousList] and [newList]. Null if [newList] or [previousList] lists are
      * empty, where it does not make sense to transform [lastAccessedIndex].
@@ -111,7 +117,8 @@
         previousList: NullPaddedList<T>,
         newList: NullPaddedList<T>,
         newCombinedLoadStates: CombinedLoadStates,
-        lastAccessedIndex: Int
+        lastAccessedIndex: Int,
+        onListPresentable: () -> Unit,
     ): Int?
 
     open fun postEvents(): Boolean = false
@@ -126,13 +133,23 @@
                     lastAccessedIndexUnfulfilled = false
 
                     val newPresenter = PagePresenter(event)
+                    var onListPresentableCalled = false
                     val transformedLastAccessedIndex = presentNewList(
                         previousList = presenter,
                         newList = newPresenter,
                         newCombinedLoadStates = event.combinedLoadStates,
-                        lastAccessedIndex = lastAccessedIndex
+                        lastAccessedIndex = lastAccessedIndex,
+                        onListPresentable = {
+                            presenter = newPresenter
+                            onListPresentableCalled = true
+                        }
                     )
-                    presenter = newPresenter
+                    check(onListPresentableCalled) {
+                        "Missing call to onListPresentable after new list was presented. If you " +
+                            "are seeing this exception, it is generally an indication of an " +
+                            "issue with Paging. Please file a bug so we can fix it at: " +
+                            "https://issuetracker.google.com/issues/new?component=413106"
+                    }
 
                     // Dispatch LoadState updates as soon as we are done diffing, but after setting
                     // presenter.
@@ -277,7 +294,6 @@
     val size: Int
         get() = presenter.size
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     private val _combinedLoadState = MutableStateFlow(combinedLoadStates.snapshot())
 
     /**
@@ -294,7 +310,6 @@
         get() = _combinedLoadState
 
     init {
-        @OptIn(ExperimentalCoroutinesApi::class)
         addLoadStateListener {
             _combinedLoadState.value = it
         }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagingSource.kt b/paging/common/src/main/kotlin/androidx/paging/PagingSource.kt
index 3f64938..4db4e26 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagingSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagingSource.kt
@@ -315,7 +315,6 @@
      * list of loaded pages is not empty. In the case where a refresh is triggered before the
      * initial load succeeds or it errors out, the initial key passed to [Pager] will be used.
      */
-    @ExperimentalPagingApi
     open fun getRefreshKey(state: PagingState<Key, Value>): Key? = null
 
     private val onInvalidatedCallbacks = CopyOnWriteArrayList<() -> Unit>()
@@ -335,9 +334,7 @@
      * This method is idempotent. i.e., If [invalidate] has already been called, subsequent calls to
      * this method should have no effect.
      */
-    open fun invalidate() {
-        // TODO(b/137971356): Investigate making this not open when able to remove
-        //  LegacyPagingSource.
+    fun invalidate() {
         if (_invalid.compareAndSet(false, true)) {
             onInvalidatedCallbacks.forEach { it.invoke() }
         }
diff --git a/paging/common/src/main/kotlin/androidx/paging/Separators.kt b/paging/common/src/main/kotlin/androidx/paging/Separators.kt
index 94ed298..632f888 100644
--- a/paging/common/src/main/kotlin/androidx/paging/Separators.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/Separators.kt
@@ -16,10 +16,13 @@
 
 package androidx.paging
 
+import androidx.paging.LoadState.NotLoading
 import androidx.paging.LoadType.APPEND
 import androidx.paging.LoadType.PREPEND
+import androidx.paging.LoadType.REFRESH
 import androidx.paging.PageEvent.Drop
 import androidx.paging.PageEvent.Insert
+import androidx.paging.PageEvent.LoadStateUpdate
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
 
@@ -165,17 +168,18 @@
     var endTerminalSeparatorDeferred = false
     var startTerminalSeparatorDeferred = false
 
+    val loadStates = MutableLoadStateCollection()
+    var placeholdersBefore = 0
+    var placeholdersAfter = 0
+
     var footerAdded = false
     var headerAdded = false
 
     @Suppress("UNCHECKED_CAST")
     suspend fun onEvent(event: PageEvent<T>): PageEvent<R> = when (event) {
         is Insert<T> -> onInsert(event)
-        is Drop -> {
-            onDrop(event) // Update pageStash state
-            event as Drop<R>
-        }
-        is PageEvent.LoadStateUpdate -> event as PageEvent<R>
+        is Drop -> onDrop(event)
+        is LoadStateUpdate -> onLoadStateUpdate(event)
     }.also {
         // validate internal state after each modification
         if (endTerminalSeparatorDeferred) {
@@ -191,16 +195,14 @@
         return this as Insert<R>
     }
 
-    fun LoadState.isTerminal(): Boolean {
-        return this is LoadState.NotLoading && endOfPaginationReached
-    }
-
     fun CombinedLoadStates.terminatesStart(): Boolean {
-        return source.prepend.isTerminal() && mediator?.prepend?.isTerminal() != false
+        return source.prepend.endOfPaginationReached &&
+            mediator?.prepend?.endOfPaginationReached != false
     }
 
     fun CombinedLoadStates.terminatesEnd(): Boolean {
-        return source.append.isTerminal() && mediator?.append?.isTerminal() != false
+        return source.append.endOfPaginationReached &&
+            mediator?.append?.endOfPaginationReached != false
     }
 
     fun <T : Any> Insert<T>.terminatesStart(): Boolean = if (loadType == APPEND) {
@@ -227,6 +229,17 @@
             "Additional append event after append state is done"
         }
 
+        // Update SeparatorState before we do any real work.
+        loadStates.set(event.combinedLoadStates)
+        // Append insert has placeholdersBefore = -1 as a placeholder value.
+        if (event.loadType != APPEND) {
+            placeholdersBefore = event.placeholdersBefore
+        }
+        // Prepend insert has placeholdersAfter = -1 as a placeholder value.
+        if (event.loadType != PREPEND) {
+            placeholdersAfter = event.placeholdersAfter
+        }
+
         if (eventEmpty) {
             if (eventTerminatesStart && eventTerminatesEnd) {
                 // if event is empty, and fully terminal, resolve single separator, and that's it
@@ -410,7 +423,16 @@
     /**
      * Process a [Drop] event to update [pageStash] stage.
      */
-    fun onDrop(event: Drop<T>) {
+    fun onDrop(event: Drop<T>): Drop<R> {
+        loadStates.set(type = event.loadType, remote = false, state = NotLoading.Incomplete)
+        if (event.loadType == PREPEND) {
+            placeholdersBefore = event.placeholdersRemaining
+            headerAdded = false
+        } else if (event.loadType == APPEND) {
+            placeholdersAfter = event.placeholdersRemaining
+            footerAdded = false
+        }
+
         if (pageStash.isEmpty()) {
             if (event.loadType == PREPEND) {
                 startTerminalSeparatorDeferred = false
@@ -424,6 +446,49 @@
         pageStash.removeAll { stash ->
             stash.originalPageOffsets.any { pageOffsetsToDrop.contains(it) }
         }
+
+        @Suppress("UNCHECKED_CAST")
+        return event as Drop<R>
+    }
+
+    suspend fun onLoadStateUpdate(event: LoadStateUpdate<T>): PageEvent<R> {
+        // Check for redundant LoadStateUpdate events to avoid unnecessary mapping to empty inserts
+        // that might cause terminal separators to get added out of place.
+        if (loadStates.get(event.loadType, event.fromMediator) == event.loadState) {
+            @Suppress("UNCHECKED_CAST")
+            return event as PageEvent<R>
+        }
+
+        loadStates.set(type = event.loadType, remote = event.fromMediator, state = event.loadState)
+
+        // Transform terminal load state updates into empty inserts for header + footer support
+        // when used with RemoteMediator. In cases where we defer adding a terminal separator,
+        // RemoteMediator can report endOfPaginationReached via LoadStateUpdate event, which
+        // isn't possible to add a separator to. Note: Adding a separate insert event also
+        // doesn't work in the case where .insertSeparators() is called multiple times on the
+        // same page event stream - we have to transform the terminating LoadStateUpdate event.
+        if (event.loadType != REFRESH && event.fromMediator &&
+            event.loadState.endOfPaginationReached
+        ) {
+            val emptyTerminalInsert: Insert<T> = if (event.loadType == PREPEND) {
+                Insert.Prepend(
+                    pages = emptyList(),
+                    placeholdersBefore = placeholdersBefore,
+                    combinedLoadStates = loadStates.snapshot(),
+                )
+            } else {
+                Insert.Append(
+                    pages = emptyList(),
+                    placeholdersAfter = placeholdersAfter,
+                    combinedLoadStates = loadStates.snapshot(),
+                )
+            }
+
+            return onInsert(emptyTerminalInsert)
+        }
+
+        @Suppress("UNCHECKED_CAST")
+        return event as PageEvent<R>
     }
 
     private fun <T : Any> transformablePageToStash(
diff --git a/paging/common/src/main/kotlin/androidx/paging/SuspendingPagingSourceFactory.kt b/paging/common/src/main/kotlin/androidx/paging/SuspendingPagingSourceFactory.kt
new file mode 100644
index 0000000..1b15542f
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/SuspendingPagingSourceFactory.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.paging
+
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+/**
+ * Utility class to convert the paging source factory to a suspend one.
+ *
+ * This is internal because it is only necessary for the legacy paging source implementation
+ * where the data source must be created on the given thread pool for API guarantees.
+ * see: b/173029013
+ * see: b/168061354
+ */
+internal class SuspendingPagingSourceFactory<Key : Any, Value : Any>(
+    private val dispatcher: CoroutineDispatcher,
+    private val delegate: () -> PagingSource<Key, Value>
+) : () -> PagingSource<Key, Value> {
+    suspend fun create(): PagingSource<Key, Value> {
+        return withContext(dispatcher) {
+            delegate()
+        }
+    }
+
+    override fun invoke(): PagingSource<Key, Value> {
+        return delegate()
+    }
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/multicast/Multicaster.kt b/paging/common/src/main/kotlin/androidx/paging/multicast/Multicaster.kt
index 18d85e5..b2073b5 100644
--- a/paging/common/src/main/kotlin/androidx/paging/multicast/Multicaster.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/multicast/Multicaster.kt
@@ -17,7 +17,6 @@
 package androidx.paging.multicast
 
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.consumeAsFlow
@@ -72,8 +71,7 @@
         )
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
-    val flow = flow<T> {
+    val flow: Flow<T> = flow {
         val channel = Channel<ChannelManager.Message.Dispatch.Value<T>>(Channel.UNLIMITED)
         val subFlow = channel.consumeAsFlow()
             .onStart {
diff --git a/paging/common/src/main/kotlin/androidx/paging/multicast/StoreRealActor.kt b/paging/common/src/main/kotlin/androidx/paging/multicast/StoreRealActor.kt
index ce499c7..74c7fc1 100644
--- a/paging/common/src/main/kotlin/androidx/paging/multicast/StoreRealActor.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/multicast/StoreRealActor.kt
@@ -17,9 +17,11 @@
 
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ObsoleteCoroutinesApi
-import kotlinx.coroutines.channels.SendChannel
-import kotlinx.coroutines.channels.actor
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.consumeAsFlow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onEach
 import java.util.concurrent.atomic.AtomicBoolean
 
 /**
@@ -29,29 +31,23 @@
 internal abstract class StoreRealActor<T>(
     scope: CoroutineScope
 ) {
-    private val inboundChannel: SendChannel<Any?>
+    private val inboundChannel = Channel<Any?>(capacity = Channel.RENDEZVOUS)
     private val closeCompleted = CompletableDeferred<Unit>()
     private val didClose = AtomicBoolean(false)
 
     init {
-        @OptIn(ObsoleteCoroutinesApi::class)
-        inboundChannel = scope.actor(
-            capacity = 0
-        ) {
-            try {
-                for (msg in channel) {
-                    if (msg === CLOSE_TOKEN) {
-                        doClose()
-                        break
-                    } else {
-                        @Suppress("UNCHECKED_CAST")
-                        handle(msg as T)
-                    }
+        inboundChannel.consumeAsFlow()
+            .onEach { msg ->
+                if (msg === CLOSE_TOKEN) {
+                    doClose()
+                } else {
+                    @Suppress("UNCHECKED_CAST")
+                    handle(msg as T)
                 }
-            } finally {
+            }.onCompletion {
                 doClose()
             }
-        }
+            .launchIn(scope)
     }
 
     private fun doClose() {
diff --git a/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
index a6941fc..03811c2 100644
--- a/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
 @file:Suppress("DEPRECATION")
 
 package androidx.paging
@@ -62,9 +61,9 @@
      * and alignment restrictions. These tests were written before positional+contiguous enforced
      * these behaviors.
      */
-    private inner class TestPagingSource(val listData: List<Item> = ITEMS) :
-        PagingSource<Int, Item>() {
-        @OptIn(ExperimentalPagingApi::class)
+    private inner class TestPagingSource(
+        val listData: List<Item> = ITEMS
+    ) : PagingSource<Int, Item>() {
         override fun getRefreshKey(state: PagingState<Int, Item>): Int? {
             return state.anchorPosition
                 ?.let { anchorPosition -> state.closestItemToPosition(anchorPosition)?.pos }
@@ -313,10 +312,6 @@
         }
     }
 
-    private fun verifyDropCallback(callback: Callback, position: Int) {
-        verifyDropCallback(callback, position, position)
-    }
-
     @Test
     fun append() {
         val pagedList = createCountedPagedList(0)
@@ -463,7 +458,7 @@
         drain()
         verifyRange(20, 60, pagedList)
         verifyCallback(callback, 60)
-        verifyDropCallback(callback, 0)
+        verifyDropCallback(callback, 0, 0)
         verifyNoMoreInteractions(callback)
     }
 
@@ -1050,10 +1045,10 @@
 
         assertTrue { mainThread.queue.isEmpty() }
 
-        pagedList.dispatchStateChangeAsync(LoadType.REFRESH, LoadState.Loading)
+        pagedList.dispatchStateChangeAsync(LoadType.REFRESH, Loading)
         assertEquals(1, mainThread.queue.size)
 
-        pagedList.dispatchStateChangeAsync(LoadType.REFRESH, LoadState.NotLoading.Incomplete)
+        pagedList.dispatchStateChangeAsync(LoadType.REFRESH, NotLoading.Incomplete)
         assertEquals(2, mainThread.queue.size)
     }
 
diff --git a/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
index 5448b6d..13182ab 100644
--- a/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
@@ -19,6 +19,7 @@
 import androidx.paging.PagingSource.LoadResult.Page.Companion.COUNT_UNDEFINED
 import com.nhaarman.mockitokotlin2.capture
 import com.nhaarman.mockitokotlin2.mock
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
@@ -289,7 +290,7 @@
         @Suppress("DEPRECATION")
         PagedList.Builder(dataSource, 10)
             .setNotifyDispatcher(FailDispatcher())
-            .setFetchDispatcher(DirectDispatcher)
+            .setFetchDispatcher(Dispatchers.IO)
             .setInitialKey("")
             .build()
     }
diff --git a/paging/common/src/test/kotlin/androidx/paging/LegacyPageFetcherTest.kt b/paging/common/src/test/kotlin/androidx/paging/LegacyPageFetcherTest.kt
index b93aea5..6e8ede2 100644
--- a/paging/common/src/test/kotlin/androidx/paging/LegacyPageFetcherTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/LegacyPageFetcherTest.kt
@@ -139,7 +139,7 @@
             GlobalScope,
             config,
             pagingSource,
-            DirectDispatcher,
+            testDispatcher,
             testDispatcher,
             consumer,
             storage as LegacyPageFetcher.KeyProvider<Int>
diff --git a/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
index 5b8ea29..add2986 100644
--- a/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
@@ -17,20 +17,23 @@
 package androidx.paging
 
 import androidx.paging.PagingSource.LoadResult.Page
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.Runnable
-import kotlinx.coroutines.launch
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.runBlocking
 import org.junit.Assert
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import kotlin.coroutines.CoroutineContext
+import java.util.concurrent.Executors
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
 
-@OptIn(ExperimentalPagingApi::class)
 @RunWith(JUnit4::class)
 class LegacyPagingSourceTest {
     private val fakePagingState = PagingState(
@@ -76,7 +79,10 @@
 
             override fun getKey(item: String) = item.hashCode()
         }
-        val pagingSource = LegacyPagingSource { dataSource }
+        val pagingSource = LegacyPagingSource(
+            fetchDispatcher = Dispatchers.Unconfined,
+            dataSource
+        )
 
         // Check that jumpingSupported is disabled.
         assertFalse { pagingSource.jumpingSupported }
@@ -119,7 +125,10 @@
                 Assert.fail("loadAfter not expected")
             }
         }
-        val pagingSource = LegacyPagingSource { dataSource }
+        val pagingSource = LegacyPagingSource(
+            fetchDispatcher = Dispatchers.Unconfined,
+            dataSource = dataSource
+        )
 
         // Check that jumpingSupported is disabled.
         assertFalse { pagingSource.jumpingSupported }
@@ -139,7 +148,10 @@
 
     @Test
     fun positional() {
-        val pagingSource = LegacyPagingSource { createTestPositionalDataSource() }
+        val pagingSource = LegacyPagingSource(
+            fetchDispatcher = Dispatchers.Unconfined,
+            dataSource = createTestPositionalDataSource()
+        )
 
         // Check that jumpingSupported is enabled.
         assertTrue { pagingSource.jumpingSupported }
@@ -189,7 +201,10 @@
 
     @Test
     fun invalidateFromPagingSource() {
-        val pagingSource = LegacyPagingSource { createTestPositionalDataSource() }
+        val pagingSource = LegacyPagingSource(
+            fetchDispatcher = Dispatchers.Unconfined,
+            dataSource = createTestPositionalDataSource()
+        )
         val dataSource = pagingSource.dataSource
 
         var kotlinInvalidated = false
@@ -216,7 +231,10 @@
 
     @Test
     fun invalidateFromDataSource() {
-        val pagingSource = LegacyPagingSource { createTestPositionalDataSource() }
+        val pagingSource = LegacyPagingSource(
+            fetchDispatcher = Dispatchers.Unconfined,
+            dataSource = createTestPositionalDataSource()
+        )
         val dataSource = pagingSource.dataSource
 
         var kotlinInvalidated = false
@@ -241,41 +259,57 @@
         assertTrue { javaInvalidated }
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun createDataSourceOnFetchDispatcher() {
-        val manualDispatcher = object : CoroutineDispatcher() {
-            val coroutines = ArrayList<Pair<CoroutineContext, Runnable>>()
-            override fun dispatch(context: CoroutineContext, block: Runnable) {
-                coroutines.add(context to block)
+        val methodCalls = mutableMapOf<String, MutableList<Thread>>()
+
+        val dataSourceFactory = object : DataSource.Factory<Int, String>() {
+            override fun create(): DataSource<Int, String> {
+                return ThreadCapturingDataSource { methodName ->
+                    methodCalls.getOrPut(methodName) {
+                        mutableListOf()
+                    }.add(Thread.currentThread())
+                }
             }
         }
 
-        var initialized = false
-        val pagingSource = LegacyPagingSource(manualDispatcher) {
-            initialized = true
-            createTestPositionalDataSource(expectInitialLoad = true)
-        }
+        // create an executor special to the legacy data source
+        val executor = Executors.newSingleThreadExecutor()
 
-        assertFalse { initialized }
+        // extract the thread instance from the executor. we'll use it to assert calls later
+        var dataSourceThread: Thread? = null
+        executor.submit {
+            dataSourceThread = Thread.currentThread()
+        }.get()
 
-        // Trigger lazy-initialization dispatch.
-        val job = GlobalScope.launch {
-            pagingSource.load(PagingSource.LoadParams.Refresh(0, 1, false))
-        }
-
-        // Assert that initialization has been scheduled on manualDispatcher, which has not been
-        // triggered yet.
-        assertFalse { initialized }
-
-        // Force all tasks on manualDispatcher to run.
-        while (!job.isCompleted) {
-            while (manualDispatcher.coroutines.isNotEmpty()) {
-                @OptIn(ExperimentalStdlibApi::class)
-                manualDispatcher.coroutines.removeFirst().second.run()
+        val pager = Pager(
+            config = PagingConfig(10, enablePlaceholders = false),
+            pagingSourceFactory = dataSourceFactory.asPagingSourceFactory(
+                executor.asCoroutineDispatcher()
+            )
+        )
+        // collect from pager. we take only 2 paging data generations and only take 1 PageEvent
+        // from them
+        runBlocking {
+            pager.flow.take(2).collectLatest { pagingData ->
+                // wait until first insert happens
+                pagingData.flow.filter {
+                    it is PageEvent.Insert
+                }.first()
+                pagingData.receiver.refresh()
             }
         }
-
-        assertTrue { initialized }
+        // validate method calls (to ensure test did run as expected) and their threads.
+        assertThat(methodCalls["<init>"]).hasSize(2)
+        assertThat(methodCalls["<init>"]?.toSet()).containsExactly(dataSourceThread)
+        assertThat(methodCalls["addInvalidatedCallback"]).hasSize(2)
+        assertThat(methodCalls["addInvalidatedCallback"]?.toSet()).containsExactly(dataSourceThread)
+        assertThat(methodCalls["loadInitial"]).hasSize(2)
+        assertThat(methodCalls).containsKey("isInvalid")
+        assertThat(methodCalls["loadInitial"]?.toSet()).containsExactly(dataSourceThread)
+        // TODO b/174625633 this should also be 2
+        assertThat(methodCalls["removeInvalidatedCallback"]).hasSize(1)
     }
 
     @Test
@@ -333,4 +367,55 @@
                 Assert.fail("loadRange not expected")
             }
         }
+
+    /**
+     * A data source implementation which tracks method calls and their threads.
+     */
+    @Suppress("DEPRECATION")
+    class ThreadCapturingDataSource(
+        private val recordMethodCall: (methodName: String) -> Unit
+    ) : PositionalDataSource<String>() {
+        init {
+            recordMethodCall("<init>")
+        }
+
+        override fun loadInitial(
+            params: LoadInitialParams,
+            callback: LoadInitialCallback<String>
+        ) {
+            recordMethodCall("loadInitial")
+            callback.onResult(
+                data = emptyList(),
+                position = 0,
+            )
+        }
+
+        override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
+            recordMethodCall("loadRange")
+            callback.onResult(data = emptyList())
+        }
+
+        override val isInvalid: Boolean
+            get() {
+                // this is important because room's implementation might run a db query to
+                // update invalidations.
+                recordMethodCall("isInvalid")
+                return super.isInvalid
+            }
+
+        override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+            recordMethodCall("addInvalidatedCallback")
+            super.addInvalidatedCallback(onInvalidatedCallback)
+        }
+
+        override fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
+            recordMethodCall("removeInvalidatedCallback")
+            super.removeInvalidatedCallback(onInvalidatedCallback)
+        }
+
+        override fun invalidate() {
+            recordMethodCall("invalidate")
+            super.invalidate()
+        }
+    }
 }
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotTest.kt
index 21736e3..232c4f7 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotTest.kt
@@ -72,11 +72,12 @@
 class PageFetcherSnapshotTest {
     private val testScope = TestCoroutineScope()
     private val retryBus = ConflatedEventBus<Unit>()
-    private val pagingSourceFactory = {
-        TestPagingSource().also {
+    private val pagingSourceFactory = suspend {
+        TestPagingSource(loadDelay = 1000).also {
             currentPagingSource = it
         }
     }
+
     private var currentPagingSource: TestPagingSource? = null
     private val config = PagingConfig(
         pageSize = 1,
@@ -1752,7 +1753,7 @@
         }
 
         var createdPagingSource = false
-        val factory = {
+        val factory = suspend {
             check(!createdPagingSource)
             createdPagingSource = true
             TestPagingSource(items = List(2) { it })
@@ -1801,7 +1802,7 @@
         }
 
         var createdPagingSource = false
-        val factory = {
+        val factory = suspend {
             check(!createdPagingSource)
             createdPagingSource = true
             TestPagingSource(items = List(2) { it })
@@ -2183,7 +2184,14 @@
                 LoadStateUpdate(REFRESH, true, Loading),
                 LoadStateUpdate(REFRESH, true, Error(EXCEPTION)),
                 LoadStateUpdate(REFRESH, false, Loading),
-                createRefresh(0..2, remoteLoadStatesOf(refreshRemote = Error(EXCEPTION))),
+                createRefresh(
+                    range = 0..2,
+                    remoteLoadStatesOf(
+                        refresh = Error(EXCEPTION),
+                        prependLocal = NotLoading.Complete,
+                        refreshRemote = Error(EXCEPTION),
+                    ),
+                ),
                 // since remote refresh failed and launch initial refresh is requested,
                 // we won't receive any append/prepend events
             )
@@ -2262,6 +2270,79 @@
     }
 
     @Test
+    fun remoteMediator_remoteRefreshEndOfPaginationReached() = testScope.runBlockingTest {
+        @OptIn(ExperimentalPagingApi::class)
+        val remoteMediator = RemoteMediatorMock().apply {
+            initializeResult = RemoteMediator.InitializeAction.LAUNCH_INITIAL_REFRESH
+            loadCallback = { _, _ -> RemoteMediator.MediatorResult.Success(true) }
+        }
+
+        val config = PagingConfig(
+            pageSize = 1,
+            prefetchDistance = 2,
+            enablePlaceholders = true,
+            initialLoadSize = 1,
+            maxSize = 5
+        )
+        val pager = PageFetcher(
+            initialKey = 0,
+            pagingSourceFactory = { TestPagingSource(items = listOf(0)) },
+            config = config,
+            remoteMediator = remoteMediator
+        )
+
+        val state = collectFetcherState(pager)
+
+        advanceUntilIdle()
+        assertThat(state.newEvents()).isEqualTo(
+            listOf(
+                LoadStateUpdate(loadType = REFRESH, fromMediator = true, loadState = Loading),
+                LoadStateUpdate(
+                    loadType = REFRESH,
+                    fromMediator = true,
+                    loadState = NotLoading(endOfPaginationReached = true)
+                ),
+                LoadStateUpdate(
+                    loadType = PREPEND,
+                    fromMediator = true,
+                    loadState = NotLoading(endOfPaginationReached = true)
+                ),
+                LoadStateUpdate(
+                    loadType = APPEND,
+                    fromMediator = true,
+                    loadState = NotLoading(endOfPaginationReached = true)
+                ),
+                LoadStateUpdate(loadType = REFRESH, fromMediator = false, loadState = Loading),
+                Refresh(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0),
+                            data = listOf(0),
+                            hintOriginalPageOffset = 0,
+                            hintOriginalIndices = null
+                        )
+                    ),
+                    placeholdersBefore = 0,
+                    placeholdersAfter = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        refresh = NotLoading(endOfPaginationReached = true),
+                        prepend = NotLoading(endOfPaginationReached = true),
+                        append = NotLoading(endOfPaginationReached = true),
+                        refreshLocal = NotLoading(endOfPaginationReached = false),
+                        prependLocal = NotLoading(endOfPaginationReached = true),
+                        appendLocal = NotLoading(endOfPaginationReached = true),
+                        refreshRemote = NotLoading(endOfPaginationReached = true),
+                        prependRemote = NotLoading(endOfPaginationReached = true),
+                        appendRemote = NotLoading(endOfPaginationReached = true),
+                    )
+                )
+            )
+        )
+
+        state.job.cancel()
+    }
+
+    @Test
     fun remoteMediator_endOfPaginationNotReachedLoadStatePrepend() = testScope.runBlockingTest {
         @OptIn(ExperimentalPagingApi::class)
         val remoteMediator = object : RemoteMediatorMock() {
@@ -2305,7 +2386,9 @@
                     ),
                     placeholdersBefore = 0,
                     placeholdersAfter = 99,
-                    combinedLoadStates = remoteLoadStatesOf()
+                    combinedLoadStates = remoteLoadStatesOf(
+                        prependLocal = NotLoading.Complete
+                    )
                 ),
                 LoadStateUpdate(
                     loadType = PREPEND,
@@ -2328,7 +2411,9 @@
                     ),
                     placeholdersBefore = 0,
                     placeholdersAfter = 99,
-                    combinedLoadStates = remoteLoadStatesOf()
+                    combinedLoadStates = remoteLoadStatesOf(
+                        prependLocal = NotLoading.Complete
+                    )
                 )
             )
 
@@ -2380,7 +2465,9 @@
                         ),
                         placeholdersBefore = 0,
                         placeholdersAfter = 99,
-                        combinedLoadStates = remoteLoadStatesOf()
+                        combinedLoadStates = remoteLoadStatesOf(
+                            prependLocal = NotLoading.Complete,
+                        )
                     ),
                     LoadStateUpdate(
                         loadType = PREPEND,
@@ -2444,10 +2531,7 @@
                     combinedLoadStates = remoteLoadStatesOf()
                 )
             )
-            assertEvents(
-                eventsByGeneration[0],
-                refreshEvents
-            )
+            assertThat(eventsByGeneration[0]).isEqualTo(refreshEvents)
             accessHint(
                 ViewportHint.Access(
                     pageOffset = 0,
@@ -2485,7 +2569,7 @@
                     loadType = PREPEND,
                     fromMediator = true,
                     loadState = NotLoading.Complete
-                )
+                ),
             )
             awaitEventCount(refreshEvents.size + postHintEvents.size)
             assertEquals(
@@ -2539,7 +2623,9 @@
                         ),
                         placeholdersBefore = 99,
                         placeholdersAfter = 0,
-                        combinedLoadStates = remoteLoadStatesOf()
+                        combinedLoadStates = remoteLoadStatesOf(
+                            appendLocal = NotLoading.Complete,
+                        )
                     ),
                     LoadStateUpdate(
                         loadType = APPEND,
@@ -2562,7 +2648,9 @@
                         ),
                         placeholdersBefore = 99,
                         placeholdersAfter = 0,
-                        combinedLoadStates = remoteLoadStatesOf()
+                        combinedLoadStates = remoteLoadStatesOf(
+                            appendLocal = NotLoading.Complete,
+                        )
                     ),
                 )
             )
@@ -2597,7 +2685,7 @@
         )
 
         val expected = listOf(
-            listOf<PageEvent<Int>>(
+            listOf(
                 LoadStateUpdate(
                     loadType = REFRESH,
                     fromMediator = false,
@@ -2612,7 +2700,9 @@
                     ),
                     placeholdersBefore = 99,
                     placeholdersAfter = 0,
-                    combinedLoadStates = remoteLoadStatesOf()
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                    )
                 ),
                 LoadStateUpdate(
                     loadType = APPEND,
@@ -2716,7 +2806,7 @@
                 ),
             )
             awaitEventCount(initialEvents.size + postHintEvents.size)
-            assertEvents(initialEvents + postHintEvents, eventsByGeneration[0])
+            assertThat(eventsByGeneration[0]).isEqualTo(initialEvents + postHintEvents)
         }
     }
 
@@ -2760,8 +2850,13 @@
                         fromMediator = true,
                         loadState = Loading
                     ),
+                    LoadStateUpdate(
+                        loadType = REFRESH,
+                        fromMediator = true,
+                        loadState = NotLoading.Incomplete
+                    ),
                 ),
-                listOf<PageEvent<Int>>(
+                listOf(
                     LoadStateUpdate(
                         loadType = REFRESH,
                         fromMediator = false,
@@ -2786,7 +2881,7 @@
     @Test
     fun remoteMediator_initialRefreshSuccessEndOfPagination() = testScope.runBlockingTest {
         @OptIn(ExperimentalPagingApi::class)
-        val remoteMediator = object : RemoteMediatorMock() {
+        val remoteMediator = object : RemoteMediatorMock(loadDelay = 2000) {
             override suspend fun initialize(): InitializeAction {
                 super.initialize()
                 return InitializeAction.LAUNCH_INITIAL_REFRESH
@@ -2810,57 +2905,79 @@
         )
         val pager = PageFetcher(
             initialKey = 50,
-            pagingSourceFactory = pagingSourceFactory,
+            pagingSourceFactory = {
+                TestPagingSource().apply {
+                    nextLoadResult = Page(
+                        data = listOf(50),
+                        prevKey = null,
+                        nextKey = null,
+                        itemsBefore = 50,
+                        itemsAfter = 49
+                    )
+                }
+            },
             config = config,
             remoteMediator = remoteMediator
         )
 
-        pager.assertEventByGeneration(
+        val fetcherState = collectFetcherState(pager)
+
+        advanceTimeBy(1000)
+
+        assertThat(fetcherState.newEvents()).isEqualTo(
             listOf(
-                listOf(
-                    LoadStateUpdate(
-                        loadType = REFRESH,
-                        fromMediator = true,
-                        loadState = Loading,
-                    ),
-                    LoadStateUpdate(
-                        loadType = REFRESH,
-                        fromMediator = true,
-                        loadState = NotLoading.Complete,
-                    ),
-                    LoadStateUpdate(
-                        loadType = PREPEND,
-                        fromMediator = true,
-                        loadState = NotLoading.Complete,
-                    ),
-                    LoadStateUpdate(
-                        loadType = APPEND,
-                        fromMediator = true,
-                        loadState = NotLoading.Complete,
-                    ),
-                    LoadStateUpdate(
-                        loadType = REFRESH,
-                        fromMediator = false,
-                        loadState = Loading,
-                    ),
-                    Refresh(
-                        pages = listOf(
-                            TransformablePage(
-                                originalPageOffset = 0,
-                                data = listOf(50)
-                            )
-                        ),
-                        placeholdersBefore = 50,
-                        placeholdersAfter = 49,
-                        combinedLoadStates = remoteLoadStatesOf(
-                            refreshRemote = NotLoading.Complete,
-                            prependRemote = NotLoading.Complete,
-                            appendRemote = NotLoading.Complete,
+                LoadStateUpdate(
+                    loadType = REFRESH,
+                    fromMediator = true,
+                    loadState = Loading,
+                ),
+                LoadStateUpdate(
+                    loadType = REFRESH,
+                    fromMediator = false,
+                    loadState = Loading,
+                ),
+                Refresh(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = 0,
+                            data = listOf(50)
                         )
                     ),
+                    placeholdersBefore = 50,
+                    placeholdersAfter = 49,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        refresh = Loading,
+                        prependLocal = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        refreshRemote = Loading,
+                    )
+                ),
+            ),
+        )
+
+        advanceUntilIdle()
+
+        assertThat(fetcherState.newEvents()).isEqualTo(
+            listOf<PageEvent<Int>>(
+                LoadStateUpdate(
+                    loadType = REFRESH,
+                    fromMediator = true,
+                    loadState = NotLoading.Complete,
+                ),
+                LoadStateUpdate(
+                    loadType = PREPEND,
+                    fromMediator = true,
+                    loadState = NotLoading.Complete
+                ),
+                LoadStateUpdate(
+                    loadType = APPEND,
+                    fromMediator = true,
+                    loadState = NotLoading.Complete
                 ),
             )
         )
+
+        fetcherState.job.cancel()
     }
 
     @Test
@@ -3225,15 +3342,6 @@
         assertFalse { initialHint.shouldPrioritizeOver(accessHint, APPEND) }
     }
 
-    @OptIn(ExperimentalPagingApi::class)
-    private suspend fun <Key : Any, Value : Any> createRemoteMediatorAccessor(
-        delegate: RemoteMediator<Key, Value>
-    ): RemoteMediatorAccessor<Key, Value> {
-        return RemoteMediatorAccessor(testScope, delegate).also {
-            it.initialize()
-        }
-    }
-
     internal class CollectedPageEvents<T : Any>(val pageEvents: ArrayList<PageEvent<T>>) {
         var lastIndex = 0
         fun newEvents(): List<PageEvent<T>> = when {
@@ -3309,7 +3417,7 @@
             stop()
         }
         expected.forEachIndexed { index, list ->
-            assertEvents(list, actual.getOrNull(index) ?: emptyList())
+            assertThat(actual.getOrNull(index) ?: emptyList<PageEvent<T>>()).isEqualTo(list)
         }
         assertThat(actual.size).isEqualTo(expected.size)
     }
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt
index b347092..4cb76d0 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt
@@ -49,7 +49,7 @@
 @RunWith(JUnit4::class)
 class PageFetcherTest {
     private val testScope = TestCoroutineScope()
-    private val pagingSourceFactory = { TestPagingSource() }
+    private val pagingSourceFactory = suspend { TestPagingSource() }
     private val config = PagingConfig(
         pageSize = 1,
         prefetchDistance = 1,
@@ -91,7 +91,9 @@
     @Test
     fun refresh_fromPagingSource() = testScope.runBlockingTest {
         var pagingSource: PagingSource<Int, Int>? = null
-        val pagingSourceFactory = { TestPagingSource().also { pagingSource = it } }
+        val pagingSourceFactory = suspend {
+            TestPagingSource().also { pagingSource = it }
+        }
         val pageFetcher = PageFetcher(pagingSourceFactory, 50, config)
         val fetcherState = collectFetcherState(pageFetcher)
 
@@ -114,7 +116,9 @@
     @Test
     fun refresh_callsInvalidate() = testScope.runBlockingTest {
         var pagingSource: PagingSource<Int, Int>? = null
-        val pagingSourceFactory = { TestPagingSource().also { pagingSource = it } }
+        val pagingSourceFactory = suspend {
+            TestPagingSource().also { pagingSource = it }
+        }
         val pageFetcher = PageFetcher(pagingSourceFactory, 50, config)
         val fetcherState = collectFetcherState(pageFetcher)
 
@@ -303,7 +307,9 @@
     fun jump() = testScope.runBlockingTest {
         pauseDispatcher {
             val pagingSources = mutableListOf<PagingSource<Int, Int>>()
-            val pagingSourceFactory = { TestPagingSource().also { pagingSources.add(it) } }
+            val pagingSourceFactory = suspend {
+                TestPagingSource().also { pagingSources.add(it) }
+            }
             val config = PagingConfig(
                 pageSize = 1,
                 prefetchDistance = 1,
@@ -382,7 +388,11 @@
                 prefetchDistance = 1,
                 initialLoadSize = 2
             )
-            val pageFetcher = PageFetcher({ pagingSource }, 50, config)
+            val pageFetcher = PageFetcher(
+                pagingSourceFactory = suspend { pagingSource },
+                initialKey = 50,
+                config = config
+            )
             val job = testScope.launch {
                 assertFailsWith<IllegalStateException> {
                     pageFetcher.flow.collect { }
@@ -444,7 +454,7 @@
             )
             val pagingSources = mutableListOf<TestPagingSource>()
             val pageFetcher = PageFetcher(
-                pagingSourceFactory = {
+                pagingSourceFactory = suspend {
                     TestPagingSource(loadDelay = 1000).also {
                         pagingSources.add(it)
                     }
@@ -582,7 +592,6 @@
     val pageEventLists: ArrayList<ArrayList<PageEvent<Int>>> = ArrayList()
 
     val job = launch {
-        @OptIn(ExperimentalCoroutinesApi::class)
         fetcher.flow.collectIndexed { index, pagingData ->
             pagingDataList.add(index, pagingData)
             pageEventLists.add(index, ArrayList())
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
index 44398e8..c94b1cf 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
@@ -18,6 +18,9 @@
 
 import androidx.testutils.TestDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineDispatcher
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -86,16 +89,17 @@
         }
     }
 
+    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun loadFullVerify() {
         // validate paging entire ItemDataSource results in full, correctly ordered data
-        val testCoroutineScope = CoroutineScope(EmptyCoroutineContext)
-
+        val dispatcher = TestCoroutineDispatcher()
+        val testCoroutineScope = CoroutineScope(dispatcher)
         @Suppress("DEPRECATION")
         val pagedList = PagedList.Builder(ItemDataSource(), 100)
             .setCoroutineScope(testCoroutineScope)
-            .setNotifyDispatcher(mainThread)
-            .setFetchDispatcher(DirectDispatcher)
+            .setNotifyDispatcher(dispatcher)
+            .setFetchDispatcher(dispatcher)
             .build()
 
         // validate initial load
@@ -105,7 +109,7 @@
         for (i in 0..PAGE_MAP.keys.size) {
             pagedList.loadAround(0)
             pagedList.loadAround(pagedList.size - 1)
-            drain()
+            dispatcher.advanceUntilIdle()
         }
 
         // validate full load
@@ -148,7 +152,7 @@
         @Suppress("DEPRECATION")
         PagedList.Builder(dataSource, 10)
             .setNotifyDispatcher(FailDispatcher())
-            .setFetchDispatcher(DirectDispatcher)
+            .setFetchDispatcher(Dispatchers.IO)
             .build()
     }
 
@@ -248,7 +252,7 @@
         val pagedList = PagedList.Builder(dataSource, 10)
             .setBoundaryCallback(boundaryCallback)
             .setCoroutineScope(testCoroutineScope)
-            .setFetchDispatcher(dispatcher)
+            .setFetchDispatcher(Dispatchers.Unconfined)
             .setNotifyDispatcher(dispatcher)
             .build()
 
@@ -301,7 +305,7 @@
         val pagedList = PagedList.Builder(dataSource, 10)
             .setBoundaryCallback(boundaryCallback)
             .setCoroutineScope(testCoroutineScope)
-            .setFetchDispatcher(dispatcher)
+            .setFetchDispatcher(Dispatchers.Unconfined)
             .setNotifyDispatcher(dispatcher)
             .build()
 
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
index bd766f3..a0ea57f 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
@@ -21,11 +21,14 @@
 import androidx.testutils.TestDispatcher
 import androidx.testutils.TestExecutor
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import java.util.concurrent.Executor
+import kotlin.concurrent.thread
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.test.assertFailsWith
 import kotlin.test.assertTrue
@@ -53,10 +56,18 @@
 
     @Test
     fun createLegacy() {
+        val slowFetchExecutor = Executor {
+            // just be slow to ensure `build()` really waited on fetch to complete.
+            // but still run it on another thread to ensure we are not blocking the test here
+            thread {
+                Thread.sleep(1000)
+                it.run()
+            }
+        }
         @Suppress("DEPRECATION")
         val pagedList = PagedList.Builder(TestPositionalDataSource(ITEMS), 100)
             .setNotifyExecutor(TestExecutor())
-            .setFetchExecutor(TestExecutor())
+            .setFetchExecutor(slowFetchExecutor)
             .build()
         // if build succeeds without flushing an executor, success!
         assertEquals(ITEMS, pagedList)
@@ -76,8 +87,8 @@
                     pagingSource,
                     null,
                     testCoroutineScope,
-                    DirectDispatcher,
-                    DirectDispatcher,
+                    Dispatchers.Default,
+                    Dispatchers.IO,
                     null,
                     Config(10),
                     0
@@ -104,8 +115,8 @@
                     pagingSource,
                     null,
                     testCoroutineScope,
-                    DirectDispatcher,
-                    DirectDispatcher,
+                    Dispatchers.Default,
+                    Dispatchers.IO,
                     null,
                     Config(10),
                     0
@@ -126,8 +137,8 @@
 
         @Suppress("DEPRECATION")
         val pagedList = PagedList.Builder(pagingSource, initialPage, config)
-            .setNotifyDispatcher(DirectDispatcher)
-            .setFetchDispatcher(DirectDispatcher)
+            .setNotifyDispatcher(Dispatchers.Default)
+            .setFetchDispatcher(Dispatchers.IO)
             .build()
 
         assertEquals(pagingSource, pagedList.pagingSource)
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt
index 6a6395c..883b4f2 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt
@@ -524,8 +524,12 @@
         previousList: NullPaddedList<Int>,
         newList: NullPaddedList<Int>,
         newCombinedLoadStates: CombinedLoadStates,
-        lastAccessedIndex: Int
-    ): Int? = null
+        lastAccessedIndex: Int,
+        onListPresentable: () -> Unit
+    ): Int? {
+        onListPresentable()
+        return null
+    }
 }
 
 internal val dummyReceiver = object : UiReceiver {
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagingSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagingSourceTest.kt
index a5b05f9..6f90f6b 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagingSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagingSourceTest.kt
@@ -256,7 +256,6 @@
 
         private var error = false
 
-        @OptIn(ExperimentalPagingApi::class)
         override fun getRefreshKey(state: PagingState<Key, Item>): Key? {
             return state.anchorPosition
                 ?.let { anchorPosition -> state.closestItemToPosition(anchorPosition) }
diff --git a/paging/common/src/test/kotlin/androidx/paging/RemoteMediatorAccessorTest.kt b/paging/common/src/test/kotlin/androidx/paging/RemoteMediatorAccessorTest.kt
index 151a55d..29d1dbd 100644
--- a/paging/common/src/test/kotlin/androidx/paging/RemoteMediatorAccessorTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/RemoteMediatorAccessorTest.kt
@@ -39,6 +39,7 @@
 class RemoteMediatorAccessorTest {
     private val testScope = TestCoroutineScope()
     private var mockStateId = 0
+
     // creates a unique state using the anchor position to be able to do equals check in assertions
     private fun createMockState(
         anchorPosition: Int? = mockStateId++
@@ -52,6 +53,110 @@
     }
 
     @Test
+    fun load_reportsPrependLoadState() = testScope.runBlockingTest {
+        val emptyState = PagingState<Int, Int>(listOf(), null, PagingConfig(10), COUNT_UNDEFINED)
+        val remoteMediator = RemoteMediatorMock(loadDelay = 1000)
+        val remoteMediatorAccessor = createAccessor(remoteMediator)
+
+        // Assert initial state is NotLoading.Incomplete.
+        assertEquals(
+            LoadStates.IDLE.copy(prepend = LoadState.NotLoading.Incomplete),
+            remoteMediatorAccessor.state.value,
+        )
+
+        // Start a PREPEND load.
+        remoteMediatorAccessor.requestLoad(
+            loadType = PREPEND,
+            pagingState = emptyState,
+        )
+
+        // Assert state is immediately set to Loading.
+        assertEquals(
+            LoadStates.IDLE.copy(prepend = LoadState.Loading),
+            remoteMediatorAccessor.state.value,
+        )
+
+        // Wait for load to finish.
+        advanceUntilIdle()
+
+        // Assert state is set to NotLoading.Incomplete.
+        assertEquals(
+            LoadStates.IDLE.copy(prepend = LoadState.NotLoading.Incomplete),
+            remoteMediatorAccessor.state.value,
+        )
+
+        // Start a PREPEND load which results in endOfPaginationReached = true.
+        remoteMediator.loadCallback = { _, _ ->
+            RemoteMediator.MediatorResult.Success(endOfPaginationReached = true)
+        }
+        remoteMediatorAccessor.requestLoad(
+            loadType = PREPEND,
+            pagingState = emptyState,
+        )
+
+        // Wait for load to finish.
+        advanceUntilIdle()
+
+        // Assert state is set to NotLoading.Incomplete.
+        assertEquals(
+            LoadStates.IDLE.copy(prepend = LoadState.NotLoading.Complete),
+            remoteMediatorAccessor.state.value,
+        )
+    }
+
+    @Test
+    fun load_reportsAppendLoadState() = testScope.runBlockingTest {
+        val emptyState = PagingState<Int, Int>(listOf(), null, PagingConfig(10), COUNT_UNDEFINED)
+        val remoteMediator = RemoteMediatorMock(loadDelay = 1000)
+        val remoteMediatorAccessor = createAccessor(remoteMediator)
+
+        // Assert initial state is NotLoading.Incomplete.
+        assertEquals(
+            LoadStates.IDLE.copy(prepend = LoadState.NotLoading.Incomplete),
+            remoteMediatorAccessor.state.value,
+        )
+
+        // Start a APPEND load.
+        remoteMediatorAccessor.requestLoad(
+            loadType = APPEND,
+            pagingState = emptyState,
+        )
+
+        // Assert state is immediately set to Loading.
+        assertEquals(
+            LoadStates.IDLE.copy(append = LoadState.Loading),
+            remoteMediatorAccessor.state.value,
+        )
+
+        // Wait for load to finish.
+        advanceUntilIdle()
+
+        // Assert state is set to NotLoading.Incomplete.
+        assertEquals(
+            LoadStates.IDLE.copy(append = LoadState.NotLoading.Incomplete),
+            remoteMediatorAccessor.state.value,
+        )
+
+        // Start a APPEND load which results in endOfPaginationReached = true.
+        remoteMediator.loadCallback = { _, _ ->
+            RemoteMediator.MediatorResult.Success(endOfPaginationReached = true)
+        }
+        remoteMediatorAccessor.requestLoad(
+            loadType = APPEND,
+            pagingState = emptyState,
+        )
+
+        // Wait for load to finish.
+        advanceUntilIdle()
+
+        // Assert state is set to NotLoading.Incomplete.
+        assertEquals(
+            LoadStates.IDLE.copy(append = LoadState.NotLoading.Complete),
+            remoteMediatorAccessor.state.value,
+        )
+    }
+
+    @Test
     fun load_conflatesPrepend() = testScope.runBlockingTest {
         val remoteMediator = RemoteMediatorMock(loadDelay = 1000)
         val remoteMediatorAccessor = createAccessor(remoteMediator)
diff --git a/paging/common/src/test/kotlin/androidx/paging/SeparatorsTest.kt b/paging/common/src/test/kotlin/androidx/paging/SeparatorsTest.kt
index 38d8dc7..276285b 100644
--- a/paging/common/src/test/kotlin/androidx/paging/SeparatorsTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/SeparatorsTest.kt
@@ -19,10 +19,12 @@
 import androidx.paging.LoadState.NotLoading
 import androidx.paging.LoadType.APPEND
 import androidx.paging.LoadType.PREPEND
+import androidx.paging.LoadType.REFRESH
 import androidx.paging.PageEvent.Drop
 import androidx.paging.PageEvent.Insert.Companion.Append
 import androidx.paging.PageEvent.Insert.Companion.Prepend
 import androidx.paging.PageEvent.Insert.Companion.Refresh
+import androidx.paging.PageEvent.LoadStateUpdate
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
@@ -985,6 +987,481 @@
         )
     }
 
+    @Test
+    fun remoteRefreshEndOfPaginationReached() = runBlockingTest {
+        assertThat(
+            flowOf(
+                LoadStateUpdate(
+                    loadType = REFRESH,
+                    fromMediator = true,
+                    loadState = LoadState.Loading
+                ),
+                LoadStateUpdate(
+                    loadType = REFRESH,
+                    fromMediator = false,
+                    loadState = LoadState.Loading
+                ),
+                Refresh(
+                    pages = listOf(listOf("a1")).toTransformablePages(),
+                    placeholdersBefore = 1,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                    )
+                ),
+                LoadStateUpdate(
+                    loadType = REFRESH,
+                    fromMediator = true,
+                    loadState = NotLoading(endOfPaginationReached = true)
+                ),
+                LoadStateUpdate(
+                    loadType = PREPEND,
+                    fromMediator = true,
+                    loadState = NotLoading(endOfPaginationReached = true)
+                ),
+                LoadStateUpdate(
+                    loadType = APPEND,
+                    fromMediator = true,
+                    loadState = NotLoading(endOfPaginationReached = true)
+                ),
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                LoadStateUpdate(
+                    loadType = REFRESH,
+                    fromMediator = true,
+                    loadState = LoadState.Loading
+                ),
+                LoadStateUpdate(
+                    loadType = REFRESH,
+                    fromMediator = false,
+                    loadState = LoadState.Loading
+                ),
+                Refresh(
+                    pages = listOf(listOf("a1")).toTransformablePages(),
+                    placeholdersBefore = 1,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                    )
+                ),
+                LoadStateUpdate(
+                    loadType = REFRESH,
+                    fromMediator = true,
+                    loadState = NotLoading(endOfPaginationReached = true)
+                ),
+                Prepend(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0),
+                            data = listOf("A"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = 0,
+                        ),
+                    ),
+                    placeholdersBefore = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        refresh = NotLoading.Complete,
+                        prepend = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                        refreshRemote = NotLoading.Complete,
+                        prependRemote = NotLoading.Complete,
+                    ),
+                ),
+                Append(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0),
+                            data = listOf("END"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = 0,
+                        ),
+                    ),
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        refresh = NotLoading.Complete,
+                        prepend = NotLoading.Complete,
+                        append = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                        refreshRemote = NotLoading.Complete,
+                        prependRemote = NotLoading.Complete,
+                        appendRemote = NotLoading.Complete,
+                    ),
+                ),
+            )
+        )
+    }
+
+    @Test
+    fun remotePrependEndOfPaginationReached() = runBlockingTest {
+        assertThat(
+            flowOf(
+                Refresh(
+                    pages = listOf(listOf("a1")).toTransformablePages(),
+                    placeholdersBefore = 1,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                    )
+                ),
+                // Signalling that remote prepend is done triggers the header to resolve.
+                LoadStateUpdate(PREPEND, true, NotLoading.Complete),
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                Refresh(
+                    pages = listOf(listOf("a1")).toTransformablePages(),
+                    placeholdersBefore = 1,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                    )
+                ),
+                Prepend(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0),
+                            data = listOf("A"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = 0,
+                        ),
+                    ),
+                    placeholdersBefore = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        prepend = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                        prependRemote = NotLoading.Complete,
+                    ),
+                ),
+            )
+        )
+    }
+
+    @Test
+    fun remotePrependEndOfPaginationReachedWithDrops() = runBlockingTest {
+        assertThat(
+            flowOf(
+                Refresh(
+                    pages = listOf(listOf("b1")).toTransformablePages(),
+                    placeholdersBefore = 1,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Incomplete,
+                    )
+                ),
+                Prepend(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = -1,
+                            data = listOf("a1"),
+                        )
+                    ),
+                    placeholdersBefore = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                    )
+                ),
+                // Signalling that remote prepend is done triggers the header to resolve.
+                LoadStateUpdate(PREPEND, true, NotLoading.Complete),
+                // Drop the first page, header and separator between "b1" and "a1"
+                Drop(
+                    loadType = PREPEND,
+                    minPageOffset = -1,
+                    maxPageOffset = -1,
+                    placeholdersRemaining = 1
+                ),
+                Prepend(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = -1,
+                            data = listOf("a1"),
+                        )
+                    ),
+                    placeholdersBefore = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        prepend = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                        prependRemote = NotLoading.Complete,
+                    )
+                ),
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                Refresh(
+                    pages = listOf(listOf("b1")).toTransformablePages(),
+                    placeholdersBefore = 1,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Incomplete,
+                    )
+                ),
+                Prepend(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = -1,
+                            data = listOf("a1"),
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-1, 0),
+                            data = listOf("B"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = -1
+                        ),
+                    ),
+                    placeholdersBefore = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                    )
+                ),
+                Prepend(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-1),
+                            data = listOf("A"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = -1,
+                        ),
+                    ),
+                    placeholdersBefore = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        prepend = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                        prependRemote = NotLoading.Complete,
+                    ),
+                ),
+                Drop(
+                    loadType = PREPEND,
+                    minPageOffset = -1,
+                    maxPageOffset = -1,
+                    placeholdersRemaining = 1
+                ),
+                Prepend(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-1),
+                            data = listOf("A"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = -1,
+                        ),
+                        TransformablePage(
+                            originalPageOffset = -1,
+                            data = listOf("a1"),
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-1, 0),
+                            data = listOf("B"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = -1
+                        ),
+                    ),
+                    placeholdersBefore = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        prepend = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                        prependRemote = NotLoading.Complete,
+                    )
+                ),
+            )
+        )
+    }
+
+    @Test
+    fun remoteAppendEndOfPaginationReached() = runBlockingTest {
+        assertThat(
+            flowOf(
+                Refresh(
+                    pages = listOf(listOf("a1")).toTransformablePages(),
+                    placeholdersBefore = 1,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                    )
+                ),
+                // Signalling that remote append is done triggers the footer to resolve.
+                LoadStateUpdate(APPEND, true, NotLoading.Complete),
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                Refresh(
+                    pages = listOf(listOf("a1")).toTransformablePages(),
+                    placeholdersBefore = 1,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                    )
+                ),
+                Append(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0),
+                            data = listOf("END"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = 0,
+                        ),
+                    ),
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        append = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                        appendRemote = NotLoading.Complete,
+                    ),
+                ),
+            )
+        )
+    }
+
+    @Test
+    fun remoteAppendEndOfPaginationReachedWithDrops() = runBlockingTest {
+        assertThat(
+            flowOf(
+                Refresh(
+                    pages = listOf(listOf("b1")).toTransformablePages(),
+                    placeholdersBefore = 1,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        prependLocal = NotLoading.Complete,
+                        appendLocal = NotLoading.Incomplete,
+                    )
+                ),
+                Append(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = 1,
+                            data = listOf("c1"),
+                        )
+                    ),
+                    placeholdersAfter = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        prependLocal = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                    )
+                ),
+                // Signalling that remote append is done triggers the footer to resolve.
+                LoadStateUpdate(APPEND, true, NotLoading.Complete),
+                // Drop the last page, footer and separator between "b1" and "c1"
+                Drop(
+                    loadType = APPEND,
+                    minPageOffset = 1,
+                    maxPageOffset = 1,
+                    placeholdersRemaining = 1
+                ),
+                Append(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = 1,
+                            data = listOf("c1"),
+                        )
+                    ),
+                    placeholdersAfter = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        append = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        appendRemote = NotLoading.Complete,
+                    )
+                ),
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                Refresh(
+                    pages = listOf(listOf("b1")).toTransformablePages(),
+                    placeholdersBefore = 1,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        prependLocal = NotLoading.Complete,
+                        appendLocal = NotLoading.Incomplete,
+                    )
+                ),
+                Append(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0, 1),
+                            data = listOf("C"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = 1
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 1,
+                            data = listOf("c1"),
+                        ),
+                    ),
+                    placeholdersAfter = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        prependLocal = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                    )
+                ),
+                Append(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(1),
+                            data = listOf("END"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = 1,
+                        ),
+                    ),
+                    placeholdersAfter = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        append = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        appendRemote = NotLoading.Complete,
+                    ),
+                ),
+                Drop(
+                    loadType = APPEND,
+                    minPageOffset = 1,
+                    maxPageOffset = 1,
+                    placeholdersRemaining = 1
+                ),
+                Append(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0, 1),
+                            data = listOf("C"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = 1
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 1,
+                            data = listOf("c1"),
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(1),
+                            data = listOf("END"),
+                            hintOriginalIndices = listOf(0),
+                            hintOriginalPageOffset = 1
+                        ),
+                    ),
+                    placeholdersAfter = 0,
+                    combinedLoadStates = remoteLoadStatesOf(
+                        append = NotLoading.Complete,
+                        prependLocal = NotLoading.Complete,
+                        appendLocal = NotLoading.Complete,
+                        appendRemote = NotLoading.Complete,
+                    )
+                ),
+            )
+        )
+    }
+
     companion object {
         /**
          * Creates an upper-case letter at the beginning of each section of strings that start
diff --git a/paging/common/src/test/kotlin/androidx/paging/WrappedItemKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/WrappedItemKeyedDataSourceTest.kt
index 2c90329..f642d6c 100644
--- a/paging/common/src/test/kotlin/androidx/paging/WrappedItemKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/WrappedItemKeyedDataSourceTest.kt
@@ -21,7 +21,6 @@
 import org.junit.runners.JUnit4
 import kotlin.test.assertTrue
 
-@OptIn(ExperimentalPagingApi::class)
 @RunWith(JUnit4::class)
 class WrappedItemKeyedDataSourceTest {
 
diff --git a/paging/common/src/test/kotlin/androidx/paging/WrappedPageKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/WrappedPageKeyedDataSourceTest.kt
index 129cd3f..cf4ef24 100644
--- a/paging/common/src/test/kotlin/androidx/paging/WrappedPageKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/WrappedPageKeyedDataSourceTest.kt
@@ -21,7 +21,6 @@
 import org.junit.runners.JUnit4
 import kotlin.test.assertTrue
 
-@OptIn(ExperimentalPagingApi::class)
 @RunWith(JUnit4::class)
 class WrappedPageKeyedDataSourceTest {
 
diff --git a/paging/common/src/test/kotlin/androidx/paging/WrappedPositionalDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/WrappedPositionalDataSourceTest.kt
index cc02f84..b56fe139 100644
--- a/paging/common/src/test/kotlin/androidx/paging/WrappedPositionalDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/WrappedPositionalDataSourceTest.kt
@@ -21,7 +21,6 @@
 import org.junit.runners.JUnit4
 import kotlin.test.assertTrue
 
-@OptIn(ExperimentalPagingApi::class)
 @RunWith(JUnit4::class)
 class WrappedPositionalDataSourceTest {
 
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
index 5ce84fd..a107d6e 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
@@ -18,7 +18,6 @@
 
 import android.graphics.Color
 import androidx.annotation.ColorInt
-import androidx.paging.ExperimentalPagingApi
 import androidx.paging.PagingSource
 import androidx.paging.PagingState
 import kotlinx.coroutines.delay
@@ -34,7 +33,6 @@
 
     private val generationId = sGenerationId++
 
-    @OptIn(ExperimentalPagingApi::class)
     override fun getRefreshKey(state: PagingState<Int, Item>): Int? = state.anchorPosition
 
     override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Item> =
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/CustomerDao.java b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/CustomerDao.java
index 1b400ca..58cd34c 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/CustomerDao.java
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/CustomerDao.java
@@ -52,16 +52,16 @@
     void removeAll();
 
     /**
-     * @return DataSource.Factory of customers, ordered by last name. Use
+     * @return DataSource.Factory of customers, ordered by id. Use
      * {@link androidx.paging.LivePagedListBuilder} to get a LiveData of PagedLists.
      */
-    @Query("SELECT * FROM customer ORDER BY mLastName ASC")
+    @Query("SELECT * FROM customer ORDER BY mId ASC")
     DataSource.Factory<Integer, Customer> loadPagedAgeOrder();
 
     /**
-     * @return PagingSource of customers, ordered by last name.
+     * @return PagingSource of customers, ordered by id.
      */
-    @Query("SELECT * FROM customer ORDER BY mLastName ASC")
+    @Query("SELECT * FROM customer ORDER BY mId ASC")
     PagingSource<Integer, Customer> loadPagedAgeOrderPagingSource();
 
     /**
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/ItemPagingSource.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/ItemPagingSource.kt
index a8eb290..e7f4e4a 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/ItemPagingSource.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/ItemPagingSource.kt
@@ -18,7 +18,6 @@
 
 import android.graphics.Color
 import androidx.annotation.ColorInt
-import androidx.paging.ExperimentalPagingApi
 import androidx.paging.PagingSource
 import androidx.paging.PagingState
 import kotlinx.coroutines.delay
@@ -34,7 +33,6 @@
 
     private val generationId = sGenerationId++
 
-    @OptIn(ExperimentalPagingApi::class)
     override fun getRefreshKey(state: PagingState<Int, Item>): Int? = state.anchorPosition
 
     override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Item> =
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3Activity.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3Activity.kt
index bfd595f..a8288e4 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3Activity.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3Activity.kt
@@ -30,7 +30,6 @@
 import androidx.paging.integration.testapp.R
 import androidx.paging.map
 import androidx.recyclerview.widget.RecyclerView
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
@@ -50,7 +49,6 @@
         }
         // NOTE: lifecycleScope means we don't respect paused state here
         lifecycleScope.launch {
-            @OptIn(ExperimentalCoroutinesApi::class)
             viewModel.flow
                 .map { pagingData ->
                     pagingData.map { it.copy(text = "${it.text} - $orientationText") }
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/NetworkCustomerPagingSource.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/NetworkCustomerPagingSource.kt
index ae1744b..13709de 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/NetworkCustomerPagingSource.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/NetworkCustomerPagingSource.kt
@@ -16,7 +16,6 @@
 
 package androidx.paging.integration.testapp.v3room
 
-import androidx.paging.ExperimentalPagingApi
 import androidx.paging.PagingSource
 import androidx.paging.PagingState
 import androidx.paging.integration.testapp.room.Customer
@@ -28,20 +27,27 @@
 internal class NetworkCustomerPagingSource : PagingSource<Int, Customer>() {
     private fun createCustomer(i: Int): Customer {
         val customer = Customer()
-        customer.name = UUID.randomUUID().toString()
+        customer.name = "customer_$i"
         customer.lastName = "${"%04d".format(i)}_${UUID.randomUUID()}"
         return customer
     }
 
-    @OptIn(ExperimentalPagingApi::class)
-    override fun getRefreshKey(state: PagingState<Int, Customer>): Int? = state.anchorPosition
+    override fun getRefreshKey(
+        state: PagingState<Int, Customer>
+    ): Int? = state.anchorPosition?.let {
+        maxOf(0, it - 5)
+    }
 
     override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Customer> {
         val key = params.key ?: 0
-        val data = List(params.loadSize) { createCustomer(it + key) }
+        val data = if (params is LoadParams.Prepend) {
+            List(params.loadSize) { createCustomer(it + key - params.loadSize) }
+        } else {
+            List(params.loadSize) { createCustomer(it + key) }
+        }
         return LoadResult.Page(
             data = data,
-            prevKey = if (key > 0) key - 1 else null,
+            prevKey = if (key > 0) key else null,
             nextKey = key + data.size
         )
     }
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RemoteMediator.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RemoteMediator.kt
index 7ed4007..761a3d8 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RemoteMediator.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RemoteMediator.kt
@@ -36,7 +36,9 @@
         loadType: LoadType,
         state: PagingState<Int, Customer>
     ): MediatorResult {
-        if (loadType == LoadType.PREPEND) return MediatorResult.Success(false)
+        if (loadType == LoadType.PREPEND) {
+            return MediatorResult.Success(endOfPaginationReached = true)
+        }
 
         // TODO: Move this to be a more fully featured sample which demonstrated key translation
         //  between two types of PagingSources where the keys do not map 1:1.
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomActivity.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomActivity.kt
index 454b2d2..8bad4dc 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomActivity.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomActivity.kt
@@ -23,7 +23,6 @@
 import androidx.lifecycle.lifecycleScope
 import androidx.paging.integration.testapp.R
 import androidx.recyclerview.widget.RecyclerView
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.launch
 
@@ -39,7 +38,6 @@
         recyclerView.adapter = adapter
 
         lifecycleScope.launch {
-            @OptIn(ExperimentalCoroutinesApi::class)
             viewModel.flow.collectLatest {
                 adapter.submitData(it)
             }
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomAdapter.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomAdapter.kt
index 5e0f1bf..a1e2dfb 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomAdapter.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomAdapter.kt
@@ -40,7 +40,7 @@
     override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
         val item = getItem(position)
         if (item != null) {
-            (holder.itemView as TextView).text = item.lastName
+            (holder.itemView as TextView).text = item.name
             holder.itemView.setBackgroundColor(Color.BLUE)
         } else {
             (holder.itemView as TextView).setText(R.string.loading)
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomViewModel.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomViewModel.kt
index 27e4eea..8c84a91 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomViewModel.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3room/V3RoomViewModel.kt
@@ -21,6 +21,7 @@
 import androidx.arch.core.executor.ArchTaskExecutor
 import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.viewModelScope
+import androidx.paging.ExperimentalPagingApi
 import androidx.paging.Pager
 import androidx.paging.PagingConfig
 import androidx.paging.cachedIn
@@ -57,6 +58,7 @@
             .executeOnDiskIO { database.customerDao.removeAll() }
     }
 
+    @OptIn(ExperimentalPagingApi::class)
     val flow = Pager(
         PagingConfig(10),
         remoteMediator = V3RemoteMediator(
@@ -76,7 +78,7 @@
                         Customer().apply {
                             id = -1
                             name = "RIGHT ABOVE DIVIDER"
-                            lastName = "LAST NAME"
+                            lastName = "RIGHT ABOVE DIVIDER"
                         }
                     }
                 }
@@ -85,19 +87,21 @@
                         Customer().apply {
                             id = -2
                             name = "RIGHT BELOW DIVIDER"
-                            lastName = "LAST NAME"
+                            lastName = "RIGHT BELOW DIVIDER"
                         }
                     } else null
                 }
                 .insertHeaderItem(
                     Customer().apply {
                         id = Int.MIN_VALUE
+                        name = "HEADER"
                         lastName = "HEADER"
                     }
                 )
                 .insertFooterItem(
                     Customer().apply {
                         id = Int.MAX_VALUE
+                        name = "FOOTER"
                         lastName = "FOOTER"
                     }
                 )
diff --git a/paging/paging-compose/build.gradle b/paging/paging-compose/build.gradle
index 2d099c2..ef5d031 100644
--- a/paging/paging-compose/build.gradle
+++ b/paging/paging-compose/build.gradle
@@ -35,7 +35,7 @@
 
     implementation(KOTLIN_STDLIB)
     api projectOrArtifact(":compose:foundation:foundation")
-    api("androidx.paging:paging-common-ktx:3.0.0-alpha06")
+    api projectOrArtifact(":paging:paging-common")
 
     androidTestImplementation projectOrArtifact(":compose:ui:ui-test-junit4")
     androidTestImplementation projectOrArtifact(':internal-testutils-paging')
diff --git a/paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt b/paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
index 12200c8..f6b3ac0 100644
--- a/paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
+++ b/paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
@@ -89,8 +89,10 @@
             previousList: NullPaddedList<T>,
             newList: NullPaddedList<T>,
             newCombinedLoadStates: CombinedLoadStates,
-            lastAccessedIndex: Int
+            lastAccessedIndex: Int,
+            onListPresentable: () -> Unit,
         ): Int? {
+            onListPresentable()
             // TODO: This logic may be changed after the implementation of an async model which
             //  composes the offscreen elements
             recomposerPlaceholder.value++
@@ -166,7 +168,12 @@
      * A [CombinedLoadStates] object which represents the current loading state.
      */
     public var loadState: CombinedLoadStates by mutableStateOf(
-        CombinedLoadStates(InitialLoadStates)
+        CombinedLoadStates(
+            refresh = InitialLoadStates.refresh,
+            prepend = InitialLoadStates.prepend,
+            append = InitialLoadStates.append,
+            source = InitialLoadStates,
+        )
     )
         private set
 
diff --git a/paging/runtime/api/current.txt b/paging/runtime/api/current.txt
index 51899cf..21124c9 100644
--- a/paging/runtime/api/current.txt
+++ b/paging/runtime/api/current.txt
@@ -122,6 +122,7 @@
 
   public final class PagingLiveData {
     method public static <T> androidx.lifecycle.LiveData<androidx.paging.PagingData<T>> cachedIn(androidx.lifecycle.LiveData<androidx.paging.PagingData<T>>, androidx.lifecycle.Lifecycle lifecycle);
+    method public static <T> androidx.lifecycle.LiveData<androidx.paging.PagingData<T>> cachedIn(androidx.lifecycle.LiveData<androidx.paging.PagingData<T>>, androidx.lifecycle.ViewModel viewModel);
     method public static <T> androidx.lifecycle.LiveData<androidx.paging.PagingData<T>> cachedIn(androidx.lifecycle.LiveData<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
     method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> getLiveData(androidx.paging.Pager<Key,Value>);
   }
diff --git a/paging/runtime/api/public_plus_experimental_current.txt b/paging/runtime/api/public_plus_experimental_current.txt
index 51899cf..21124c9 100644
--- a/paging/runtime/api/public_plus_experimental_current.txt
+++ b/paging/runtime/api/public_plus_experimental_current.txt
@@ -122,6 +122,7 @@
 
   public final class PagingLiveData {
     method public static <T> androidx.lifecycle.LiveData<androidx.paging.PagingData<T>> cachedIn(androidx.lifecycle.LiveData<androidx.paging.PagingData<T>>, androidx.lifecycle.Lifecycle lifecycle);
+    method public static <T> androidx.lifecycle.LiveData<androidx.paging.PagingData<T>> cachedIn(androidx.lifecycle.LiveData<androidx.paging.PagingData<T>>, androidx.lifecycle.ViewModel viewModel);
     method public static <T> androidx.lifecycle.LiveData<androidx.paging.PagingData<T>> cachedIn(androidx.lifecycle.LiveData<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
     method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> getLiveData(androidx.paging.Pager<Key,Value>);
   }
diff --git a/paging/runtime/api/restricted_current.txt b/paging/runtime/api/restricted_current.txt
index 51899cf..21124c9 100644
--- a/paging/runtime/api/restricted_current.txt
+++ b/paging/runtime/api/restricted_current.txt
@@ -122,6 +122,7 @@
 
   public final class PagingLiveData {
     method public static <T> androidx.lifecycle.LiveData<androidx.paging.PagingData<T>> cachedIn(androidx.lifecycle.LiveData<androidx.paging.PagingData<T>>, androidx.lifecycle.Lifecycle lifecycle);
+    method public static <T> androidx.lifecycle.LiveData<androidx.paging.PagingData<T>> cachedIn(androidx.lifecycle.LiveData<androidx.paging.PagingData<T>>, androidx.lifecycle.ViewModel viewModel);
     method public static <T> androidx.lifecycle.LiveData<androidx.paging.PagingData<T>> cachedIn(androidx.lifecycle.LiveData<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
     method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> getLiveData(androidx.paging.Pager<Key,Value>);
   }
diff --git a/paging/runtime/build.gradle b/paging/runtime/build.gradle
index 9a80921..79c0ee1 100644
--- a/paging/runtime/build.gradle
+++ b/paging/runtime/build.gradle
@@ -26,14 +26,22 @@
     id("kotlin-android")
 }
 
+android {
+    defaultConfig {
+        multiDexEnabled true
+    }
+}
+
 dependencies {
     api(project(":paging:paging-common"))
     // Ensure that the -ktx dependency graph mirrors the Java dependency graph
     api(project(":paging:paging-common-ktx"))
 
-    api("androidx.lifecycle:lifecycle-runtime-ktx:2.2.0")
+
     api("androidx.lifecycle:lifecycle-livedata-ktx:2.2.0")
-    api("androidx.recyclerview:recyclerview:1.2.0-alpha04")
+    api("androidx.lifecycle:lifecycle-runtime-ktx:2.2.0")
+    api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0")
+    api("androidx.recyclerview:recyclerview:1.2.0-beta01")
     api(KOTLIN_STDLIB)
     api(KOTLIN_COROUTINES_ANDROID)
     implementation("androidx.core:core-ktx:1.2.0")
@@ -46,9 +54,11 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
+    androidTestImplementation(TRUTH)
     androidTestImplementation(KOTLIN_TEST)
     androidTestImplementation(KOTLIN_COROUTINES_TEST)
     androidTestImplementation(JUNIT)
+    androidTestImplementation(MULTIDEX)
 }
 
 androidx {
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
index 0435993..b0e3005 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
@@ -65,11 +65,16 @@
         data: List<V>,
         initialKey: Int
     ): PagedList<V> {
+        // unblock page loading thread to allow build to succeed
+        pageLoadingThread.autoRun = true
         return PagedList.Builder(TestPositionalDataSource(data), config)
             .setInitialKey(initialKey)
             .setNotifyExecutor(mainThread)
             .setFetchExecutor(pageLoadingThread)
             .build()
+            .also {
+                pageLoadingThread.autoRun = false
+            }
     }
 
     @Test
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagingDataDifferTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagingDataDifferTest.kt
index 6bb8c66..6f89fd1 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagingDataDifferTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagingDataDifferTest.kt
@@ -23,7 +23,6 @@
 import androidx.paging.ListUpdateEvent.Removed
 import androidx.paging.LoadState.Loading
 import androidx.paging.LoadState.NotLoading
-import androidx.paging.LoadType.REFRESH
 import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.ListUpdateCallback
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -133,9 +132,10 @@
         // empty previous list.
         assertEvents(
             listOf(
-                REFRESH to Loading,
-                REFRESH to NotLoading(endOfPaginationReached = false)
-            ).toCombinedLoadStatesLocal(),
+                localLoadStatesOf(),
+                localLoadStatesOf(refreshLocal = Loading),
+                localLoadStatesOf(refreshLocal = NotLoading(endOfPaginationReached = false)),
+            ),
             loadEvents
         )
         loadEvents.clear()
@@ -152,8 +152,8 @@
                 localLoadStatesOf(
                     refreshLocal = NotLoading(endOfPaginationReached = false),
                     prependLocal = NotLoading(endOfPaginationReached = true),
-                    appendLocal = NotLoading(endOfPaginationReached = true)
-                )
+                    appendLocal = NotLoading(endOfPaginationReached = true),
+                ),
             ),
             actual = loadEvents
         )
@@ -190,9 +190,10 @@
         // empty previous list.
         assertEvents(
             listOf(
-                REFRESH to Loading,
-                REFRESH to NotLoading(endOfPaginationReached = false)
-            ).toCombinedLoadStatesLocal(),
+                localLoadStatesOf(),
+                localLoadStatesOf(refreshLocal = Loading),
+                localLoadStatesOf(refreshLocal = NotLoading(endOfPaginationReached = false)),
+            ),
             loadEvents
         )
         loadEvents.clear()
@@ -209,8 +210,8 @@
                 localLoadStatesOf(
                     refreshLocal = NotLoading(endOfPaginationReached = false),
                     prependLocal = NotLoading(endOfPaginationReached = true),
-                    appendLocal = NotLoading(endOfPaginationReached = true)
-                )
+                    appendLocal = NotLoading(endOfPaginationReached = true),
+                ),
             ),
             actual = loadEvents
         )
@@ -510,48 +511,21 @@
 
         // Initial refresh
         advanceUntilIdle()
-        assertEquals(
-            CombinedLoadStates(
-                source = LoadStates(
-                    refresh = NotLoading(endOfPaginationReached = false),
-                    prepend = NotLoading(endOfPaginationReached = false),
-                    append = NotLoading(endOfPaginationReached = false)
-                )
-            ),
-            combinedLoadStates
-        )
+        assertEquals(localLoadStatesOf(), combinedLoadStates)
         assertEquals(10, itemCount)
         assertEquals(10, differ.itemCount)
 
         // Append
         differ.getItem(9)
         advanceUntilIdle()
-        assertEquals(
-            CombinedLoadStates(
-                source = LoadStates(
-                    refresh = NotLoading(endOfPaginationReached = false),
-                    prepend = NotLoading(endOfPaginationReached = false),
-                    append = NotLoading(endOfPaginationReached = false)
-                )
-            ),
-            combinedLoadStates
-        )
+        assertEquals(localLoadStatesOf(), combinedLoadStates)
         assertEquals(20, itemCount)
         assertEquals(20, differ.itemCount)
 
         // Prepend
         differ.getItem(0)
         advanceUntilIdle()
-        assertEquals(
-            CombinedLoadStates(
-                source = LoadStates(
-                    refresh = NotLoading(endOfPaginationReached = false),
-                    prepend = NotLoading(endOfPaginationReached = false),
-                    append = NotLoading(endOfPaginationReached = false)
-                )
-            ),
-            combinedLoadStates
-        )
+        assertEquals(localLoadStatesOf(), combinedLoadStates)
         assertEquals(30, itemCount)
         assertEquals(30, differ.itemCount)
 
@@ -584,52 +558,90 @@
 
             // Initial refresh
             advanceUntilIdle()
-            assertEquals(
-                CombinedLoadStates(
-                    source = LoadStates(
-                        refresh = NotLoading(endOfPaginationReached = false),
-                        prepend = NotLoading(endOfPaginationReached = false),
-                        append = NotLoading(endOfPaginationReached = false)
-                    )
-                ),
-                combinedLoadStates
-            )
+            assertEquals(localLoadStatesOf(), combinedLoadStates)
             assertEquals(10, itemCount)
             assertEquals(10, differ.itemCount)
 
             // Append
             differ.getItem(9)
             advanceUntilIdle()
-            assertEquals(
-                CombinedLoadStates(
-                    source = LoadStates(
-                        refresh = NotLoading(endOfPaginationReached = false),
-                        prepend = NotLoading(endOfPaginationReached = false),
-                        append = NotLoading(endOfPaginationReached = false)
-                    )
-                ),
-                combinedLoadStates
-            )
+            assertEquals(localLoadStatesOf(), combinedLoadStates)
             assertEquals(20, itemCount)
             assertEquals(20, differ.itemCount)
 
             // Prepend
             differ.getItem(0)
             advanceUntilIdle()
-            assertEquals(
-                CombinedLoadStates(
-                    source = LoadStates(
-                        refresh = NotLoading(endOfPaginationReached = false),
-                        prepend = NotLoading(endOfPaginationReached = false),
-                        append = NotLoading(endOfPaginationReached = false)
-                    )
-                ),
-                combinedLoadStates
-            )
+            assertEquals(localLoadStatesOf(), combinedLoadStates)
             assertEquals(30, itemCount)
             assertEquals(30, differ.itemCount)
 
             job.cancel()
         }
     }
+
+    @Test
+    fun listUpdateCallbackSynchronouslyUpdates() = testScope.runBlockingTest {
+        pauseDispatcher {
+            // Keep track of .snapshot() result within each ListUpdateCallback
+            val initialSnapshot: ItemSnapshotList<Int> = ItemSnapshotList(0, 0, emptyList())
+            var onInsertedSnapshot = initialSnapshot
+            var onRemovedSnapshot = initialSnapshot
+
+            val listUpdateCallback = object : ListUpdateCallback {
+                lateinit var differ: AsyncPagingDataDiffer<Int>
+
+                override fun onChanged(position: Int, count: Int, payload: Any?) {
+                    // TODO: Trigger this callback so we can assert state at this point as well
+                }
+
+                override fun onMoved(fromPosition: Int, toPosition: Int) {
+                    // TODO: Trigger this callback so we can assert state at this point as well
+                }
+
+                override fun onInserted(position: Int, count: Int) {
+                    onInsertedSnapshot = differ.snapshot()
+                }
+
+                override fun onRemoved(position: Int, count: Int) {
+                    onRemovedSnapshot = differ.snapshot()
+                }
+            }
+
+            val differ = AsyncPagingDataDiffer(
+                diffCallback = object : DiffUtil.ItemCallback<Int>() {
+                    override fun areContentsTheSame(oldItem: Int, newItem: Int): Boolean {
+                        return oldItem == newItem
+                    }
+
+                    override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean {
+                        return oldItem == newItem
+                    }
+                },
+                updateCallback = listUpdateCallback,
+                mainDispatcher = Dispatchers.Main,
+                workerDispatcher = Dispatchers.Main,
+            ).also {
+                listUpdateCallback.differ = it
+            }
+
+            // Initial insert; this only triggers onInserted
+            differ.submitData(PagingData.from(listOf(0)))
+            advanceUntilIdle()
+
+            val firstList = ItemSnapshotList(0, 0, listOf(0))
+            assertEquals(firstList, differ.snapshot())
+            assertEquals(firstList, onInsertedSnapshot)
+            assertEquals(initialSnapshot, onRemovedSnapshot)
+
+            // Switch item to 1; this triggers onInserted + onRemoved
+            differ.submitData(PagingData.from(listOf(1)))
+            advanceUntilIdle()
+
+            val secondList = ItemSnapshotList(0, 0, listOf(1))
+            assertEquals(secondList, differ.snapshot())
+            assertEquals(secondList, onInsertedSnapshot)
+            assertEquals(secondList, onRemovedSnapshot)
+        }
+    }
 }
\ No newline at end of file
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/StateRestorationTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/StateRestorationTest.kt
new file mode 100644
index 0000000..f7df7ee
--- /dev/null
+++ b/paging/runtime/src/androidTest/java/androidx/paging/StateRestorationTest.kt
@@ -0,0 +1,530 @@
+/*
+ * 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.paging
+
+import android.app.Application
+import android.content.Context
+import android.os.Parcelable
+import android.view.View
+import android.view.View.MeasureSpec.EXACTLY
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationPolicy.ALLOW
+import androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationPolicy.PREVENT
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.internal.ThreadSafeHeap
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestCoroutineDispatcher
+import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.withContext
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.coroutines.ContinuationInterceptor
+import kotlin.coroutines.CoroutineContext
+import kotlin.time.ExperimentalTime
+
+/**
+ * We are only capable of restoring state if one the two is valid:
+ * a) pager's flow is cached in the view model (only for config change)
+ * b) data source is counted and placeholders are enabled (both config change and app restart)
+ *
+ * Both of these cases actually work without using the initial key, except it is relatively
+ * slower in option B because we need to load all items from initial key to the required position.
+ *
+ * This test validates those two cases for now. For more complicated cases, we need some helper
+ * as developer needs to intervene to provide more information.
+ */
+@ExperimentalCoroutinesApi
+@ExperimentalTime
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class StateRestorationTest {
+    /**
+     * List of dispatchers we track in the test for idling + pushing execution.
+     * We have 3 dispatchers for more granular control:
+     * main, and background for pager.
+     * testScope for running tests.
+     */
+    private val trackedDispatchers = mutableListOf<TestCoroutineDispatcher>()
+
+    private val mainDispatcher = TestCoroutineDispatcher().track()
+    private val backgroundDispatcher = TestCoroutineDispatcher().track()
+    private val testScope = TestCoroutineScope().track()
+
+    /**
+     * A fake lifecycle scope for collections that get cancelled when we recreate the recyclerview.
+     */
+    private lateinit var lifecycleScope: TestCoroutineScope
+    private lateinit var recyclerView: TestRecyclerView
+    private lateinit var layoutManager: RestoreAwareLayoutManager
+    private lateinit var adapter: TestAdapter
+
+    /**
+     * tracks [this] dispatcher for idling control.
+     */
+    private fun TestCoroutineDispatcher.track() = apply {
+        trackedDispatchers.add(this)
+    }
+
+    /**
+     * tracks the dispatcher of this scope for idling control.
+     */
+    private fun TestCoroutineScope.track() = apply {
+        (this@track.coroutineContext[ContinuationInterceptor.Key] as TestCoroutineDispatcher)
+            .track()
+    }
+
+    @Before
+    fun init() {
+        createRecyclerView()
+    }
+
+    @Test
+    fun restoreState_withPlaceholders() {
+        runTest {
+            collectPagesAsync(
+                createPager(
+                    pageSize = 100,
+                    enablePlaceholders = true
+                ).flow
+            )
+            measureAndLayout()
+            val visible = recyclerView.captureSnapshot()
+            assertThat(visible).isNotEmpty()
+            scrollToPosition(50)
+            val expected = recyclerView.captureSnapshot()
+            saveAndRestore()
+            // make sure state is not restored before items are loaded
+            assertThat(
+                layoutManager.restoredState
+            ).isFalse()
+            backgroundDispatcher.pauseDispatcher()
+            collectPagesAsync(
+                createPager(
+                    pageSize = 10,
+                    enablePlaceholders = true
+                ).flow
+            )
+            measureAndLayout()
+            // background worker is blocked, still shouldn't restore state
+            assertThat(
+                layoutManager.restoredState
+            ).isFalse()
+            backgroundDispatcher.resumeDispatcher()
+            measureAndLayout()
+            assertThat(
+                layoutManager.restoredState
+            ).isTrue()
+            assertThat(
+                recyclerView.captureSnapshot()
+            ).containsExactlyElementsIn(
+                expected
+            )
+        }
+    }
+
+    @Test
+    fun restoreState_withoutPlaceholders_cachedIn() {
+        runTest {
+            val pager = createPager(
+                pageSize = 60,
+                enablePlaceholders = false
+            )
+            val cacheScope = TestCoroutineScope(Job()).track()
+            val cachedFlow = pager.flow.cachedIn(cacheScope)
+            collectPagesAsync(cachedFlow)
+            measureAndLayout()
+            // now scroll
+            scrollToPosition(50)
+            val snapshot = recyclerView.captureSnapshot()
+            saveAndRestore()
+            assertThat(
+                layoutManager.restoredState
+            ).isFalse()
+            collectPagesAsync(cachedFlow)
+            measureAndLayout()
+            assertThat(
+                layoutManager.restoredState
+            ).isTrue()
+            val restoredSnapshot = recyclerView.captureSnapshot()
+            assertThat(restoredSnapshot).containsExactlyElementsIn(snapshot)
+            cacheScope.cancel()
+        }
+    }
+
+    @Test
+    fun emptyNewPage_allowRestoration() {
+        // check that we don't block restoration indefinitely if new pager is empty.
+        runTest {
+            val pager = createPager(
+                pageSize = 60,
+                enablePlaceholders = true
+            )
+            collectPagesAsync(pager.flow)
+            measureAndLayout()
+            scrollToPosition(50)
+            saveAndRestore()
+            assertThat(
+                layoutManager.restoredState
+            ).isFalse()
+            val emptyPager = createPager(
+                pageSize = 10,
+                itemCount = 0,
+                enablePlaceholders = true
+            )
+            collectPagesAsync(emptyPager.flow)
+            measureAndLayout()
+            assertThat(
+                layoutManager.restoredState
+            ).isTrue()
+        }
+    }
+
+    @Test
+    fun userOverridesStateRestoration() {
+        runTest {
+            val pager = createPager(
+                pageSize = 40,
+                enablePlaceholders = true
+            )
+            collectPagesAsync(pager.flow)
+            measureAndLayout()
+            scrollToPosition(20)
+            val snapshot = recyclerView.captureSnapshot()
+            saveAndRestore()
+            val pager2 = createPager(
+                pageSize = 40,
+                enablePlaceholders = true
+            )
+            // when user calls prevent, we should not trigger state restoration even after we
+            // receive the first page
+            adapter.stateRestorationPolicy = PREVENT
+            collectPagesAsync(pager2.flow)
+            measureAndLayout()
+            assertThat(
+                layoutManager.restoredState
+            ).isFalse()
+            // make sure test did work as expected, that is, new items are loaded
+            assertThat(adapter.itemCount).isGreaterThan(0)
+            // now if user allows it, restoration should happen properly
+            adapter.stateRestorationPolicy = ALLOW
+            measureAndLayout()
+            assertThat(
+                layoutManager.restoredState
+            ).isTrue()
+            assertThat(recyclerView.captureSnapshot()).isEqualTo(snapshot)
+        }
+    }
+
+    private fun createRecyclerView() {
+        // cancel previous lifecycle if it exists
+        if (this::lifecycleScope.isInitialized) {
+            this.lifecycleScope.cancel()
+        }
+        lifecycleScope = TestCoroutineScope(
+            SupervisorJob() + mainDispatcher
+        ).track()
+        val context = ApplicationProvider.getApplicationContext<Application>()
+        recyclerView = TestRecyclerView(context)
+        recyclerView.itemAnimator = null
+        adapter = TestAdapter()
+        recyclerView.adapter = adapter
+        layoutManager = RestoreAwareLayoutManager(context)
+        recyclerView.layoutManager = layoutManager
+    }
+
+    private fun runPending() {
+        while (trackedDispatchers.any { it.isNotEmpty && it.isNotPaused }) {
+            trackedDispatchers.filter { it.isNotPaused }.forEach {
+                it.runCurrent()
+            }
+        }
+    }
+
+    private fun scrollToPosition(pos: Int) {
+        while (adapter.itemCount <= pos) {
+            val prevSize = adapter.itemCount
+            adapter.triggerItemLoad(prevSize - 1)
+            runPending()
+            // this might be an issue with dropping but it is not the case here
+            assertWithMessage("more items should be loaded")
+                .that(adapter.itemCount)
+                .isGreaterThan(prevSize)
+        }
+        runPending()
+        recyclerView.scrollToPosition(pos)
+        measureAndLayout()
+        val child = layoutManager.findViewByPosition(pos)
+        assertWithMessage("scrolled child $pos exists")
+            .that(child)
+            .isNotNull()
+
+        val vh = recyclerView.getChildViewHolder(child!!) as ItemViewHolder
+        assertWithMessage("scrolled child should be fully loaded")
+            .that(vh.item)
+            .isNotNull()
+    }
+
+    private fun measureAndLayout() {
+        runPending()
+        while (recyclerView.isLayoutRequested) {
+            measure()
+            layout()
+            runPending()
+        }
+    }
+
+    private fun measure() {
+        recyclerView.measure(EXACTLY or RV_WIDTH, EXACTLY or RV_HEIGHT)
+    }
+
+    private fun layout() {
+        recyclerView.layout(0, 0, 100, 200)
+    }
+
+    private fun saveAndRestore() {
+        val state = recyclerView.saveState()
+        createRecyclerView()
+        recyclerView.restoreState(state)
+        measureAndLayout()
+    }
+
+    private fun runTest(block: TestCoroutineScope.() -> Unit) {
+        testScope.runBlockingTest {
+            try {
+                this.block()
+            } finally {
+                runPending()
+                // always cancel the lifecycle scope to ensure any collection there ends
+                if (this@StateRestorationTest::lifecycleScope.isInitialized) {
+                    lifecycleScope.cancel()
+                }
+            }
+        }
+    }
+
+    /**
+     * collects pages in the lifecycle scope and sends them to the adapter
+     */
+    private fun collectPagesAsync(
+        flow: Flow<PagingData<Item>>
+    ) {
+        val targetAdapter = adapter
+        lifecycleScope.launch {
+            flow.collectLatest {
+                targetAdapter.submitData(it)
+            }
+        }
+    }
+
+    private fun createPager(
+        pageSize: Int,
+        enablePlaceholders: Boolean,
+        itemCount: Int = 100,
+        initialKey: Int? = null
+    ): Pager<Int, Item> {
+        return Pager(
+            config = PagingConfig(
+                pageSize = pageSize,
+                enablePlaceholders = enablePlaceholders,
+            ),
+            initialKey = initialKey,
+            pagingSourceFactory = {
+                ItemPagingSource(
+                    context = backgroundDispatcher,
+                    items = (0 until itemCount).map { Item(it) }
+                )
+            }
+        )
+    }
+
+    /**
+     * Returns the list of all visible items in the recyclerview including their locations.
+     */
+    private fun RecyclerView.captureSnapshot(): List<PositionSnapshot> {
+        return (0 until childCount).mapNotNull {
+            val child = getChildAt(it)
+            // if child is not visible, ignore it as RV might have extra views around the visible
+            // area.
+            if (child.top >= height || child.bottom <= 0) {
+                // not visible, ignore
+                null
+            } else {
+                val vh = getChildViewHolder(child)
+                (vh as ItemViewHolder).captureSnapshot()
+            }
+        }
+    }
+
+    class ItemView(context: Context) : View(context)
+
+    class ItemViewHolder(context: Context) : RecyclerView.ViewHolder(ItemView(context)) {
+        var item: Item? = null
+            private set
+
+        init {
+            itemView.layoutParams = RecyclerView.LayoutParams(0, 0)
+        }
+
+        fun captureSnapshot(): PositionSnapshot {
+            val item = checkNotNull(item)
+            return PositionSnapshot(
+                item = item,
+                top = itemView.top,
+                bottom = itemView.bottom
+            )
+        }
+
+        fun bindTo(item: Item?) {
+            this.item = item
+            // setting placeholder height to 0 creates a weird jumping bug, investigate
+            itemView.layoutParams.height = item?.height ?: RV_HEIGHT / 10
+        }
+    }
+
+    /**
+     * Checks whether a [TestCoroutineDispatcher] has any pending actions using reflection :)
+     */
+    @OptIn(InternalCoroutinesApi::class)
+    private val TestCoroutineDispatcher.isNotEmpty: Boolean
+        get() {
+            this@isNotEmpty::class.java.getDeclaredField("queue").let {
+                it.isAccessible = true
+                val heap = it.get(this) as ThreadSafeHeap<*>
+                return !heap.isEmpty
+            }
+        }
+
+    /**
+     * Checks whether a [TestCoroutineDispatcher] is paused or not using reflection.
+     */
+    private val TestCoroutineDispatcher.isNotPaused: Boolean
+        get() {
+            this@isNotPaused::class.java.getDeclaredField("dispatchImmediately").let {
+                it.isAccessible = true
+                return it.get(this) as Boolean
+            }
+        }
+
+    data class Item(
+        val id: Int,
+        val height: Int = (RV_HEIGHT / 10) + (1 + (id % 10))
+    ) {
+        companion object {
+            val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Item>() {
+                override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
+                    return oldItem.id == newItem.id
+                }
+
+                override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
+                    return oldItem == newItem
+                }
+            }
+        }
+    }
+
+    inner class TestAdapter : PagingDataAdapter<Item, ItemViewHolder>(
+        diffCallback = Item.DIFF_CALLBACK,
+        mainDispatcher = mainDispatcher,
+        workerDispatcher = backgroundDispatcher
+    ) {
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
+            return ItemViewHolder(parent.context)
+        }
+
+        override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
+            holder.bindTo(getItem(position))
+        }
+
+        fun triggerItemLoad(pos: Int) = super.getItem(pos)
+    }
+
+    class ItemPagingSource(
+        private val context: CoroutineContext,
+        private val items: List<Item>
+    ) : PagingSource<Int, Item>() {
+        override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Item> {
+            return withContext(context) {
+                val key = params.key ?: 0
+                val isPrepend = params is LoadParams.Prepend
+                val start = if (isPrepend) key - params.loadSize + 1 else key
+                val end = if (isPrepend) key + 1 else key + params.loadSize
+
+                LoadResult.Page(
+                    data = items.subList(maxOf(0, start), minOf(end, items.size)),
+                    prevKey = if (start > 0) start - 1 else null,
+                    nextKey = if (end < items.size) end else null,
+                    itemsBefore = maxOf(0, start),
+                    itemsAfter = maxOf(0, items.size - end)
+                )
+            }
+        }
+    }
+
+    /**
+     * Snapshot of an item in RecyclerView.
+     */
+    data class PositionSnapshot(
+        val item: Item,
+        val top: Int,
+        val bottom: Int
+    )
+
+    /**
+     * RecyclerView class that allows saving and restoring state.
+     */
+    class TestRecyclerView(context: Context) : RecyclerView(context) {
+        fun restoreState(state: Parcelable?) {
+            super.onRestoreInstanceState(state)
+        }
+
+        fun saveState(): Parcelable? {
+            return super.onSaveInstanceState()
+        }
+    }
+
+    /**
+     * A layout manager that tracks whether state is restored or not so that we can assert on it.
+     */
+    class RestoreAwareLayoutManager(context: Context) : LinearLayoutManager(context) {
+        var restoredState = false
+        override fun onRestoreInstanceState(state: Parcelable?) {
+            super.onRestoreInstanceState(state)
+            restoredState = true
+        }
+    }
+
+    companion object {
+        private const val RV_HEIGHT = 200
+        private const val RV_WIDTH = 100
+    }
+}
\ No newline at end of file
diff --git a/paging/runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt b/paging/runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
index 2a5de36..8cb757b 100644
--- a/paging/runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
+++ b/paging/runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
@@ -71,15 +71,18 @@
             previousList: NullPaddedList<T>,
             newList: NullPaddedList<T>,
             newCombinedLoadStates: CombinedLoadStates,
-            lastAccessedIndex: Int
+            lastAccessedIndex: Int,
+            onListPresentable: () -> Unit,
         ) = when {
             // fast path for no items -> some items
             previousList.size == 0 -> {
+                onListPresentable()
                 differCallback.onInserted(0, newList.size)
                 null
             }
             // fast path for some items -> no items
             newList.size == 0 -> {
+                onListPresentable()
                 differCallback.onRemoved(0, previousList.size)
                 null
             }
@@ -87,6 +90,7 @@
                 val diffResult = withContext(workerDispatcher) {
                     previousList.computeDiff(newList, diffCallback)
                 }
+                onListPresentable()
                 previousList.dispatchDiff(updateCallback, newList, diffResult)
                 previousList.transformAnchorIndex(
                     diffResult = diffResult,
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt b/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
index 32bfa57..ab56fae 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
@@ -41,10 +41,12 @@
     private val fetchDispatcher: CoroutineDispatcher
 ) : LiveData<PagedList<Value>>(
     InitialPagedList(
-        pagingSourceFactory(),
-        coroutineScope,
-        config,
-        initialKey
+        pagingSource = pagingSourceFactory(),
+        coroutineScope = coroutineScope,
+        notifyDispatcher = notifyDispatcher,
+        backgroundDispatcher = fetchDispatcher,
+        config = config,
+        initialLastKey = initialKey
     )
 ) {
     private var currentData: PagedList<Value>
diff --git a/paging/runtime/src/main/java/androidx/paging/PagingDataAdapter.kt b/paging/runtime/src/main/java/androidx/paging/PagingDataAdapter.kt
index 3f19e4b..82892f3 100644
--- a/paging/runtime/src/main/java/androidx/paging/PagingDataAdapter.kt
+++ b/paging/runtime/src/main/java/androidx/paging/PagingDataAdapter.kt
@@ -46,6 +46,14 @@
  * compute fine grained updates as updated content in the form of new PagingData objects are
  * received.
  *
+ * *State Restoration*: To be able to restore [RecyclerView] state (e.g. scroll position) after a
+ * configuration change / application recreate, [PagingDataAdapter] calls
+ * [RecyclerView.Adapter.setStateRestorationPolicy] with
+ * [RecyclerView.Adapter.StateRestorationPolicy.PREVENT] upon initialization and waits for the
+ * first page to load before allowing state restoration.
+ * Any other call to [RecyclerView.Adapter.setStateRestorationPolicy] by the application will
+ * disable this logic and will rely on the user set value.
+ *
  * @sample androidx.paging.samples.pagingDataAdapterSample
  */
 abstract class PagingDataAdapter<T : Any, VH : RecyclerView.ViewHolder> @JvmOverloads constructor(
@@ -53,6 +61,41 @@
     mainDispatcher: CoroutineDispatcher = Dispatchers.Main,
     workerDispatcher: CoroutineDispatcher = Dispatchers.Default
 ) : RecyclerView.Adapter<VH>() {
+
+    init {
+        super.setStateRestorationPolicy(StateRestorationPolicy.PREVENT)
+        // prevent state restoration and then watch for the first insert event.
+        // differ calls this with the inserted page even when it is empty, which is what we want
+        // here
+        @Suppress("LeakingThis")
+        registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
+            override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
+                considerAllowingStateRestoration()
+                unregisterAdapterDataObserver(this)
+                super.onItemRangeInserted(positionStart, itemCount)
+            }
+
+            private fun considerAllowingStateRestoration() {
+                if (stateRestorationPolicy == StateRestorationPolicy.PREVENT &&
+                    !userSetRestorationPolicy
+                ) {
+                    this@PagingDataAdapter.setStateRestorationPolicy(StateRestorationPolicy.ALLOW)
+                }
+            }
+        })
+    }
+
+    /**
+     * Track whether developer called [setStateRestorationPolicy] or not to decide whether the
+     * automated state restoration should apply or not.
+     */
+    private var userSetRestorationPolicy = false
+
+    override fun setStateRestorationPolicy(strategy: StateRestorationPolicy) {
+        userSetRestorationPolicy = true
+        super.setStateRestorationPolicy(strategy)
+    }
+
     private val differ = AsyncPagingDataDiffer(
         diffCallback = diffCallback,
         updateCallback = AdapterListUpdateCallback(this),
diff --git a/paging/runtime/src/main/java/androidx/paging/PagingLiveData.kt b/paging/runtime/src/main/java/androidx/paging/PagingLiveData.kt
index d4f549f..caed0dd 100644
--- a/paging/runtime/src/main/java/androidx/paging/PagingLiveData.kt
+++ b/paging/runtime/src/main/java/androidx/paging/PagingLiveData.kt
@@ -20,9 +20,11 @@
 
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LiveData
+import androidx.lifecycle.ViewModel
 import androidx.lifecycle.asFlow
 import androidx.lifecycle.asLiveData
 import androidx.lifecycle.coroutineScope
+import androidx.lifecycle.viewModelScope
 import kotlinx.coroutines.CoroutineScope
 
 /**
@@ -37,7 +39,7 @@
  *
  * [cachedIn] multicasts pages loaded and transformed by a [PagingData], allowing multiple
  * observers on the same instance of [PagingData] to receive the same events, avoiding redundant
- * work, but comes at the cost of buffering those events in memory.
+ * work, but comes at the cost of buffering those pages in memory.
  *
  * Calling [cachedIn] is required to allow calling
  * [submitData][androidx.paging.AsyncPagingDataAdapter] on the same instance of [PagingData]
@@ -51,11 +53,30 @@
     .asLiveData()
 
 /**
+ * Operator which caches a [LiveData] of [PagingData] within the scope of a [ViewModel].
+ *
+ * [cachedIn] multicasts pages loaded and transformed by a [PagingData], allowing multiple
+ * observers on the same instance of [PagingData] to receive the same events, avoiding redundant
+ * work, but comes at the cost of buffering those pages in memory.
+ *
+ * Calling [cachedIn] is required to allow calling
+ * [submitData][androidx.paging.AsyncPagingDataAdapter] on the same instance of [PagingData]
+ * emitted by [Pager] or any of its transformed derivatives, as reloading data from scratch on the
+ * same generation of [PagingData] is an unsupported operation.
+ *
+ * @param viewModel The [ViewModel] whose [viewModelScope] will dictate how long the page
+ * cache will be kept alive.
+ */
+fun <T : Any> LiveData<PagingData<T>>.cachedIn(viewModel: ViewModel) = asFlow()
+    .cachedIn(viewModel.viewModelScope)
+    .asLiveData()
+
+/**
  * Operator which caches a [LiveData] of [PagingData] within a [CoroutineScope].
  *
  * [cachedIn] multicasts pages loaded and transformed by a [PagingData], allowing multiple
  * observers on the same instance of [PagingData] to receive the same events, avoiding redundant
- * work, but comes at the cost of buffering those events in memory.
+ * work, but comes at the cost of buffering those pages in memory.
  *
  * Calling [cachedIn] is required to allow calling
  * [submitData][androidx.paging.AsyncPagingDataAdapter] on the same instance of [PagingData]
diff --git a/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.kt b/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.kt
index 1a8211d..2959747 100644
--- a/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.kt
+++ b/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.kt
@@ -356,10 +356,12 @@
 
         init {
             currentData = InitialPagedList(
-                pagingSourceFactory(),
-                GlobalScope,
-                config,
-                initialLoadKey
+                pagingSource = pagingSourceFactory(),
+                coroutineScope = GlobalScope,
+                notifyDispatcher = notifyDispatcher,
+                backgroundDispatcher = fetchDispatcher,
+                config = config,
+                initialLastKey = initialLoadKey
             )
             currentData.setRetryCallback(refreshRetryCallback)
         }
diff --git a/paging/rxjava2/src/main/java/androidx/paging/rxjava2/PagingRx.kt b/paging/rxjava2/src/main/java/androidx/paging/rxjava2/PagingRx.kt
index 4c44f6f..14c8517 100644
--- a/paging/rxjava2/src/main/java/androidx/paging/rxjava2/PagingRx.kt
+++ b/paging/rxjava2/src/main/java/androidx/paging/rxjava2/PagingRx.kt
@@ -57,7 +57,7 @@
  *
  * [cachedIn] multicasts pages loaded and transformed by a [PagingData], allowing multiple
  * observers on the same instance of [PagingData] to receive the same events, avoiding redundant
- * work, but comes at the cost of buffering those events in memory.
+ * work, but comes at the cost of buffering those pages in memory.
  *
  * Calling [cachedIn] is required to allow calling
  * [submitData][androidx.paging.AsyncPagingDataAdapter] on the same instance of [PagingData]
@@ -82,7 +82,7 @@
  *
  * [cachedIn] multicasts pages loaded and transformed by a [PagingData], allowing multiple
  * observers on the same instance of [PagingData] to receive the same events, avoiding redundant
- * work, but comes at the cost of buffering those events in memory.
+ * work, but comes at the cost of buffering those pages in memory.
  *
  * Calling [cachedIn] is required to allow calling
  * [submitData][androidx.paging.AsyncPagingDataAdapter] on the same instance of [PagingData]
diff --git a/paging/rxjava3/src/main/java/androidx/paging/rxjava3/PagingRx.kt b/paging/rxjava3/src/main/java/androidx/paging/rxjava3/PagingRx.kt
index 3d8a680..24e84fb 100644
--- a/paging/rxjava3/src/main/java/androidx/paging/rxjava3/PagingRx.kt
+++ b/paging/rxjava3/src/main/java/androidx/paging/rxjava3/PagingRx.kt
@@ -57,7 +57,7 @@
  *
  * [cachedIn] multicasts pages loaded and transformed by a [PagingData], allowing multiple
  * observers on the same instance of [PagingData] to receive the same events, avoiding redundant
- * work, but comes at the cost of buffering those events in memory.
+ * work, but comes at the cost of buffering those pages in memory.
  *
  * Calling [cachedIn] is required to allow calling
  * [submitData][androidx.paging.AsyncPagingDataAdapter] on the same instance of [PagingData]
@@ -82,7 +82,7 @@
  *
  * [cachedIn] multicasts pages loaded and transformed by a [PagingData], allowing multiple
  * observers on the same instance of [PagingData] to receive the same events, avoiding redundant
- * work, but comes at the cost of buffering those events in memory.
+ * work, but comes at the cost of buffering those pages in memory.
  *
  * Calling [cachedIn] is required to allow calling
  * [submitData][androidx.paging.AsyncPagingDataAdapter] on the same instance of [PagingData]
diff --git a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/BaseInstrumentationTestCase.java b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/BaseInstrumentationTestCase.java
index f89a8f1..9cdcd79 100644
--- a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/BaseInstrumentationTestCase.java
+++ b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/BaseInstrumentationTestCase.java
@@ -21,18 +21,19 @@
 import android.app.Activity;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 public abstract class BaseInstrumentationTestCase<A extends Activity> {
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<A> mActivityTestRule;
+    public final androidx.test.rule.ActivityTestRule<A> mActivityTestRule;
 
+    @SuppressWarnings("deprecation")
     protected BaseInstrumentationTestCase(Class<A> activityClass) {
-        mActivityTestRule = new ActivityTestRule<A>(activityClass);
+        mActivityTestRule = new androidx.test.rule.ActivityTestRule<A>(activityClass);
     }
 
     protected static void assertFuzzyEquals(String description, float expected, float actual) {
diff --git a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentDynamicLayoutTest.java b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentDynamicLayoutTest.java
index ca4efd3..2e38fb8 100755
--- a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentDynamicLayoutTest.java
+++ b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentDynamicLayoutTest.java
@@ -41,6 +41,7 @@
 /**
  * Test cases to verify that percent layouts properly account for their own paddings.
  */
+@SuppressWarnings("deprecation")
 @LargeTest
 public class PercentDynamicLayoutTest
         extends BaseInstrumentationTestCase<PercentDynamicLayoutActivity> {
diff --git a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentFrameTest.java b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentFrameTest.java
index c88e063..f4bf9ee 100644
--- a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentFrameTest.java
+++ b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentFrameTest.java
@@ -28,6 +28,7 @@
 import org.junit.Before;
 import org.junit.Test;
 
+@SuppressWarnings("deprecation")
 @LargeTest
 public class PercentFrameTest extends BaseInstrumentationTestCase<TestFrameActivity> {
     private PercentFrameLayout mPercentFrameLayout;
diff --git a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentRelativeRtlTest.java b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentRelativeRtlTest.java
index bc51a0c..640be5b 100644
--- a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentRelativeRtlTest.java
+++ b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentRelativeRtlTest.java
@@ -94,6 +94,7 @@
  * the core classes, but rather just provide a translation layer between percentage-based values
  * and pixel-based ones.
  */
+@SuppressWarnings("deprecation")
 @LargeTest
 public class PercentRelativeRtlTest extends BaseInstrumentationTestCase<TestRelativeRtlActivity> {
     private PercentRelativeLayout mPercentRelativeLayout;
diff --git a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentRelativeTest.java b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentRelativeTest.java
index b821678..ffbf0dbb 100644
--- a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentRelativeTest.java
+++ b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/PercentRelativeTest.java
@@ -55,6 +55,7 @@
  *     <li>Center child (marked with C) - margin (all sides) from the other four children.</li>
  * </ul>
  */
+@SuppressWarnings("deprecation")
 @LargeTest
 public class PercentRelativeTest extends BaseInstrumentationTestCase<TestRelativeActivity> {
     private PercentRelativeLayout mPercentRelativeLayout;
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index 03609a8..0c651335 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -28,7 +28,7 @@
 androidx.enableDocumentation=false
 # Disable coverage
 androidx.coverageEnabled=false
-androidx.playground.snapshotBuildId=6990786
+androidx.playground.snapshotBuildId=7012196
 androidx.playground.metalavaBuildId=6990868
 androidx.playground.dokkaBuildId=6915080
 androidx.studio.type=playground
diff --git a/preference/preference/src/androidTest/java/androidx/preference/tests/EditTextPreferenceTest.java b/preference/preference/src/androidTest/java/androidx/preference/tests/EditTextPreferenceTest.java
index 9091896..4f2d7f0 100644
--- a/preference/preference/src/androidTest/java/androidx/preference/tests/EditTextPreferenceTest.java
+++ b/preference/preference/src/androidTest/java/androidx/preference/tests/EditTextPreferenceTest.java
@@ -38,7 +38,6 @@
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -52,9 +51,10 @@
 @RunWith(AndroidJUnit4.class)
 public class EditTextPreferenceTest {
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<PreferenceTestHelperActivity> mActivityRule =
-            new ActivityTestRule<>(PreferenceTestHelperActivity.class);
+    public androidx.test.rule.ActivityTestRule<PreferenceTestHelperActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(PreferenceTestHelperActivity.class);
 
     private static final String PREFERENCE = "preference";
 
diff --git a/preference/preference/src/androidTest/java/androidx/preference/tests/ListPreferenceSummaryTest.java b/preference/preference/src/androidTest/java/androidx/preference/tests/ListPreferenceSummaryTest.java
index 6fae995..0f5dd8f 100644
--- a/preference/preference/src/androidTest/java/androidx/preference/tests/ListPreferenceSummaryTest.java
+++ b/preference/preference/src/androidTest/java/androidx/preference/tests/ListPreferenceSummaryTest.java
@@ -69,6 +69,7 @@
         assertTrue(preference.getSummary() instanceof String);
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     @UiThreadTest
     public void styledSummary() {
diff --git a/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceComparisonCallbackTest.java b/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceComparisonCallbackTest.java
index 31a12b4..f36c692 100644
--- a/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceComparisonCallbackTest.java
+++ b/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceComparisonCallbackTest.java
@@ -260,6 +260,7 @@
                 mComparisonCallback.arePreferenceContentsTheSame(dropdown1, dropdown2));
     }
 
+    @SuppressWarnings("deprecation")
     private static class ComparisonDrawable extends Drawable {
 
         private final int mId;
diff --git a/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceCopyingTest.java b/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceCopyingTest.java
index d72d608..a8cb723 100644
--- a/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceCopyingTest.java
+++ b/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceCopyingTest.java
@@ -41,7 +41,6 @@
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -55,9 +54,10 @@
 @RunWith(AndroidJUnit4.class)
 public class PreferenceCopyingTest {
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<PreferenceTestHelperActivity> mActivityRule =
-            new ActivityTestRule<>(PreferenceTestHelperActivity.class);
+    public androidx.test.rule.ActivityTestRule<PreferenceTestHelperActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(PreferenceTestHelperActivity.class);
 
     private static final String COPY_BUTTON = "Copy";
     private static final String PREFERENCE_1 = "Preference 1";
diff --git a/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceDataStoreTest.java b/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceDataStoreTest.java
index 9ea37b6..17f8d75 100644
--- a/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceDataStoreTest.java
+++ b/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceDataStoreTest.java
@@ -404,6 +404,7 @@
         putStringSetTestCommon();
     }
 
+    @SuppressWarnings({"deprecation", "unchecked"})
     private void putStringSetTestCommon() {
         Set<String> testSet = Collections.singleton(TEST_STR);
 
diff --git a/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceVisibilityTest.java b/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceVisibilityTest.java
index f96d68c..d8869b7 100644
--- a/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceVisibilityTest.java
+++ b/preference/preference/src/androidTest/java/androidx/preference/tests/PreferenceVisibilityTest.java
@@ -41,7 +41,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -57,9 +56,10 @@
 @RunWith(AndroidJUnit4.class)
 public class PreferenceVisibilityTest {
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public ActivityTestRule<PreferenceTestHelperActivity> mActivityRule =
-            new ActivityTestRule<>(PreferenceTestHelperActivity.class);
+    public androidx.test.rule.ActivityTestRule<PreferenceTestHelperActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(PreferenceTestHelperActivity.class);
 
     private static final String CATEGORY = "Category";
     private static final String DEFAULT = "Default";
diff --git a/recyclerview/README.md b/recyclerview/README.md
index 6648c7b..71fb426 100644
--- a/recyclerview/README.md
+++ b/recyclerview/README.md
@@ -6,7 +6,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/recyclerview)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/recyclerview/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/recyclerview/)
 
 [Reference documentation](https://developer.android.com/reference/androidx/classes.html)
 
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java
index 85efcd3..7d90015 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java
@@ -16,8 +16,7 @@
 
 package androidx.recyclerview.selection;
 
-import static junit.framework.Assert.assertEquals;
-
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java
index 7a6e171..249463c 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java
@@ -219,6 +219,7 @@
         assertEquals(0, mModel.getPositionNearestOrigin());
     }
 
+    @SuppressWarnings("unchecked")
     private void initData(final int numChildren, int numColumns) {
         mHost = new TestHost(numChildren, numColumns);
         mAdapter = new TestAdapter() {
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java
index 425bcc6..11211d4 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java
@@ -71,14 +71,14 @@
         mSelection = new SelectionProbe(mSelectionMgr);
 
         mMouseCallbacks = new TestOnContextClickListener();
-        mActivationCallbacks = new TestOnItemActivatedListener();
-        mFocusCallbacks = new TestFocusDelegate();
+        mActivationCallbacks = new TestOnItemActivatedListener<>();
+        mFocusCallbacks = new TestFocusDelegate<>();
 
-        mInputDelegate = new MouseInputHandler(
+        mInputDelegate = new MouseInputHandler<>(
                 mSelectionMgr,
-                new TestItemKeyProvider(
+                new TestItemKeyProvider<>(
                         ItemKeyProvider.SCOPE_MAPPED,
-                        new TestAdapter(TestData.createStringData(100))),
+                        new TestAdapter<>(TestData.createStringData(100))),
                 mDetailsLookup,
                 mMouseCallbacks,
                 mActivationCallbacks,
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandler_RangeTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandler_RangeTest.java
index 825d45f..a3c50e2 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandler_RangeTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandler_RangeTest.java
@@ -50,6 +50,7 @@
     private TestFocusDelegate<String> mFocusCallbacks;
     private TestItemDetailsLookup mDetailsLookup;
 
+    @SuppressWarnings("unchecked")
     @Before
     public void setUp() {
         SelectionTracker<String> selectionMgr =
@@ -64,9 +65,9 @@
 
         mInputDelegate = new MouseInputHandler<>(
                 selectionMgr,
-                new TestItemKeyProvider(
+                new TestItemKeyProvider<>(
                         ItemKeyProvider.SCOPE_MAPPED,
-                        new TestAdapter(TestData.createStringData(100))),
+                        new TestAdapter<>(TestData.createStringData(100))),
                 mDetailsLookup,
                 mouseCallbacks,
                 activationCallbacks,
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/SelectionTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/SelectionTest.java
index fa37577..a9a8b75 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/SelectionTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/SelectionTest.java
@@ -191,6 +191,7 @@
         assertTrue(err, mSelection.contains(id));
     }
 
+    @SuppressWarnings("unchecked")
     public static <E> Set<E> newSet(E... elements) {
         HashSet<E> set = new HashSet<>(elements.length);
         Collections.addAll(set, elements);
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/StableIdKeyProviderTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/StableIdKeyProviderTest.java
index 92089428..5d218e7 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/StableIdKeyProviderTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/StableIdKeyProviderTest.java
@@ -160,6 +160,7 @@
     }
 
     private static final class StableIdTestAdapter extends TestAdapter<Long> {
+        @SuppressWarnings("unchecked")
         StableIdTestAdapter() {
             super(Collections.EMPTY_LIST, true);
         }
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index a44f788..4e67cdc 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -60,4 +60,5 @@
     mavenGroup = LibraryGroups.RECYCLERVIEW
     inceptionYear = "2014"
     description = "Android Support RecyclerView"
+    failOnDeprecationWarnings = false
 }
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/ItemAnimatorTestDouble.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/ItemAnimatorTestDouble.java
index 4d9a546..03cd284 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/ItemAnimatorTestDouble.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/ItemAnimatorTestDouble.java
@@ -44,11 +44,11 @@
 
     static final long TIMEOUT_SECOND = 10;
 
-    ArrayList<RecyclerView.ViewHolder> mAdds = new ArrayList();
-    ArrayList<RecyclerView.ViewHolder> mRemoves = new ArrayList();
-    ArrayList<RecyclerView.ViewHolder> mMoves = new ArrayList();
-    ArrayList<RecyclerView.ViewHolder> mChangesOld = new ArrayList();
-    ArrayList<RecyclerView.ViewHolder> mChangesNew = new ArrayList();
+    ArrayList<RecyclerView.ViewHolder> mAdds = new ArrayList<>();
+    ArrayList<RecyclerView.ViewHolder> mRemoves = new ArrayList<>();
+    ArrayList<RecyclerView.ViewHolder> mMoves = new ArrayList<>();
+    ArrayList<RecyclerView.ViewHolder> mChangesOld = new ArrayList<>();
+    ArrayList<RecyclerView.ViewHolder> mChangesNew = new ArrayList<>();
 
     @Retention(CLASS)
     @Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD})
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityLifecycleTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityLifecycleTest.java
index c9c0be9..9000986 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityLifecycleTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityLifecycleTest.java
@@ -217,7 +217,7 @@
         layoutManager.waitForLayout(1);
 
         assertEquals(layoutCount, recyclerView.getChildCount());
-        final ArrayList<View> children = new ArrayList();
+        final ArrayList<View> children = new ArrayList<>();
         mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
@@ -306,7 +306,7 @@
         // let all items go to recycler pool
         layoutManager.expectLayouts(1);
         layoutCount[0] = 0;
-        adapter.resetItemsTo(new ArrayList());
+        adapter.resetItemsTo(new ArrayList<Item>());
         layoutManager.waitForLayout(1);
         assertEquals(0, recyclerView.getChildCount());
         assertEquals(firstPassLayoutCount, recyclerView.getRecycledViewPool()
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewBasicTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewBasicTest.java
index 3e11b02..a736765 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewBasicTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewBasicTest.java
@@ -58,6 +58,7 @@
 import java.util.List;
 import java.util.UUID;
 
+@SuppressWarnings("unchecked")
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class RecyclerViewBasicTest {
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewCacheTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewCacheTest.java
index 330f659..0aa4e29 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewCacheTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewCacheTest.java
@@ -61,6 +61,7 @@
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
+@SuppressWarnings("unchecked")
 @MediumTest
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 @RunWith(AndroidJUnit4.class)
diff --git a/resourceinspection/OWNERS b/resourceinspection/OWNERS
new file mode 100644
index 0000000..9878e82
--- /dev/null
+++ b/resourceinspection/OWNERS
@@ -0,0 +1 @@
+emberrose@google.com
diff --git a/resourceinspection/resourceinspection-processor/build.gradle b/resourceinspection/resourceinspection-processor/build.gradle
new file mode 100644
index 0000000..639f6e5
--- /dev/null
+++ b/resourceinspection/resourceinspection-processor/build.gradle
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryType
+import androidx.build.LibraryVersions
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+}
+
+dependencies {
+    implementation(KOTLIN_STDLIB)
+}
+
+androidx {
+    name = "Android Resource Inspection - Annotation Processor"
+    type = LibraryType.ANNOTATION_PROCESSOR
+    mavenGroup = LibraryGroups.RESOURCEINSPECTION
+    inceptionYear = "2020"
+    description = "Annotation processors for Android resource and layout inspection"
+}
diff --git a/room/compiler-processing-testing/build.gradle b/room/compiler-processing-testing/build.gradle
index 0edce59..b3a42f6 100644
--- a/room/compiler-processing-testing/build.gradle
+++ b/room/compiler-processing-testing/build.gradle
@@ -26,12 +26,19 @@
 
 dependencies {
     implementation("androidx.annotation:annotation:1.1.0")
-    implementation(project(":room:room-compiler-processing"))
+    api(project(":room:room-compiler-processing"))
     implementation(KOTLIN_STDLIB)
     implementation(KOTLIN_KSP_API)
-    testImplementation(KOTLIN_KSP)
+    implementation(KOTLIN_STDLIB_JDK8) // KSP defines older version as dependency, force update.
+    implementation(KOTLIN_KSP)
     implementation(GOOGLE_COMPILE_TESTING)
     implementation(KOTLIN_COMPILE_TESTING_KSP)
+    // specify these because KSP do not specify them and we might get an older version from kotlin
+    // compile testing
+    // https://github.com/google/ksp/issues/187
+    implementation(KOTLIN_COMPILER_EMBEDDABLE)
+    implementation(KOTLIN_COMPILER_DAEMON_EMBEDDABLE)
+    implementation(KOTLIN_ANNOTATION_PROCESSING_EMBEDDABLE)
 }
 
 androidx {
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticJavacProcessor.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticJavacProcessor.kt
index 0ac5928..54fc124 100644
--- a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticJavacProcessor.kt
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticJavacProcessor.kt
@@ -16,21 +16,27 @@
 
 package androidx.room.compiler.processing
 
+import androidx.room.compiler.processing.util.RecordingXMessager
 import androidx.room.compiler.processing.util.XTestInvocation
-import java.lang.AssertionError
 import javax.lang.model.SourceVersion
 
 class SyntheticJavacProcessor(
-    val handler: (XTestInvocation) -> Unit
-) : JavacTestProcessor() {
+    val handler: (XTestInvocation) -> Unit,
+) : JavacTestProcessor(), SyntheticProcessor {
+    override val invocationInstances = mutableListOf<XTestInvocation>()
     private var result: Result<Unit>? = null
+    override val messageWatcher = RecordingXMessager()
 
     override fun doProcess(annotations: Set<XTypeElement>, roundEnv: XRoundEnv): Boolean {
+        val xEnv = XProcessingEnv.create(processingEnv)
+        xEnv.messager.addMessageWatcher(messageWatcher)
         result = kotlin.runCatching {
             handler(
                 XTestInvocation(
-                    processingEnv = XProcessingEnv.create(processingEnv)
-                )
+                    processingEnv = xEnv
+                ).also {
+                    invocationInstances.add(it)
+                }
             )
         }
         return true
@@ -42,7 +48,7 @@
 
     override fun getSupportedAnnotationTypes() = setOf("*")
 
-    fun throwIfFailed() {
+    override fun throwIfFailed() {
         val result = checkNotNull(result) {
             "did not compile"
         }
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticKspProcessor.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticKspProcessor.kt
index 106d6b1..68ec813 100644
--- a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticKspProcessor.kt
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticKspProcessor.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.compiler.processing
 
+import androidx.room.compiler.processing.util.RecordingXMessager
 import androidx.room.compiler.processing.util.XTestInvocation
 import com.google.devtools.ksp.processing.CodeGenerator
 import com.google.devtools.ksp.processing.KSPLogger
@@ -24,11 +25,14 @@
 
 class SyntheticKspProcessor(
     private val handler: (XTestInvocation) -> Unit
-) : SymbolProcessor {
+) : SymbolProcessor, SyntheticProcessor {
+    override val invocationInstances = mutableListOf<XTestInvocation>()
     private var result: Result<Unit>? = null
     private lateinit var options: Map<String, String>
     private lateinit var codeGenerator: CodeGenerator
     private lateinit var logger: KSPLogger
+    override val messageWatcher = RecordingXMessager()
+
     override fun finish() {
     }
 
@@ -44,21 +48,25 @@
     }
 
     override fun process(resolver: Resolver) {
+        val xEnv = XProcessingEnv.create(
+            options,
+            resolver,
+            codeGenerator,
+            logger
+        )
+        xEnv.messager.addMessageWatcher(messageWatcher)
         result = kotlin.runCatching {
             handler(
                 XTestInvocation(
-                    processingEnv = XProcessingEnv.create(
-                        options,
-                        resolver,
-                        codeGenerator,
-                        logger
-                    )
-                )
+                    processingEnv = xEnv
+                ).also {
+                    invocationInstances.add(it)
+                }
             )
         }
     }
 
-    fun throwIfFailed() {
+    override fun throwIfFailed() {
         val result = checkNotNull(result) {
             "did not compile"
         }
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticProcessor.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticProcessor.kt
new file mode 100644
index 0000000..05cc826
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticProcessor.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing
+
+import androidx.room.compiler.processing.util.RecordingXMessager
+import androidx.room.compiler.processing.util.XTestInvocation
+
+/**
+ * Common interface for SyntheticProcessors that we create for testing.
+ */
+internal interface SyntheticProcessor {
+    /**
+     * List of invocations that was sent to the test code.
+     *
+     * The test code can register assertions on the compilation result, which is why we need this
+     * list (to run assertions after compilation).
+     */
+    val invocationInstances: List<XTestInvocation>
+
+    /**
+     * The recorder for messages where we'll grab the diagnostics.
+     */
+    val messageWatcher: RecordingXMessager
+
+    /**
+     * Should throw if processor did throw an exception.
+     * When assertions fail, we don't fail the compilation to keep the stack trace, instead,
+     * dispatch them afterwards.
+     */
+    fun throwIfFailed()
+}
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
new file mode 100644
index 0000000..3dbbff7
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util
+
+import androidx.room.compiler.processing.SyntheticJavacProcessor
+import androidx.room.compiler.processing.SyntheticProcessor
+import androidx.room.compiler.processing.util.runner.CompilationTestRunner
+import com.google.common.truth.Fact.simpleFact
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
+import com.google.testing.compile.Compilation
+import com.google.testing.compile.CompileTester
+import com.tschuchort.compiletesting.KotlinCompilation
+import javax.tools.Diagnostic
+
+/**
+ * Holds the information about a test compilation result.
+ */
+abstract class CompilationResult internal constructor(
+    /**
+     * The test infra which run this test
+     */
+    internal val testRunnerName: String,
+    /**
+     * The [SyntheticProcessor] used in this compilation.
+     */
+    internal val processor: SyntheticProcessor,
+    /**
+     * True if compilation result was success.
+     */
+    internal val successfulCompilation: Boolean,
+) {
+    private val diagnostics = processor.messageWatcher.diagnostics()
+
+    fun diagnosticsOfKind(kind: Diagnostic.Kind) = diagnostics[kind].orEmpty()
+
+    override fun toString(): String {
+        return buildString {
+            appendLine("CompilationResult (with $testRunnerName)")
+            Diagnostic.Kind.values().forEach { kind ->
+                val messages = diagnosticsOfKind(kind)
+                appendLine("${kind.name}: ${messages.size}")
+                messages.forEach {
+                    appendLine(it)
+                }
+                appendLine()
+            }
+        }
+    }
+}
+
+/**
+ * Truth subject that can run assertions on the [CompilationResult].
+ * see: [XTestInvocation.assertCompilationResult]
+ */
+class CompilationResultSubject(
+    failureMetadata: FailureMetadata,
+    val compilationResult: CompilationResult,
+) : Subject<CompilationResultSubject, CompilationResult>(
+    failureMetadata, compilationResult
+) {
+    /**
+     * set to true if any assertion on the subject requires it to fail (e.g. looking for errors)
+     */
+    internal var shouldSucceed: Boolean = true
+
+    /**
+     * Asserts that compilation did fail. This covers the cases where the processor won't print
+     * any diagnostics but compilation will still fail (e.g. bad generated code).
+     *
+     * @see hasError
+     */
+    fun compilationDidFail() = chain {
+        shouldSucceed = false
+    }
+
+    /**
+     * Asserts that compilation has a warning with the given text.
+     *
+     * @see hasError
+     */
+    fun hasWarning(expected: String) = chain {
+        hasDiagnosticWithMessage(
+            kind = Diagnostic.Kind.WARNING,
+            expected = expected
+        ) {
+            "expected warning: $expected"
+        }
+    }
+
+    /**
+     * Asserts that compilation has an error with the given text.
+     *
+     * @see hasWarning
+     */
+    fun hasError(expected: String) = chain {
+        shouldSucceed = false
+        hasDiagnosticWithMessage(
+            kind = Diagnostic.Kind.ERROR,
+            expected = expected
+        ) {
+            "expected error: $expected"
+        }
+    }
+
+    /**
+     * Asserts that compilation has at least one diagnostics message with kind error.
+     *
+     * @see compilationDidFail
+     * @see hasWarning
+     */
+    fun hasError() = chain {
+        shouldSucceed = false
+        if (actual().diagnosticsOfKind(Diagnostic.Kind.ERROR).isEmpty()) {
+            failWithActual(
+                simpleFact("expected at least one failure message")
+            )
+        }
+    }
+
+    /**
+     * Called after handler is invoked to check its compilation failure assertion against the
+     * compilation result.
+     */
+    internal fun assertCompilationResult() {
+        if (compilationResult.successfulCompilation != shouldSucceed) {
+            failWithActual(
+                simpleFact(
+                    "expected compilation result to be: $shouldSucceed but was " +
+                        "${compilationResult.successfulCompilation}"
+                )
+            )
+        }
+    }
+
+    private fun hasDiagnosticWithMessage(
+        kind: Diagnostic.Kind,
+        expected: String,
+        buildErrorMessage: () -> String
+    ) {
+        val diagnostics = compilationResult.diagnosticsOfKind(kind)
+        if (diagnostics.any { it.msg == expected }) {
+            return
+        }
+        failWithActual(simpleFact(buildErrorMessage()))
+    }
+
+    private fun chain(
+        block: () -> Unit
+    ): CompileTester.ChainingClause<CompilationResultSubject> {
+        block()
+        return CompileTester.ChainingClause<CompilationResultSubject> {
+            this
+        }
+    }
+
+    companion object {
+        private val FACTORY =
+            Factory<CompilationResultSubject, CompilationResult> { metadata, actual ->
+                CompilationResultSubject(metadata, actual)
+            }
+
+        fun assertThat(
+            compilationResult: CompilationResult
+        ): CompilationResultSubject {
+            return Truth.assertAbout(FACTORY).that(
+                compilationResult
+            )
+        }
+    }
+}
+
+internal class JavaCompileTestingCompilationResult(
+    testRunner: CompilationTestRunner,
+    @Suppress("unused")
+    private val delegate: Compilation,
+    processor: SyntheticJavacProcessor
+) : CompilationResult(
+    testRunnerName = testRunner.name,
+    processor = processor,
+    successfulCompilation = delegate.status() == Compilation.Status.SUCCESS
+)
+
+internal class KotlinCompileTestingCompilationResult(
+    testRunner: CompilationTestRunner,
+    @Suppress("unused")
+    private val delegate: KotlinCompilation.Result,
+    processor: SyntheticProcessor,
+    successfulCompilation: Boolean
+) : CompilationResult(
+    testRunnerName = testRunner.name,
+    processor = processor,
+    successfulCompilation = successfulCompilation
+)
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessage.kt
similarity index 73%
copy from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
copy to room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessage.kt
index f9cb2fe..34ef8e3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessage.kt
@@ -14,7 +14,14 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.focus
+package androidx.room.compiler.processing.util
 
-@RequiresOptIn("The Focus API is experimental and is likely to change in the future.")
-annotation class ExperimentalFocus
\ No newline at end of file
+import androidx.room.compiler.processing.XElement
+
+/**
+ * Holder for diagnostics messages
+ */
+data class DiagnosticMessage(
+    val msg: String,
+    val element: XElement?
+)
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
index 076b531..bbf2888 100644
--- a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
@@ -16,275 +16,147 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.room.compiler.processing.SyntheticJavacProcessor
-import androidx.room.compiler.processing.SyntheticKspProcessor
-import com.google.common.truth.Truth
+import androidx.room.compiler.processing.util.runner.CompilationTestRunner
+import androidx.room.compiler.processing.util.runner.JavacCompilationTestRunner
+import androidx.room.compiler.processing.util.runner.KaptCompilationTestRunner
+import androidx.room.compiler.processing.util.runner.KspCompilationTestRunner
+import androidx.room.compiler.processing.util.runner.TestCompilationParameters
 import com.google.common.truth.Truth.assertThat
-import com.google.testing.compile.CompileTester
-import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.google.common.truth.Truth.assertWithMessage
 import com.tschuchort.compiletesting.KotlinCompilation
-import com.tschuchort.compiletesting.SourceFile
-import com.tschuchort.compiletesting.kspSourcesDir
-import com.tschuchort.compiletesting.symbolProcessors
 import java.io.File
 
-// TODO get rid of these once kotlin compile testing supports two step compilation for KSP.
-//  https://github.com/tschuchortdev/kotlin-compile-testing/issues/72
-private val KotlinCompilation.kspJavaSourceDir: File
-    get() = kspSourcesDir.resolve("java")
+private fun runTests(
+    params: TestCompilationParameters,
+    vararg runners: CompilationTestRunner
+) {
+    val runCount = runners.count { runner ->
+        if (runner.canRun(params)) {
+            val compilationResult = runner.compile(params)
+            val subject = CompilationResultSubject.assertThat(compilationResult)
+            // if any assertion failed, throw first those.
+            compilationResult.processor.throwIfFailed()
 
-private val KotlinCompilation.kspKotlinSourceDir: File
-    get() = kspSourcesDir.resolve("kotlin")
+            compilationResult.processor.invocationInstances.forEach {
+                it.runPostCompilationChecks(subject)
+            }
+            assertWithMessage(
+                "compilation should've run the processor callback at least once"
+            ).that(
+                compilationResult.processor.invocationInstances
+            ).isNotEmpty()
 
-private fun compileSources(
-    sources: List<Source>,
-    classpath: List<File>,
+            subject.assertCompilationResult()
+            true
+        } else {
+            false
+        }
+    }
+    // make sure some tests did run
+    assertThat(runCount).isGreaterThan(0)
+}
+
+fun runProcessorTestWithoutKsp(
+    sources: List<Source> = emptyList(),
+    classpath: List<File> = emptyList(),
     handler: (XTestInvocation) -> Unit
-): Pair<SyntheticJavacProcessor, CompileTester> {
-    val syntheticJavacProcessor = SyntheticJavacProcessor(handler)
-    return syntheticJavacProcessor to Truth.assertAbout(
-        JavaSourcesSubjectFactory.javaSources()
-    ).that(
-        sources.map {
-            it.toJFO()
-        }
-    ).apply {
-        if (classpath.isNotEmpty()) {
-            withClasspath(classpath)
-        }
-    }.processedWith(
-        syntheticJavacProcessor
+) {
+    runTests(
+        params = TestCompilationParameters(
+            sources = sources,
+            classpath = classpath,
+            handler = handler
+        ),
+        JavacCompilationTestRunner,
+        KaptCompilationTestRunner
     )
 }
 
-private fun compileWithKapt(
-    sources: List<Source>,
-    classpath: List<File>,
-    handler: (XTestInvocation) -> Unit
-): Pair<SyntheticJavacProcessor, KotlinCompilation> {
-    val syntheticJavacProcessor = SyntheticJavacProcessor(handler)
-    val compilation = KotlinCompilation()
-    sources.forEach {
-        compilation.workingDir.resolve("sources")
-            .resolve(it.relativePath())
-            .parentFile
-            .mkdirs()
-    }
-    compilation.sources = sources.map {
-        it.toKotlinSourceFile()
-    }
-    compilation.annotationProcessors = listOf(syntheticJavacProcessor)
-    compilation.inheritClassPath = true
-    compilation.verbose = false
-    compilation.classpaths += classpath
-
-    return syntheticJavacProcessor to compilation
-}
-
-private fun compileWithKsp(
-    sources: List<Source>,
-    classpath: List<File>,
-    handler: (XTestInvocation) -> Unit
-): Pair<SyntheticKspProcessor, KotlinCompilation.Result> {
-    @Suppress("NAME_SHADOWING")
-    val sources = if (sources.none { it is Source.KotlinSource }) {
-        // looks like this requires a kotlin source file
-        // see: https://github.com/tschuchortdev/kotlin-compile-testing/issues/57
-        sources + Source.kotlin("placeholder.kt", "")
-    } else {
-        sources
-    }
-    val syntheticKspProcessor = SyntheticKspProcessor(handler)
-    fun prepareCompilation(): KotlinCompilation {
-        val compilation = KotlinCompilation()
-        sources.forEach {
-            compilation.workingDir.resolve("sources")
-                .resolve(it.relativePath())
-                .parentFile
-                .mkdirs()
-        }
-        compilation.sources = sources.map {
-            it.toKotlinSourceFile()
-        }
-        compilation.jvmDefault = "enable"
-        compilation.jvmTarget = "1.8"
-        compilation.inheritClassPath = true
-        compilation.verbose = false
-        compilation.classpaths += classpath
-        return compilation
-    }
-
-    val kspCompilation = prepareCompilation()
-    kspCompilation.symbolProcessors = listOf(syntheticKspProcessor)
-    kspCompilation.compile()
-    // ignore KSP result for now because KSP stops compilation, which might create false negatives
-    // when java code accesses kotlin code.
-    // TODO:  fix once https://github.com/tschuchortdev/kotlin-compile-testing/issues/72 is fixed
-
-    // after ksp, compile without ksp with KSP's output as input
-    val finalCompilation = prepareCompilation()
-    // build source files from generated code
-    finalCompilation.sources += kspCompilation.kspJavaSourceDir.collectSourceFiles() +
-        kspCompilation.kspKotlinSourceDir.collectSourceFiles()
-    return syntheticKspProcessor to finalCompilation.compile()
-}
-
-private fun File.collectSourceFiles(): List<SourceFile> {
-    return walkTopDown().filter {
-        it.isFile
-    }.map { file ->
-        SourceFile.fromPath(file)
-    }.toList()
-}
-
+/**
+ * Runs the compilation test with all 3 backends (javac, kapt, ksp) if possible (e.g. javac
+ * cannot test kotlin sources).
+ *
+ * The [handler] will be invoked for each compilation hence it should be repeatable.
+ *
+ * To assert on the compilation results, [handler] can call
+ * [XTestInvocation.assertCompilationResult] where it will receive a subject for post compilation
+ * assertions.
+ *
+ * By default, the compilation is expected to succeed. If it should fail, there must be an
+ * assertion on [XTestInvocation.assertCompilationResult] which expects a failure (e.g. checking
+ * errors).
+ */
 fun runProcessorTest(
     sources: List<Source> = emptyList(),
     classpath: List<File> = emptyList(),
     handler: (XTestInvocation) -> Unit
 ) {
-    @Suppress("NAME_SHADOWING")
-    val sources = if (sources.isEmpty()) {
-        // synthesize a source to trigger compilation
-        listOf(
-            Source.java(
-                "foo.bar.SyntheticSource",
-                """
-            package foo.bar;
-            public class SyntheticSource {}
-                """.trimIndent()
-            )
-        )
-    } else {
-        sources
-    }
-    // we can compile w/ javac only if all code is in java
-    if (sources.canCompileWithJava()) {
-        runJavaProcessorTest(
+    runTests(
+        params = TestCompilationParameters(
             sources = sources,
             classpath = classpath,
-            handler = handler,
-            succeed = true
-        )
-    }
-    runKaptTest(
-        sources = sources,
-        classpath = classpath,
-        handler = handler,
-        succeed = true
+            handler = handler
+        ),
+        JavacCompilationTestRunner,
+        KaptCompilationTestRunner,
+        KspCompilationTestRunner
     )
 }
 
 /**
- * This method is oddly named instead of being an overload on runProcessorTest to easily track
- * which tests started to support KSP.
+ * Runs the test only with javac compilation backend.
  *
- * Eventually, it will be merged with runProcessorTest when all tests pass with KSP.
+ * @see runProcessorTest
  */
-fun runProcessorTestIncludingKsp(
-    sources: List<Source> = emptyList(),
-    classpath: List<File> = emptyList(),
-    handler: (XTestInvocation) -> Unit
-) {
-    runProcessorTest(
-        sources = sources,
-        classpath = classpath,
-        handler = handler
-    )
-    runKspTest(
-        sources = sources,
-        classpath = classpath,
-        succeed = true,
-        handler = handler
-    )
-}
-
-fun runProcessorTestForFailedCompilation(
-    sources: List<Source>,
-    classpath: List<File> = emptyList(),
-    handler: (XTestInvocation) -> Unit
-) {
-    if (sources.canCompileWithJava()) {
-        // run with java processor
-        runJavaProcessorTest(
-            sources = sources,
-            classpath = classpath,
-            handler = handler,
-            succeed = false
-        )
-    }
-    // now run with kapt
-    runKaptTest(
-        sources = sources,
-        classpath = classpath,
-        handler = handler,
-        succeed = false
-    )
-}
-
-fun runProcessorTestForFailedCompilationIncludingKsp(
-    sources: List<Source>,
-    classpath: List<File>,
-    handler: (XTestInvocation) -> Unit
-) {
-    runProcessorTestForFailedCompilation(
-        sources = sources,
-        classpath = classpath,
-        handler = handler
-    )
-    // now run with ksp
-    runKspTest(
-        sources = sources,
-        classpath = classpath,
-        handler = handler,
-        succeed = false
-    )
-}
-
 fun runJavaProcessorTest(
     sources: List<Source>,
     classpath: List<File>,
-    succeed: Boolean,
     handler: (XTestInvocation) -> Unit
 ) {
-    val (syntheticJavacProcessor, compileTester) = compileSources(sources, classpath, handler)
-    if (succeed) {
-        compileTester.compilesWithoutError()
-    } else {
-        compileTester.failsToCompile()
-    }
-    syntheticJavacProcessor.throwIfFailed()
+    runTests(
+        params = TestCompilationParameters(
+            sources = sources,
+            classpath = classpath,
+            handler = handler
+        ),
+        JavacCompilationTestRunner
+    )
 }
 
+/**
+ * Runs the test only with kapt compilation backend
+ */
 fun runKaptTest(
     sources: List<Source>,
     classpath: List<File> = emptyList(),
-    succeed: Boolean = true,
     handler: (XTestInvocation) -> Unit
 ) {
-    // now run with kapt
-    val (kaptProcessor, kotlinCompilation) = compileWithKapt(sources, classpath, handler)
-    val compilationResult = kotlinCompilation.compile()
-    if (succeed) {
-        assertThat(compilationResult.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
-    } else {
-        assertThat(compilationResult.exitCode).isNotEqualTo(KotlinCompilation.ExitCode.OK)
-    }
-    kaptProcessor.throwIfFailed()
+    runTests(
+        params = TestCompilationParameters(
+            sources = sources,
+            classpath = classpath,
+            handler = handler
+        ),
+        KaptCompilationTestRunner
+    )
 }
 
+/**
+ * Runs the test only with ksp compilation backend
+ */
 fun runKspTest(
     sources: List<Source>,
     classpath: List<File> = emptyList(),
-    succeed: Boolean = true,
     handler: (XTestInvocation) -> Unit
 ) {
-    val (kspProcessor, compilationResult) = compileWithKsp(sources, classpath, handler)
-    if (succeed) {
-        assertThat(compilationResult.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
-    } else {
-        assertThat(compilationResult.exitCode).isNotEqualTo(KotlinCompilation.ExitCode.OK)
-    }
-    kspProcessor.throwIfFailed()
+    runTests(
+        params = TestCompilationParameters(
+            sources = sources,
+            classpath = classpath,
+            handler = handler
+        ),
+        KspCompilationTestRunner
+    )
 }
 
 /**
@@ -314,5 +186,3 @@
     }
     return compilation.classesDir
 }
-
-private fun List<Source>.canCompileWithJava() = all { it is Source.JavaSource }
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/RecordingXMessager.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/RecordingXMessager.kt
new file mode 100644
index 0000000..c2aa5dc
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/RecordingXMessager.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util
+
+import androidx.room.compiler.processing.XElement
+import androidx.room.compiler.processing.XMessager
+import javax.tools.Diagnostic
+
+/**
+ * An XMessager implementation that holds onto dispatched diagnostics.
+ */
+class RecordingXMessager : XMessager() {
+    private val diagnostics = mutableMapOf<Diagnostic.Kind, MutableList<DiagnosticMessage>>()
+
+    fun diagnostics(): Map<Diagnostic.Kind, List<DiagnosticMessage>> = diagnostics
+
+    override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+        diagnostics.getOrPut(
+            kind
+        ) {
+            mutableListOf()
+        }.add(
+            DiagnosticMessage(
+                msg = msg,
+                element = element
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
index 267c140..d7d0e75 100644
--- a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
@@ -17,6 +17,7 @@
 package androidx.room.compiler.processing.util
 
 import androidx.room.compiler.processing.XProcessingEnv
+import kotlin.reflect.KClass
 
 /**
  * Data holder for XProcessing tests to access the processing environment.
@@ -24,6 +25,48 @@
 class XTestInvocation(
     val processingEnv: XProcessingEnv,
 ) {
+    /**
+     * Extension mechanism to allow putting objects into invocation that can be retrieved later.
+     */
+    private val userData = mutableMapOf<KClass<*>, Any>()
+
+    private val postCompilationAssertions = mutableListOf<CompilationResultSubject.() -> Unit>()
     val isKsp: Boolean
         get() = processingEnv.backend == XProcessingEnv.Backend.KSP
+
+    /**
+     * Registers a block that will be called with a [CompilationResultSubject] when compilation
+     * finishes.
+     *
+     * Note that it is not safe to access the environment in this block.
+     */
+    fun assertCompilationResult(block: CompilationResultSubject.() -> Unit) {
+        postCompilationAssertions.add(block)
+    }
+
+    internal fun runPostCompilationChecks(
+        compilationResultSubject: CompilationResultSubject
+    ) {
+        postCompilationAssertions.forEach {
+            it(compilationResultSubject)
+        }
+    }
+
+    fun <T : Any> getUserData(key: KClass<T>): T? {
+        @Suppress("UNCHECKED_CAST")
+        return userData[key] as T?
+    }
+
+    fun <T : Any> putUserData(key: KClass<T>, value: T) {
+        userData[key] = value
+    }
+
+    fun <T : Any> getOrPutUserData(key: KClass<T>, create: () -> T): T {
+        getUserData(key)?.let {
+            return it
+        }
+        return create().also {
+            putUserData(key, it)
+        }
+    }
 }
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/CompilationTestRunner.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/CompilationTestRunner.kt
new file mode 100644
index 0000000..257e58a
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/CompilationTestRunner.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.room.compiler.processing.util.runner
+
+import androidx.room.compiler.processing.util.CompilationResult
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
+import java.io.File
+
+/**
+ * Common interface for compilation tests
+ */
+internal interface CompilationTestRunner {
+    // user visible name that we can print in assertions
+    val name: String
+
+    fun canRun(params: TestCompilationParameters): Boolean
+
+    fun compile(params: TestCompilationParameters): CompilationResult
+}
+
+internal data class TestCompilationParameters(
+    val sources: List<Source> = emptyList(),
+    val classpath: List<File> = emptyList(),
+    val handler: (XTestInvocation) -> Unit
+)
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/JavacCompilationTestRunner.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/JavacCompilationTestRunner.kt
new file mode 100644
index 0000000..5fdba61
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/JavacCompilationTestRunner.kt
@@ -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.room.compiler.processing.util.runner
+
+import androidx.room.compiler.processing.SyntheticJavacProcessor
+import androidx.room.compiler.processing.util.CompilationResult
+import androidx.room.compiler.processing.util.JavaCompileTestingCompilationResult
+import androidx.room.compiler.processing.util.Source
+import com.google.testing.compile.Compiler
+
+internal object JavacCompilationTestRunner : CompilationTestRunner {
+
+    override val name: String = "javac"
+
+    override fun canRun(params: TestCompilationParameters): Boolean {
+        return params.sources.all { it is Source.JavaSource }
+    }
+
+    override fun compile(params: TestCompilationParameters): CompilationResult {
+        val syntheticJavacProcessor = SyntheticJavacProcessor(params.handler)
+        val sources = if (params.sources.isEmpty()) {
+            // synthesize a source to trigger compilation
+            listOf(
+                Source.java(
+                    qName = "foo.bar.SyntheticSource",
+                    code = """
+                    package foo.bar;
+                    public class SyntheticSource {}
+                    """.trimIndent()
+                )
+            )
+        } else {
+            params.sources
+        }
+        val compiler = Compiler
+            .javac()
+            .withProcessors(syntheticJavacProcessor)
+            .withOptions("-Xlint")
+            .let {
+                if (params.classpath.isNotEmpty()) {
+                    it.withClasspath(params.classpath)
+                } else {
+                    it
+                }
+            }
+        val javaFileObjects = sources.map {
+            it.toJFO()
+        }
+        val compilation = compiler.compile(javaFileObjects)
+        return JavaCompileTestingCompilationResult(
+            testRunner = this,
+            delegate = compilation,
+            processor = syntheticJavacProcessor
+        )
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KaptCompilationTestRunner.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KaptCompilationTestRunner.kt
new file mode 100644
index 0000000..9534163
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KaptCompilationTestRunner.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util.runner
+
+import androidx.room.compiler.processing.SyntheticJavacProcessor
+import androidx.room.compiler.processing.util.CompilationResult
+import androidx.room.compiler.processing.util.KotlinCompileTestingCompilationResult
+import com.tschuchort.compiletesting.KotlinCompilation
+
+internal object KaptCompilationTestRunner : CompilationTestRunner {
+
+    override val name: String = "kapt"
+
+    override fun canRun(params: TestCompilationParameters): Boolean {
+        return true
+    }
+
+    override fun compile(params: TestCompilationParameters): CompilationResult {
+        val syntheticJavacProcessor = SyntheticJavacProcessor(params.handler)
+        val compilation = KotlinCompilation()
+        params.sources.forEach {
+            compilation.workingDir.resolve("sources")
+                .resolve(it.relativePath())
+                .parentFile
+                .mkdirs()
+        }
+        compilation.sources = params.sources.map {
+            it.toKotlinSourceFile()
+        }
+        compilation.jvmDefault = "enable"
+        compilation.jvmTarget = "1.8"
+        compilation.annotationProcessors = listOf(syntheticJavacProcessor)
+        compilation.inheritClassPath = true
+        compilation.verbose = false
+        compilation.classpaths += params.classpath
+
+        val result = compilation.compile()
+        return KotlinCompileTestingCompilationResult(
+            testRunner = this,
+            delegate = result,
+            processor = syntheticJavacProcessor,
+            successfulCompilation = result.exitCode == KotlinCompilation.ExitCode.OK
+        )
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KspCompilationTestRunner.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KspCompilationTestRunner.kt
new file mode 100644
index 0000000..8ac5ed3
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KspCompilationTestRunner.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util.runner
+
+import androidx.room.compiler.processing.SyntheticKspProcessor
+import androidx.room.compiler.processing.util.CompilationResult
+import androidx.room.compiler.processing.util.KotlinCompileTestingCompilationResult
+import androidx.room.compiler.processing.util.Source
+import com.tschuchort.compiletesting.KotlinCompilation
+import com.tschuchort.compiletesting.SourceFile
+import com.tschuchort.compiletesting.kspSourcesDir
+import com.tschuchort.compiletesting.symbolProcessors
+import java.io.File
+import javax.tools.Diagnostic
+
+internal object KspCompilationTestRunner : CompilationTestRunner {
+
+    override val name: String = "ksp"
+
+    override fun canRun(params: TestCompilationParameters): Boolean {
+        return true
+    }
+
+    override fun compile(params: TestCompilationParameters): CompilationResult {
+        @Suppress("NAME_SHADOWING")
+        val sources = if (params.sources.none { it is Source.KotlinSource }) {
+            // looks like this requires a kotlin source file
+            // see: https://github.com/tschuchortdev/kotlin-compile-testing/issues/57
+            params.sources + Source.kotlin("placeholder.kt", "")
+        } else {
+            params.sources
+        }
+        val syntheticKspProcessor = SyntheticKspProcessor(params.handler)
+        fun prepareCompilation(): KotlinCompilation {
+            val compilation = KotlinCompilation()
+            sources.forEach {
+                compilation.workingDir.resolve("sources")
+                    .resolve(it.relativePath())
+                    .parentFile
+                    .mkdirs()
+            }
+            compilation.sources = sources.map {
+                it.toKotlinSourceFile()
+            }
+            compilation.jvmDefault = "enable"
+            compilation.jvmTarget = "1.8"
+            compilation.inheritClassPath = true
+            compilation.verbose = false
+            compilation.classpaths += params.classpath
+            return compilation
+        }
+
+        val kspCompilation = prepareCompilation()
+        kspCompilation.symbolProcessors = listOf(syntheticKspProcessor)
+        kspCompilation.compile()
+        // ignore KSP result for now because KSP stops compilation, which might create false
+        // negatives when java code accesses kotlin code.
+        // TODO:  fix once https://github.com/tschuchortdev/kotlin-compile-testing/issues/72 is
+        //  fixed
+
+        // after ksp, compile without ksp with KSP's output as input
+        val finalCompilation = prepareCompilation()
+        // build source files from generated code
+        finalCompilation.sources += kspCompilation.kspJavaSourceDir.collectSourceFiles() +
+            kspCompilation.kspKotlinSourceDir.collectSourceFiles()
+        val result = finalCompilation.compile()
+        // workaround for: https://github.com/google/ksp/issues/122
+        // KSP does not fail compilation for error diagnostics hence we do it here.
+        val hasErrorDiagnostics = syntheticKspProcessor.messageWatcher
+            .diagnostics()[Diagnostic.Kind.ERROR].orEmpty().isNotEmpty()
+        return KotlinCompileTestingCompilationResult(
+            testRunner = this,
+            delegate = result,
+            processor = syntheticKspProcessor,
+            successfulCompilation = result.exitCode == KotlinCompilation.ExitCode.OK &&
+                !hasErrorDiagnostics
+
+        )
+    }
+
+    // TODO get rid of these once kotlin compile testing supports two step compilation for KSP.
+    //  https://github.com/tschuchortdev/kotlin-compile-testing/issues/72
+    private val KotlinCompilation.kspJavaSourceDir: File
+        get() = kspSourcesDir.resolve("java")
+
+    private val KotlinCompilation.kspKotlinSourceDir: File
+        get() = kspSourcesDir.resolve("kotlin")
+
+    private fun File.collectSourceFiles(): List<SourceFile> {
+        return walkTopDown().filter {
+            it.isFile
+        }.map { file ->
+            SourceFile.fromPath(file)
+        }.toList()
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt b/room/compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
new file mode 100644
index 0000000..59dd47f7
--- /dev/null
+++ b/room/compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util
+
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.TypeSpec
+import org.junit.Test
+import javax.tools.Diagnostic
+
+class TestRunnerTest {
+    @Test
+    fun generatedBadCode_expected() = generatedBadCode(assertFailure = true)
+
+    @Test(expected = AssertionError::class)
+    fun generatedBadCode_unexpected() = generatedBadCode(assertFailure = false)
+
+    private fun generatedBadCode(assertFailure: Boolean) {
+        runProcessorTest {
+            if (it.processingEnv.findTypeElement("foo.Foo") == null) {
+                val badCode = TypeSpec.classBuilder("Foo").apply {
+                    addStaticBlock(
+                        CodeBlock.of("bad code")
+                    )
+                }.build()
+                val badGeneratedFile = JavaFile.builder("foo", badCode).build()
+                it.processingEnv.filer.write(
+                    badGeneratedFile
+                )
+            }
+            if (assertFailure) {
+                it.assertCompilationResult {
+                    compilationDidFail()
+                }
+            }
+        }
+    }
+
+    @Test
+    fun reportedError_expected() = reportedError(assertFailure = true)
+
+    @Test(expected = AssertionError::class)
+    fun reportedError_unexpected() = reportedError(assertFailure = false)
+
+    fun reportedError(assertFailure: Boolean) {
+        runProcessorTest {
+            it.processingEnv.messager.printMessage(
+                kind = Diagnostic.Kind.ERROR,
+                msg = "reported error"
+            )
+            if (assertFailure) {
+                it.assertCompilationResult {
+                    hasError("reported error")
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing/build.gradle b/room/compiler-processing/build.gradle
index 795a691..d2365d4 100644
--- a/room/compiler-processing/build.gradle
+++ b/room/compiler-processing/build.gradle
@@ -26,34 +26,23 @@
 }
 
 dependencies {
+    api(KOTLIN_STDLIB)
+    api(JAVAPOET)
     implementation("androidx.annotation:annotation:1.1.0")
     implementation(GUAVA)
-    implementation(KOTLIN_STDLIB)
     implementation(AUTO_COMMON)
     implementation(AUTO_VALUE_ANNOTATIONS)
-    implementation(JAVAPOET)
+
     implementation(KOTLIN_METADATA_JVM)
     implementation(INTELLIJ_ANNOTATIONS)
-    implementation(KOTLIN_KSP_API) {
-        version {
-            // TODO remove after KSP versions are fixed
-            //  KSP 1.4 versions are not properly ordered due to rc vs dev versions (dev is latest
-            //  but gradle thinks rc is latest).
-            //  We have to enforce it to ensure the correct version is used.
-            strictly KSP_VERSION
-        }
-    }
+    implementation(KOTLIN_KSP_API)
+    implementation(KOTLIN_STDLIB_JDK8) // KSP defines older version as dependency, force update.
 
     testImplementation(GOOGLE_COMPILE_TESTING)
     testImplementation(JUNIT)
     testImplementation(JSR250)
     testImplementation(KOTLIN_COMPILE_TESTING_KSP)
-    testImplementation(KOTLIN_KSP) {
-        version {
-            // TODO remove after KSP versions are fixed
-            strictly KSP_VERSION
-        }
-    }
+    testImplementation(KOTLIN_KSP)
     testImplementation(project(":room:room-compiler-processing-testing"))
 }
 
diff --git a/room/compiler-processing/lint-baseline.xml b/room/compiler-processing/lint-baseline.xml
deleted file mode 100644
index 7a7fa1e..0000000
--- a/room/compiler-processing/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.2.0-alpha15" client="gradle" version="4.2.0-alpha15">
-
-    <issue
-        id="BanUncheckedReflection"
-        message="Calling Method.invoke without an SDK check"
-        errorLine1="            return enumClass.getDeclaredMethod(&quot;valueOf&quot;, String::class.java)"
-        errorLine2="                   ^">
-        <location
-            file="src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationBox.kt"
-            line="260"
-            column="20"/>
-    </issue>
-
-</issues>
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt
index 0701c52..29ff818 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt
@@ -21,7 +21,8 @@
 /**
  * Logging interface for the processor
  */
-interface XMessager {
+abstract class XMessager {
+    private val watchers = mutableListOf<XMessager>()
     /**
      * Prints the given [msg] to the logs while also associating it with the given [element].
      *
@@ -29,5 +30,20 @@
      * @param msg The actual message to report to the compiler
      * @param element The element with whom the message should be associated with
      */
-    fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement? = null)
+    final fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement? = null) {
+        watchers.forEach {
+            it.printMessage(kind, msg, element)
+        }
+        onPrintMessage(kind, msg, element)
+    }
+
+    abstract fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement? = null)
+
+    fun addMessageWatcher(watcher: XMessager) {
+        watchers.add(watcher)
+    }
+
+    fun removeMessageWatcher(watcher: XMessager) {
+        watchers.remove(watcher)
+    }
 }
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt
index 03fc077..1653b4a 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt
@@ -84,11 +84,6 @@
     fun boxed(): XType
 
     /**
-     * Returns this type as an instance of [XArrayType] or fails if it is not an array.
-     */
-    fun asArray(): XArrayType = this as XArrayType
-
-    /**
      * Returns `true` if this is a primitive or boxed it
      */
     fun isInt(): Boolean
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/ElementExt.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/ElementExt.kt
index 372496c..8959ca0 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/ElementExt.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/ElementExt.kt
@@ -37,21 +37,30 @@
 )
 
 /**
- * gets all members including super privates. does not handle duplicate field names!!!
+ * Returns all fields including private fields (including private fields in super). Removes
+ * duplicate fields if class has a field with the same name as the parent.
  */
-// TODO handle conflicts with super: b/35568142
 internal fun TypeElement.getAllFieldsIncludingPrivateSupers(
     elementUtils: Elements
 ): Set<VariableElement> {
-    val myMembers = ElementFilter.fieldsIn(elementUtils.getAllMembers(this))
+    val selection = ElementFilter
+        .fieldsIn(elementUtils.getAllMembers(this))
         .filterIsInstance<VariableElement>()
-        .toSet()
-    if (superclass.kind != TypeKind.NONE) {
-        return myMembers + MoreTypes.asTypeElement(superclass)
-            .getAllFieldsIncludingPrivateSupers(elementUtils)
-    } else {
-        return myMembers
+        .toMutableSet()
+    val selectionNames = selection.mapTo(mutableSetOf()) {
+        it.simpleName
     }
+    if (superclass.kind != TypeKind.NONE) {
+        val superFields = MoreTypes.asTypeElement(superclass)
+            .getAllFieldsIncludingPrivateSupers(elementUtils)
+        // accept super fields only if the name does not conflict
+        superFields.forEach { superField ->
+            if (selectionNames.add(superField.simpleName)) {
+                selection.add(superField)
+            }
+        }
+    }
+    return selection
 }
 
 @Suppress("UnstableApiUsage")
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationBox.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationBox.kt
index bff4938..227d6f6 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationBox.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationBox.kt
@@ -91,13 +91,20 @@
             }
             returnType.isArray && returnType.componentType.isAnnotation -> {
                 @Suppress("UNCHECKED_CAST")
-                ListVisitor(env, returnType.componentType as Class<out Annotation>).visit(value)
+                AnnotationListVisitor(env, returnType.componentType as Class<out Annotation>)
+                    .visit(value)
+            }
+            returnType.isArray && returnType.componentType.isEnum -> {
+                @Suppress("UNCHECKED_CAST")
+                EnumListVisitor(returnType.componentType as Class<out Enum<*>>).visit(value)
             }
             returnType.isEnum -> {
                 @Suppress("UNCHECKED_CAST")
                 value.getAsEnum(returnType as Class<out Enum<*>>)
             }
-            else -> throw UnsupportedOperationException("$returnType isn't supported")
+            else -> {
+                throw UnsupportedOperationException("$returnType isn't supported")
+            }
         }
         method.name to result
     }
@@ -230,7 +237,7 @@
 }
 
 @Suppress("DEPRECATION")
-private class ListVisitor<T : Annotation>(
+private class AnnotationListVisitor<T : Annotation>(
     private val env: JavacProcessingEnv,
     private val annotationClass: Class<T>
 ) :
@@ -245,6 +252,24 @@
 }
 
 @Suppress("DEPRECATION")
+private class EnumListVisitor<T : Enum<T>>(private val enumClass: Class<T>) :
+    SimpleAnnotationValueVisitor6<Array<T>, Void?>() {
+    override fun visitArray(
+        values: MutableList<out AnnotationValue>?,
+        void: Void?
+    ): Array<T> {
+        val result = values?.map { it.getAsEnum(enumClass) }
+        @Suppress("UNCHECKED_CAST")
+        val resultArray = java.lang.reflect.Array
+            .newInstance(enumClass, result?.size ?: 0) as Array<T>
+        result?.forEachIndexed { index, value ->
+            resultArray[index] = value
+        }
+        return resultArray
+    }
+}
+
+@Suppress("DEPRECATION")
 private class AnnotationClassVisitor<T : Annotation>(
     private val env: JavacProcessingEnv,
     private val annotationClass: Class<T>
@@ -253,7 +278,7 @@
     override fun visitAnnotation(a: AnnotationMirror?, v: Void?) = a?.box(env, annotationClass)
 }
 
-@Suppress("UNCHECKED_CAST", "DEPRECATION")
+@Suppress("UNCHECKED_CAST", "DEPRECATION", "BanUncheckedReflection")
 private fun <T : Enum<*>> AnnotationValue.getAsEnum(enumClass: Class<T>): T {
     return object : SimpleAnnotationValueVisitor6<T, Void>() {
         override fun visitEnumConstant(value: VariableElement?, p: Void?): T {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnvMessager.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnvMessager.kt
index f5120fa..e49a5f7 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnvMessager.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnvMessager.kt
@@ -27,8 +27,8 @@
 
 internal class JavacProcessingEnvMessager(
     private val processingEnv: ProcessingEnvironment
-) : XMessager {
-    override fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+) : XMessager() {
+    override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
         val javacElement = (element as? JavacElement)?.element
         processingEnv.messager.printMessage(
             kind,
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAsMemberOf.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAsMemberOf.kt
index 6c230d8..7e84d7c 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAsMemberOf.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAsMemberOf.kt
@@ -26,10 +26,17 @@
  * Returns the type of a property as if it is member of the given [ksType].
  */
 internal fun KSPropertyDeclaration.typeAsMemberOf(resolver: Resolver, ksType: KSType): KSType {
+    val resolved = type.resolve()
     if (isStatic()) {
         // calling as member with a static would throw as it might be a member of the companion
         // object
-        return type.resolve()
+        return resolved
+    }
+    // see: https://github.com/google/ksp/issues/107
+    // as member of might lose the `isError` information hence we should check before calling
+    // asMemberOf.
+    if (resolved.isError) {
+        return resolved
     }
     return resolver.asMemberOf(
         property = this,
@@ -42,10 +49,17 @@
     functionDeclaration: KSFunctionDeclaration,
     ksType: KSType
 ): KSType {
+    val resolved = type.resolve()
     if (functionDeclaration.isStatic()) {
         // calling as member with a static would throw as it might be a member of the companion
         // object
-        return type.resolve()
+        return resolved
+    }
+    if (resolved.isError) {
+        // see: https://github.com/google/ksp/issues/107
+        // as member of might lose the `isError` information hence we should check before calling
+        // asMemberOf.
+        return resolved
     }
     val asMember = resolver.asMemberOf(
         function = functionDeclaration,
@@ -54,22 +68,25 @@
     // TODO b/173224718
     // this is counter intuitive, we should remove asMemberOf from method parameters.
     val myIndex = functionDeclaration.parameters.indexOf(this)
-    return asMember.parameterTypes[myIndex] ?: type.resolve()
+    return asMember.parameterTypes[myIndex] ?: resolved
 }
 
 internal fun KSFunctionDeclaration.returnTypeAsMemberOf(
     resolver: Resolver,
     ksType: KSType
 ): KSType {
-    val returnType = if (isStatic()) {
-        // calling as member with a static would throw as it might be a member of the companion
-        // object
-        returnType?.resolve()
-    } else {
-        resolver.asMemberOf(
+    val resolved = returnType?.resolve()
+    return when {
+        resolved == null -> null
+        resolved.isError -> resolved
+        isStatic() -> {
+            // calling as member with a static would throw as it might be a member of the companion
+            // object
+            resolved
+        }
+        else -> resolver.asMemberOf(
             function = this,
             containing = ksType
         ).returnType
-    }
-    return returnType ?: error("cannot find return type for $this")
+    } ?: error("cannot find return type for $this")
 }
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
index edfd964..b62e6ad 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
@@ -19,6 +19,7 @@
 import androidx.room.compiler.processing.XAnnotationBox
 import androidx.room.compiler.processing.XType
 import com.google.devtools.ksp.symbol.KSAnnotation
+import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.google.devtools.ksp.symbol.KSType
 import java.lang.reflect.Proxy
 
@@ -29,7 +30,7 @@
     private val annotation: KSAnnotation
 ) : XAnnotationBox<T> {
     override fun getAsType(methodName: String): XType? {
-        val value = getFieldValue<KSType>(methodName)
+        val value = getFieldValue(methodName, KSType::class.java)
         return value?.let {
             env.wrap(
                 ksType = it,
@@ -39,8 +40,8 @@
     }
 
     override fun getAsTypeList(methodName: String): List<XType> {
-        val values = getFieldValue<List<KSType>>(methodName) ?: return emptyList()
-        return values.map {
+        val values = getFieldValue(methodName, Array::class.java) ?: return emptyList()
+        return values.filterIsInstance<KSType>().map {
             env.wrap(
                 ksType = it,
                 allowPrimitives = true
@@ -49,7 +50,17 @@
     }
 
     override fun <R : Annotation> getAsAnnotationBox(methodName: String): XAnnotationBox<R> {
-        val value = getFieldValue<KSAnnotation>(methodName) ?: error("cannot get annotation")
+        val value = getFieldValue(methodName, KSAnnotation::class.java)
+        @Suppress("FoldInitializerAndIfToElvis")
+        if (value == null) {
+            // see https://github.com/google/ksp/issues/53
+            return KspReflectiveAnnotationBox.createFromDefaultValue(
+                env = env,
+                annotationClass = annotationClass,
+                methodName = methodName
+            )
+        }
+
         val annotationType = annotationClass.methods.first {
             it.name == methodName
         }.returnType as Class<R>
@@ -60,22 +71,37 @@
         )
     }
 
-    private inline fun <reified R> getFieldValue(methodName: String): R? {
-        val value = annotation.arguments.firstOrNull {
+    @Suppress("SyntheticAccessor")
+    private fun <R : Any> getFieldValue(
+        methodName: String,
+        returnType: Class<R>
+    ): R? {
+        val methodValue = annotation.arguments.firstOrNull {
             it.name?.asString() == methodName
-        }?.value ?: return null
-        return value as R?
+        }?.value
+        return methodValue?.readAs(returnType)
     }
 
     override fun <R : Annotation> getAsAnnotationBoxArray(
         methodName: String
     ): Array<XAnnotationBox<R>> {
-        val values = getFieldValue<ArrayList<*>>(methodName) ?: return emptyArray()
+        val values = getFieldValue(methodName, Array::class.java) ?: return emptyArray()
         val annotationType = annotationClass.methods.first {
             it.name == methodName
         }.returnType.componentType as Class<R>
+        if (values.isEmpty()) {
+            // KSP is unable to read defaults and returns empty array in that case.
+            // Subsequently, we don't know if developer set it to empty array intentionally or
+            // left it to default.
+            // we error on the side of default
+            return KspReflectiveAnnotationBox.createFromDefaultValues(
+                env = env,
+                annotationClass = annotationClass,
+                methodName = methodName
+            )
+        }
         return values.map {
-            KspAnnotationBox<R>(
+            KspAnnotationBox(
                 env = env,
                 annotationClass = annotationType,
                 annotation = it as KSAnnotation
@@ -84,24 +110,52 @@
     }
 
     private val valueProxy: T = Proxy.newProxyInstance(
-        KspAnnotationBox::class.java.classLoader,
+        annotationClass.classLoader,
         arrayOf(annotationClass)
     ) { _, method, _ ->
-        val fieldValue = getFieldValue(method.name) ?: method.defaultValue
-        // java gives arrays, kotlin gives array list (sometimes?) so fix it up
-        when {
-            fieldValue == null -> null
-            method.returnType.isArray && (fieldValue is ArrayList<*>) -> {
-                val componentType = method.returnType.componentType!!
-                val result =
-                    java.lang.reflect.Array.newInstance(componentType, fieldValue.size) as Array<*>
-                fieldValue.toArray(result)
-                result
-            }
-            else -> fieldValue
-        }
+        getFieldValue(method.name, method.returnType) ?: method.defaultValue
     } as T
 
     override val value: T
         get() = valueProxy
 }
+
+@Suppress("UNCHECKED_CAST")
+private fun <R> Any.readAs(returnType: Class<R>): R? {
+    return when {
+        returnType.isArray -> {
+            val values = when (this) {
+                is List<*> -> {
+                    // KSP might return list for arrays. convert it back.
+                    this.mapNotNull {
+                        it?.readAs(returnType.componentType)
+                    }
+                }
+                is Array<*> -> mapNotNull { it?.readAs(returnType.componentType) }
+                else -> error("unexpected type for array: $this / ${this::class.java}")
+            }
+            val resultArray = java.lang.reflect.Array.newInstance(
+                returnType.componentType,
+                values.size
+            ) as Array<Any?>
+            values.forEachIndexed { index, value ->
+                resultArray[index] = value
+            }
+            resultArray
+        }
+        returnType.isEnum -> {
+            this.readAsEnum(returnType)
+        }
+        else -> this
+    } as R?
+}
+
+private fun <R> Any.readAsEnum(enumClass: Class<R>): R? {
+    val ksType = this as? KSType ?: return null
+    val classDeclaration = ksType.declaration as? KSClassDeclaration ?: return null
+    val enumValue = classDeclaration.simpleName.asString()
+    // get the instance from the valueOf function.
+    @Suppress("UNCHECKED_CAST", "BanUncheckedReflection")
+    return enumClass.getDeclaredMethod("valueOf", String::class.java)
+        .invoke(null, enumValue) as R?
+}
\ No newline at end of file
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspHasModifiers.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspHasModifiers.kt
index aa1add9..1ab2cce 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspHasModifiers.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspHasModifiers.kt
@@ -18,10 +18,10 @@
 
 import androidx.room.compiler.processing.XHasModifiers
 import com.google.devtools.ksp.getVisibility
+import com.google.devtools.ksp.isAbstract
 import com.google.devtools.ksp.isOpen
 import com.google.devtools.ksp.isPrivate
 import com.google.devtools.ksp.isProtected
-import com.google.devtools.ksp.isPublic
 import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.google.devtools.ksp.symbol.KSDeclaration
 import com.google.devtools.ksp.symbol.KSFunctionDeclaration
@@ -48,7 +48,13 @@
     }
 
     override fun isAbstract(): Boolean {
-        return declaration.modifiers.contains(Modifier.ABSTRACT)
+        return declaration.modifiers.contains(Modifier.ABSTRACT) ||
+            when (declaration) {
+                is KSPropertyDeclaration -> declaration.isAbstract()
+                is KSClassDeclaration -> declaration.isAbstract()
+                is KSFunctionDeclaration -> declaration.isAbstract
+                else -> false
+            }
     }
 
     override fun isPrivate(): Boolean {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt
index 557cfa3..1497d90 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt
@@ -23,8 +23,8 @@
 
 internal class KspMessager(
     private val logger: KSPLogger
-) : XMessager {
-    override fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+) : XMessager() {
+    override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
         val ksNode = (element as? KspElement)?.declaration
         when (kind) {
             Diagnostic.Kind.ERROR -> logger.error(msg, ksNode)
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBox.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBox.kt
new file mode 100644
index 0000000..3bdd7ae
--- /dev/null
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBox.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.ksp
+
+import androidx.annotation.VisibleForTesting
+import androidx.room.compiler.processing.XAnnotationBox
+import androidx.room.compiler.processing.XType
+
+/**
+ * KSP sometimes cannot read default values in annotations. This reflective implementation
+ * handles those cases.
+ * see: https://github.com/google/ksp/issues/53
+ */
+internal class KspReflectiveAnnotationBox<T : Annotation> @VisibleForTesting constructor(
+    private val env: KspProcessingEnv,
+    private val annotationClass: Class<T>,
+    private val annotation: T
+) : XAnnotationBox<T> {
+    override val value: T = annotation
+
+    override fun getAsType(methodName: String): XType? {
+        val value = getFieldValue<Class<*>>(methodName) ?: return null
+        return env.findType(value.kotlin)
+    }
+
+    override fun getAsTypeList(methodName: String): List<XType> {
+        val values = getFieldValue<Array<*>>(methodName)
+        return values?.filterIsInstance<Class<*>>()?.mapNotNull {
+            env.findType(it.kotlin)
+        } ?: emptyList()
+    }
+
+    override fun <T : Annotation> getAsAnnotationBox(methodName: String): XAnnotationBox<T> {
+        return createFromDefaultValue(
+            env = env,
+            annotationClass = annotationClass,
+            methodName = methodName
+        )
+    }
+
+    @Suppress("UNCHECKED_CAST", "BanUncheckedReflection")
+    override fun <T : Annotation> getAsAnnotationBoxArray(
+        methodName: String
+    ): Array<XAnnotationBox<T>> {
+        val method = annotationClass.methods.firstOrNull {
+            it.name == methodName
+        } ?: error("$annotationClass does not contain $methodName")
+        val values = method.invoke(annotation) as? Array<T> ?: return emptyArray()
+        return values.map {
+            KspReflectiveAnnotationBox(
+                env = env,
+                annotationClass = method.returnType.componentType as Class<T>,
+                annotation = it
+            )
+        }.toTypedArray()
+    }
+
+    @Suppress("UNCHECKED_CAST", "BanUncheckedReflection")
+    private fun <R : Any> getFieldValue(methodName: String): R? {
+        val value = annotationClass.methods.firstOrNull {
+            it.name == methodName
+        }?.invoke(annotation) ?: return null
+        return value as R?
+    }
+
+    companion object {
+        @Suppress("UNCHECKED_CAST")
+        fun <R : Annotation> createFromDefaultValue(
+            env: KspProcessingEnv,
+            annotationClass: Class<*>,
+            methodName: String
+        ): KspReflectiveAnnotationBox<R> {
+            val method = annotationClass.methods.firstOrNull {
+                it.name == methodName
+            } ?: error("$annotationClass does not contain $methodName")
+            val defaultValue = method.defaultValue
+                ?: error("$annotationClass.$method does not have a default value and is not set")
+            return KspReflectiveAnnotationBox(
+                env = env,
+                annotationClass = method.returnType as Class<R>,
+                annotation = defaultValue as R
+            )
+        }
+
+        @Suppress("UNCHECKED_CAST")
+        fun <R : Annotation> createFromDefaultValues(
+            env: KspProcessingEnv,
+            annotationClass: Class<*>,
+            methodName: String
+        ): Array<XAnnotationBox<R>> {
+            val method = annotationClass.methods.firstOrNull {
+                it.name == methodName
+            } ?: error("$annotationClass does not contain $methodName")
+            check(method.returnType.isArray) {
+                "expected ${method.returnType} to be an array. $method"
+            }
+            val defaultValue = method.defaultValue
+                ?: error("$annotationClass.$method does not have a default value and is not set")
+            val values: Array<R> = defaultValue as Array<R>
+            return values.map {
+                KspReflectiveAnnotationBox(
+                    env = env,
+                    annotationClass = method.returnType.componentType as Class<R>,
+                    annotation = it
+                )
+            }.toTypedArray()
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
index 1292542..c60d55a 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
@@ -125,6 +125,11 @@
 
     override fun isSameType(other: XType): Boolean {
         check(other is KspType)
+        if (nullability == XNullability.UNKNOWN || other.nullability == XNullability.UNKNOWN) {
+            // if one the nullabilities is unknown, it is coming from java source code or .class.
+            // for those cases, use java platform type equality (via typename)
+            return typeName == other.typeName
+        }
         // NOTE: this is inconsistent with java where nullability is ignored.
         // it is intentional but might be reversed if it happens to break use cases.
         return ksType == other.ksType
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index 60339a5..9884900 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -105,7 +105,11 @@
         } as ClassName
     }
 
-    private val _declaredPropertyFields by lazy {
+    /**
+     * This list includes fields for all properties in this class and its static companion
+     * properties. They are not necessarily fields as it might include properties of interfaces.
+     */
+    private val _declaredProperties by lazy {
         val declaredProperties = declaration.getDeclaredProperties()
         val companionProperties = declaration
             .findCompanionObject()
@@ -127,22 +131,32 @@
         // Read all properties from all supers and select the ones that are not overridden.
         // TODO: remove once it is implemented in KSP
         // https://github.com/android/kotlin/issues/133
-        val selectedNames = mutableSetOf<String>()
-        _declaredPropertyFields.forEach {
-            selectedNames.add(it.name)
+
+        val myPropertyFields = if (declaration.classKind == ClassKind.INTERFACE) {
+            _declaredProperties.filter {
+                it.isStatic()
+            }
+        } else {
+            _declaredProperties
+        }
+        val selectedNames = myPropertyFields.mapTo(mutableSetOf()) {
+            it.name
         }
         val selection = mutableListOf<KSPropertyDeclaration>()
         declaration.getAllSuperTypes().map {
             it.declaration
         }.filterIsInstance(KSClassDeclaration::class.java)
+            .filter {
+                it.classKind != ClassKind.INTERFACE
+            }
             .flatMap {
                 it.getDeclaredProperties().asSequence()
             }.forEach {
-                if (!selectedNames.contains(it.simpleName.asString())) {
+                if (selectedNames.add(it.simpleName.asString())) {
                     selection.add(it)
                 }
             }
-        _declaredPropertyFields + selection.map {
+        myPropertyFields + selection.map {
             KspFieldElement(
                 env = env,
                 declaration = it,
@@ -152,7 +166,7 @@
     }
 
     private val syntheticGetterSetterMethods: List<XMethodElement> by lazy {
-        val setters = _declaredPropertyFields.mapNotNull {
+        val setters = _declaredProperties.mapNotNull {
             if (it.type.ksType.isInline()) {
                 // KAPT does not generate getters/setters for inlines, we'll hide them as well
                 // until room generates kotlin code
@@ -174,7 +188,7 @@
                 null
             }
         }
-        val getters = _declaredPropertyFields.mapNotNull {
+        val getters = _declaredProperties.mapNotNull {
             if (it.type.ksType.isInline()) {
                 // KAPT does not generate getters/setters for inlines, we'll hide them as well
                 // until room generates kotlin code
@@ -317,4 +331,8 @@
         .filter {
             it.simpleName == this.simpleName
         }
+
+    override fun toString(): String {
+        return declaration.toString()
+    }
 }
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt
index 734f3b1a..06a1983 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt
@@ -75,7 +75,7 @@
         mapping["java.lang.Boolean"] = "kotlin.Boolean"
         // collections. default to mutable ones since java types are always mutable
         mapping["java.util.Iterator"] = "kotlin.collections.MutableIterator"
-        mapping["java.util.Iterable"] = "kotlin.collections.Iterable"
+        mapping["java.lang.Iterable"] = "kotlin.collections.Iterable"
         mapping["java.util.Collection"] = "kotlin.collections.MutableCollection"
         mapping["java.util.Set"] = "kotlin.collections.MutableSet"
         mapping["java.util.List"] = "kotlin.collections.MutableList"
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/ResolverExt.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/ResolverExt.kt
index e8a80d4..876eb4a 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/ResolverExt.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/ResolverExt.kt
@@ -25,7 +25,6 @@
 import com.google.devtools.ksp.symbol.KSFunctionDeclaration
 import com.google.devtools.ksp.symbol.KSPropertyAccessor
 import com.google.devtools.ksp.symbol.KSPropertyDeclaration
-import com.google.devtools.ksp.symbol.Origin
 
 internal fun Resolver.findClass(qName: String) = getClassDeclarationByName(
     getKSNameFromString(qName)
@@ -92,12 +91,7 @@
 }
 
 private fun KSPropertyDeclaration.overrides(other: KSPropertyDeclaration): Boolean {
-    val overridee = try {
-        findOverridee()
-    } catch (ex: NoSuchElementException) {
-        // workaround for https://github.com/google/ksp/issues/174
-        null
-    }
+    val overridee = findOverridee()
     if (overridee == other) {
         return true
     }
@@ -108,10 +102,6 @@
 internal fun Resolver.safeGetJvmName(
     declaration: KSFunctionDeclaration
 ): String {
-    if (declaration.origin == Origin.JAVA) {
-        // https://github.com/google/ksp/issues/170
-        return declaration.simpleName.asString()
-    }
     return try {
         getJvmName(declaration)
     } catch (ignored: ClassCastException) {
@@ -126,10 +116,6 @@
     accessor: KSPropertyAccessor,
     fallback: () -> String
 ): String {
-    if (accessor.origin == Origin.JAVA) {
-        // https://github.com/google/ksp/issues/170
-        return fallback()
-    }
     return try {
         getJvmName(accessor)
     } catch (ignored: ClassCastException) {
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
index 49ad742..b31aa76 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
@@ -22,7 +22,7 @@
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.javaTypeUtils
 import androidx.room.compiler.processing.util.runKaptTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import com.google.auto.common.MoreTypes
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.MethodSpec
@@ -291,7 +291,7 @@
     private fun overridesCheck(source: Source, ignoreInheritedMethods: Boolean = false) {
         // first build golden image with Java processor so we can use JavaPoet's API
         val golden = buildMethodsViaJavaPoet(source, ignoreInheritedMethods)
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(source)
         ) { invocation ->
             val (target, methods) = invocation.getOverrideTestTargets(ignoreInheritedMethods)
@@ -316,8 +316,7 @@
     ): List<String> {
         lateinit var result: List<String>
         runKaptTest(
-            sources = listOf(source),
-            succeed = true
+            sources = listOf(source)
         ) { invocation ->
             val (target, methods) = invocation.getOverrideTestTargets(
                 ignoreInheritedMethods
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
index 7a9a0ac..40cab24 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.room.compiler.processing
 
+import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults
+import androidx.room.compiler.processing.testcode.JavaEnum
 import androidx.room.compiler.processing.testcode.MainAnnotation
 import androidx.room.compiler.processing.testcode.OtherAnnotation
 import androidx.room.compiler.processing.util.Source
@@ -23,13 +25,15 @@
 import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import com.squareup.javapoet.ClassName
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import java.util.LinkedHashMap
 
 @RunWith(JUnit4::class)
 class XAnnotationBoxTest {
@@ -44,7 +48,6 @@
             }
             """.trimIndent()
         )
-        // TODO add KSP once https://github.com/google/ksp/issues/96 is fixed.
         runProcessorTest(
             sources = listOf(source)
         ) {
@@ -83,7 +86,8 @@
             }
             """.trimIndent()
         )
-        runProcessorTest(
+        // re-enable after fixing b/175144186
+        runProcessorTestWithoutKsp(
             listOf(mySource)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -125,7 +129,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("Subject")
@@ -165,7 +169,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(mySource)
         ) { invocation ->
             val element = invocation.processingEnv.requireTypeElement("Subject")
@@ -225,7 +229,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("Subject")
 
             subject.getField("prop1").assertHasSuppressWithValue("onProp1")
@@ -276,7 +280,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("Subject")
             subject.getMethod("noAnnotations").let { method ->
                 method.assertDoesNotHaveAnnotation()
@@ -305,7 +309,7 @@
             )
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("Subject")
             subject.assertHasSuppressWithValue("onClass")
             val constructor = subject.getConstructors().single()
@@ -316,6 +320,76 @@
         }
     }
 
+    @Test
+    fun defaultValues() {
+        val kotlinSrc = Source.kotlin(
+            "KotlinClass.kt",
+            """
+            import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults
+            @JavaAnnotationWithDefaults
+            class KotlinClass
+            """.trimIndent()
+        )
+        val javaSrc = Source.java(
+            "JavaClass.java",
+            """
+            import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults;
+            @JavaAnnotationWithDefaults
+            class JavaClass {}
+            """.trimIndent()
+        )
+        runProcessorTest(sources = listOf(kotlinSrc, javaSrc)) { invocation ->
+            listOf("KotlinClass", "JavaClass")
+                .map {
+                    invocation.processingEnv.requireTypeElement(it)
+                }.forEach { typeElement ->
+                    val annotation =
+                        typeElement.toAnnotationBox(JavaAnnotationWithDefaults::class)
+                    checkNotNull(annotation)
+                    assertThat(annotation.value.intVal).isEqualTo(3)
+                    assertThat(annotation.value.stringArrayVal).isEqualTo(arrayOf("x", "y"))
+                    assertThat(annotation.value.stringVal).isEqualTo("foo")
+                    assertThat(
+                        annotation.getAsType("typeVal")?.rawType?.typeName
+                    ).isEqualTo(
+                        ClassName.get(HashMap::class.java)
+                    )
+                    assertThat(
+                        annotation.getAsTypeList("typeArrayVal").map {
+                            it.rawType.typeName
+                        }
+                    ).isEqualTo(
+                        listOf(ClassName.get(LinkedHashMap::class.java))
+                    )
+
+                    assertThat(
+                        annotation.value.enumVal
+                    ).isEqualTo(
+                        JavaEnum.DEFAULT
+                    )
+
+                    assertThat(
+                        annotation.value.enumArrayVal
+                    ).isEqualTo(
+                        arrayOf(JavaEnum.VAL1, JavaEnum.VAL2)
+                    )
+
+                    assertThat(
+                        annotation.getAsAnnotationBox<OtherAnnotation>("otherAnnotationVal")
+                            .value.value
+                    ).isEqualTo("def")
+
+                    assertThat(
+                        annotation
+                            .getAsAnnotationBoxArray<OtherAnnotation>("otherAnnotationArrayVal")
+                            .map {
+                                it.value.value
+                            }
+                    ).containsExactly("v1")
+                }
+        }
+    }
+
     // helper function to read what we need
     private fun XAnnotated.getSuppressValues(): Array<String>? {
         return this.toAnnotationBox(SuppressWarnings::class)?.value?.value
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
index 1f3260d..9502862 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
@@ -23,7 +23,6 @@
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -43,7 +42,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(source)
         ) { invocation ->
             val type = invocation.processingEnv
@@ -54,7 +53,8 @@
             assertThat(type.typeName).isEqualTo(
                 ArrayTypeName.of(String::class.java)
             )
-            type.asArray().componentType.let { component ->
+            check(type.isArray())
+            type.componentType.let { component ->
                 assertThat(component.typeName).isEqualTo(String::class.typeName())
                 assertThat(component.nullability).isEqualTo(XNullability.UNKNOWN)
             }
@@ -63,12 +63,12 @@
 
     @Test
     fun synthetic() {
-        runProcessorTestIncludingKsp {
+        runProcessorTest {
             val objArray = it.processingEnv.getArrayType(
                 TypeName.OBJECT
             )
-            assertThat(objArray.isArray()).isTrue()
-            assertThat(objArray.asArray().componentType.typeName).isEqualTo(
+            check(objArray.isArray())
+            assertThat(objArray.componentType.typeName).isEqualTo(
                 TypeName.OBJECT
             )
             assertThat(objArray.typeName).isEqualTo(
@@ -89,7 +89,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(source)
         ) { invocation ->
             val element = invocation.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -101,14 +101,15 @@
                     ArrayTypeName.of(String::class.java)
                 )
             }
-
-            nonNull.asArray().componentType.let { component ->
+            check(nonNull.isArray())
+            nonNull.componentType.let { component ->
                 assertThat(component.typeName).isEqualTo(
                     String::class.typeName()
                 )
                 assertThat(component.nullability).isEqualTo(XNullability.NONNULL)
             }
-            nullable.asArray().componentType.let { component ->
+            check(nullable.isArray())
+            nullable.componentType.let { component ->
                 assertThat(component.typeName).isEqualTo(String::class.typeName())
                 assertThat(component.nullability).isEqualTo(XNullability.NULLABLE)
             }
@@ -140,7 +141,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(listOf(src)) {
+        runProcessorTest(listOf(src)) {
             val subject = it.processingEnv.requireTypeElement("Subject")
             val types = subject.getAllFieldsIncludingPrivateSupers().map {
                 assertWithMessage(it.name).that(it.type.isArray()).isTrue()
@@ -170,8 +171,7 @@
     @Test
     fun createArray() {
         runKspTest(
-            sources = emptyList(),
-            succeed = true
+            sources = emptyList()
         ) { invocation ->
             val intType = invocation.processingEnv.requireType("kotlin.Int")
             invocation.processingEnv.getArrayType(intType).let {
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
index d84af6e..84623ee 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
@@ -22,8 +22,8 @@
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.getParameter
+import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.TypeName
@@ -36,7 +36,7 @@
 class XElementTest {
     @Test
     fun modifiers() {
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(
                 Source.java(
                     "foo.bar.Baz",
@@ -146,7 +146,7 @@
                 }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(genericBase, boundedChild)
         ) {
             fun validateElement(element: XTypeElement, tTypeName: TypeName, rTypeName: TypeName) {
@@ -210,7 +210,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -257,7 +257,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("java.lang.Object")
@@ -280,7 +280,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(subject)
         ) {
             val inner = ClassName.get("foo.bar", "Baz.Inner")
@@ -317,7 +317,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -350,7 +350,8 @@
             }
             """.trimIndent()
         )
-        runProcessorTest(
+        // enable once https://github.com/google/ksp/issues/167 is fixed
+        runProcessorTestWithoutKsp(
             sources = listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -371,9 +372,14 @@
 
     @Test
     fun toStringMatchesUnderlyingElement() {
-        runProcessorTest {
-            it.processingEnv.findTypeElement("java.util.List").let { list ->
-                assertThat(list.toString()).isEqualTo("java.util.List")
+        runProcessorTest { invocation ->
+            invocation.processingEnv.findTypeElement("java.util.List").let { list ->
+                val expected = if (invocation.isKsp) {
+                    "MutableList"
+                } else {
+                    "java.util.List"
+                }
+                assertThat(list.toString()).isEqualTo(expected)
             }
         }
     }
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index a270b74..676bc07 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -23,7 +23,7 @@
 import androidx.room.compiler.processing.util.getDeclaredMethod
 import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.getParameter
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
@@ -38,7 +38,7 @@
 class XExecutableElementTest {
     @Test
     fun basic() {
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(
                 Source.java(
                     "foo.bar.Baz",
@@ -69,8 +69,9 @@
                 assertThat(method.isOverrideableIgnoringContainer()).isTrue()
                 assertThat(method.parameters).hasSize(1)
                 method.getParameter("param1").let { param ->
-                    assertThat(param.type.isArray()).isTrue()
-                    assertThat(param.type.asArray().componentType.typeName)
+                    val paramType = param.type
+                    check(paramType.isArray())
+                    assertThat(paramType.componentType.typeName)
                         .isEqualTo(String::class.typeName())
                 }
                 assertThat(method.returnType.typeName).isEqualTo(String::class.typeName())
@@ -89,7 +90,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(subject)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -108,7 +109,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(subject)
         ) {
             val element = it.processingEnv.requireTypeElement("Subject")
@@ -138,7 +139,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(subject)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -182,7 +183,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(src)
         ) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("Subject")
@@ -269,7 +270,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val klass = invocation.processingEnv.requireTypeElement("MyDataClass")
             val methodNames = klass.getAllMethods().map {
                 it.name
@@ -323,7 +324,7 @@
             class NullableSubject: Base<String?>()
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(source)) { invocation ->
+        runProcessorTest(sources = listOf(source)) { invocation ->
             val base = invocation.processingEnv.requireTypeElement("Base")
             val subject = invocation.processingEnv.requireType("Subject")
                 .asDeclaredType()
@@ -386,7 +387,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src, javaSrc)) { invocation ->
+        runProcessorTest(sources = listOf(src, javaSrc)) { invocation ->
             val base = invocation.processingEnv.requireTypeElement("MyInterface")
             val impl = invocation.processingEnv.requireTypeElement("MyImpl")
             val javaImpl = invocation.processingEnv.requireTypeElement("JavaImpl")
@@ -461,4 +462,53 @@
             }
         }
     }
+
+    @Test
+    fun isAbstract() {
+        val javaInterface = Source.java(
+            "JavaInterface",
+            """
+            interface JavaInterface {
+                void interfaceMethod();
+            }
+            """.trimIndent()
+        )
+        val javaAbstractClass = Source.java(
+            "JavaAbstractClass",
+            """
+            abstract class JavaAbstractClass {
+                abstract void abstractMethod();
+                void nonAbstractMethod() {}
+            }
+            """.trimIndent()
+        )
+        val kotlinSource = Source.kotlin(
+            "kotlin.kt",
+            """
+            interface KotlinInterface {
+                fun interfaceMethod(): Unit
+            }
+            abstract class KotlinAbstractClass {
+                abstract fun abstractMethod(): Unit
+                fun nonAbstractMethod() {}
+            }
+            """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(javaInterface, javaAbstractClass, kotlinSource)
+        ) { invocation ->
+            listOf("JavaInterface", "KotlinInterface").forEach { qName ->
+                invocation.processingEnv.requireTypeElement(qName).let {
+                    assertThat(it.getMethod("interfaceMethod").isAbstract()).isTrue()
+                }
+            }
+
+            listOf("JavaAbstractClass", "KotlinAbstractClass").forEach { qName ->
+                invocation.processingEnv.requireTypeElement(qName).let {
+                    assertThat(it.getMethod("abstractMethod").isAbstract()).isTrue()
+                    assertThat(it.getMethod("nonAbstractMethod").isAbstract()).isFalse()
+                }
+            }
+        }
+    }
 }
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
index b2a03e5..38b007d 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
@@ -20,7 +20,7 @@
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.UNIT_CLASS_NAME
 import androidx.room.compiler.processing.util.getMethod
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ParameterizedTypeName
@@ -43,7 +43,7 @@
             abstract class Subject : MyInterface<String>
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(src)
         ) { invocation ->
             val myInterface = invocation.processingEnv.requireTypeElement("MyInterface")
@@ -112,7 +112,7 @@
             abstract class NullableSubject: MyInterface<String?>
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val myInterface = invocation.processingEnv.requireTypeElement("MyInterface")
 
             // helper method to get executable types both from sub class and also as direct child of
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
index 859e553..b808f2c 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
@@ -23,8 +23,8 @@
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.getParameter
+import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.TypeName
 import org.junit.Test
@@ -69,7 +69,7 @@
             """.trimIndent()
         )
         // TODO run with KSP once https://github.com/google/ksp/issues/167 is fixed
-        runProcessorTest(
+        runProcessorTestWithoutKsp(
             sources = listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -164,7 +164,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -257,7 +257,7 @@
 
     @Test
     fun changeNullability_primitives() {
-        runProcessorTestIncludingKsp { invocation ->
+        runProcessorTest { invocation ->
             PRIMITIVE_TYPES.forEach { primitiveTypeName ->
                 val primitive = invocation.processingEnv.requireType(primitiveTypeName)
                 assertThat(primitive.nullability).isEqualTo(NONNULL)
@@ -295,7 +295,7 @@
                 }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(javaSrc, kotlinSrc)) { invocation ->
+        runProcessorTest(sources = listOf(javaSrc, kotlinSrc)) { invocation ->
             listOf("KotlinClass", "JavaClass").forEach {
                 val subject = invocation.processingEnv.requireTypeElement(it)
                     .getField("subject").type
@@ -316,7 +316,7 @@
 
     @Test
     fun changeNullability_declared() {
-        runProcessorTestIncludingKsp { invocation ->
+        runProcessorTest { invocation ->
             val subject = invocation.processingEnv.requireType("java.util.List")
             subject.makeNullable().let {
                 assertThat(it.nullability).isEqualTo(NULLABLE)
@@ -337,7 +337,7 @@
 
     @Test
     fun changeNullability_arrayTypes() {
-        runProcessorTestIncludingKsp { invocation ->
+        runProcessorTest { invocation ->
             val subject = invocation.processingEnv.getArrayType(
                 invocation.processingEnv.requireType("java.util.List")
             )
@@ -368,7 +368,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val voidType = invocation.processingEnv.requireTypeElement("Foo")
                 .getMethod("subject").returnType
             assertThat(voidType.typeName).isEqualTo(TypeName.VOID)
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
index 4ae8c01..2f92301 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
@@ -17,8 +17,7 @@
 package androidx.room.compiler.processing
 
 import androidx.room.compiler.processing.util.Source
-import androidx.room.compiler.processing.util.runProcessorTestForFailedCompilation
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.JavaFile
@@ -34,7 +33,7 @@
 class XProcessingEnvTest {
     @Test
     fun getElement() {
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(
                 Source.java(
                     "foo.bar.Baz",
@@ -98,7 +97,7 @@
 
     @Test
     fun basic() {
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(
                 Source.java(
                     "foo.bar.Baz",
@@ -138,7 +137,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(source)
         ) { invocation ->
             PRIMITIVE_TYPES.flatMap {
@@ -163,7 +162,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) {
+        runProcessorTest(sources = listOf(src)) {
             it.processingEnv.requireTypeElement("foo.bar.Outer.Inner").let {
                 val className = it.className
                 assertThat(className.packageName()).isEqualTo("foo.bar")
@@ -175,7 +174,7 @@
 
     @Test
     fun findGeneratedAnnotation() {
-        runProcessorTestIncludingKsp { invocation ->
+        runProcessorTest { invocation ->
             val generatedAnnotation = invocation.processingEnv.findGeneratedAnnotation()
             assertThat(generatedAnnotation?.name).isEqualTo("Generated")
         }
@@ -200,7 +199,7 @@
             """.trimIndent()
         )
         listOf(javaSrc, kotlinSrc).forEach { src ->
-            runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+            runProcessorTest(sources = listOf(src)) { invocation ->
                 val className = ClassName.get("foo.bar", "ToBeGenerated")
                 if (invocation.processingEnv.findTypeElement(className) == null) {
                     // generate only if it doesn't exist to handle multi-round
@@ -223,14 +222,18 @@
             class Foo {}
             """.trimIndent()
         )
-        // TODO include KSP when https://github.com/google/ksp/issues/122 is fixed.
-        runProcessorTestForFailedCompilation(
+        runProcessorTest(
             sources = listOf(src)
         ) {
             it.processingEnv.messager.printMessage(
                 Diagnostic.Kind.ERROR,
                 "intentional failure"
             )
+            it.assertCompilationResult {
+                compilationDidFail()
+                    .and()
+                    .hasError("intentional failure")
+            }
         }
     }
 
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index 2cd2588..814425b 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.compiler.processing
 
+import androidx.room.compiler.processing.ksp.ERROR_TYPE_NAME
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.className
 import androidx.room.compiler.processing.util.getDeclaredMethod
@@ -25,8 +26,6 @@
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.compiler.processing.util.runProcessorTestForFailedCompilation
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
@@ -53,7 +52,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(parent)
         ) {
             val type = it.processingEnv.requireType("foo.bar.Parent") as XDeclaredType
@@ -110,22 +109,28 @@
                 }
             """.trimIndent()
         )
-        // TODO run with KSP as well once https://github.com/google/ksp/issues/107 is resolved
-        runProcessorTestForFailedCompilation(
+
+        runProcessorTest(
             sources = listOf(missingTypeRef)
         ) {
+            val errorTypeName = if (it.isKsp) {
+                // in ksp, we lose the name when resolving the type.
+                // b/175246617
+                ERROR_TYPE_NAME
+            } else {
+                ClassName.get("", "NotExistingType")
+            }
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
             element.getField("badField").let { field ->
                 assertThat(field.type.isError()).isTrue()
-                assertThat(field.type.typeName).isEqualTo(
-                    ClassName.get("", "NotExistingType")
-                )
+                assertThat(field.type.typeName).isEqualTo(errorTypeName)
             }
             element.getDeclaredMethod("badMethod").let { method ->
                 assertThat(method.returnType.isError()).isTrue()
-                assertThat(method.returnType.typeName).isEqualTo(
-                    ClassName.get("", "NotExistingType")
-                )
+                assertThat(method.returnType.typeName).isEqualTo(errorTypeName)
+            }
+            it.assertCompilationResult {
+                compilationDidFail()
             }
         }
     }
@@ -141,7 +146,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(subject)
         ) {
             val type = it.processingEnv.requireType("foo.bar.Baz")
@@ -154,6 +159,56 @@
     }
 
     @Test
+    fun sameType_kotlinJava() {
+        val javaSrc = Source.java(
+            "JavaClass",
+            """
+            class JavaClass {
+                int intField;
+                Integer integerField;
+            }
+            """.trimIndent()
+        )
+        val kotlinSrc = Source.kotlin(
+            "Foo.kt",
+            """
+            class KotlinClass {
+                val intProp: Int = 0
+                val integerProp : Int? = null
+            }
+            """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(javaSrc, kotlinSrc)
+        ) { invocation ->
+            val javaElm = invocation.processingEnv.requireTypeElement("JavaClass")
+            val kotlinElm = invocation.processingEnv.requireTypeElement("KotlinClass")
+            fun XFieldElement.isSameType(other: XFieldElement): Boolean {
+                return type.isSameType(other.type)
+            }
+            val fields = javaElm.getAllFieldsIncludingPrivateSupers() +
+                kotlinElm.getAllFieldsIncludingPrivateSupers()
+            val results = fields.flatMap { f1 ->
+                fields.map { f2 ->
+                    f1 to f2
+                }.filter { (first, second) ->
+                    first.isSameType(second)
+                }
+            }.map { (first, second) ->
+                first.name to second.name
+            }
+
+            val expected = setOf(
+                "intField" to "intProp",
+                "intProp" to "intField",
+                "integerField" to "integerProp",
+                "integerProp" to "integerField"
+            ) + fields.map { it.name to it.name }.toSet()
+            assertThat(results).containsExactlyElementsIn(expected)
+        }
+    }
+
+    @Test
     fun isCollection() {
         runProcessorTest {
             it.processingEnv.requireType("java.util.List").let { list ->
@@ -174,7 +229,7 @@
 
     @Test
     fun isCollection_kotlin() {
-        runKspTest(sources = emptyList(), succeed = true) { invocation ->
+        runKspTest(sources = emptyList()) { invocation ->
             val subjects = listOf("Map" to false, "List" to true, "Set" to true)
             subjects.forEach { (subject, expected) ->
                 invocation.processingEnv.requireType("kotlin.collections.$subject").let { type ->
@@ -187,7 +242,7 @@
 
     @Test
     fun toStringMatchesUnderlyingElement() {
-        runProcessorTestIncludingKsp {
+        runProcessorTest {
             val subject = "java.lang.String"
             val expected = if (it.isKsp) {
                 it.kspResolver.getClassDeclarationByName(subject)?.toString()
@@ -212,12 +267,14 @@
                 }
             """.trimIndent()
         )
-        // TODO run with KSP as well once https://github.com/google/ksp/issues/107 is resolved
-        runProcessorTestForFailedCompilation(
+        runProcessorTest(
             sources = listOf(missingTypeRef)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
             assertThat(element.superType?.isError()).isTrue()
+            it.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -256,7 +313,7 @@
 
     @Test
     fun rawType() {
-        runProcessorTestIncludingKsp {
+        runProcessorTest {
             val subject = it.processingEnv.getDeclaredType(
                 it.processingEnv.requireTypeElement(List::class),
                 it.processingEnv.requireType(String::class)
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
index 679af1c..7251136 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
@@ -18,7 +18,7 @@
 
 import androidx.room.compiler.processing.javac.JavacProcessingEnv
 import androidx.room.compiler.processing.util.Source
-import androidx.room.compiler.processing.util.runProcessorTest
+import androidx.room.compiler.processing.util.runKaptTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.AssumptionViolatedException
 import org.junit.Test
@@ -478,7 +478,7 @@
         sources: List<Source> = emptyList(),
         handler: (ProcessingEnvironment) -> Unit
     ) {
-        runProcessorTest(sources) {
+        runKaptTest(sources) {
             val processingEnv = it.processingEnv
             if (processingEnv !is JavacProcessingEnv) {
                 throw AssumptionViolatedException("This test only works for java/kapt compilation")
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
index 1bc7787..5cfa2d6 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
@@ -48,7 +48,7 @@
             """.trimIndent()
         )
 
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val base = invocation.processingEnv.requireTypeElement("BaseClass")
             val sub = invocation.processingEnv.requireType("SubClass").asDeclaredType()
             base.getField("normalInt").let { prop ->
@@ -121,7 +121,7 @@
             abstract class NullableSubject: MyInterface<String?>()
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val myInterface = invocation.processingEnv.requireTypeElement("MyInterface")
             val nonNullSubject = invocation.processingEnv.requireType("NonNullSubject")
                 .asDeclaredType()
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
index c185f47..2865715 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
@@ -26,7 +26,6 @@
 import com.google.common.truth.Truth.assertThat
 import com.google.devtools.ksp.getDeclaredFunctions
 import com.google.devtools.ksp.getDeclaredProperties
-import com.google.devtools.ksp.processing.Resolver
 import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
@@ -55,18 +54,18 @@
             }
             """.trimIndent()
         )
-        runTest(subjectSrc) { resolver ->
-            val subject = resolver.requireClass("foo.bar.Baz")
-            assertThat(subject.propertyType("intField").typeName(resolver))
+        runKspTest(sources = listOf(subjectSrc)) { invocation ->
+            val subject = invocation.kspResolver.requireClass("foo.bar.Baz")
+            assertThat(subject.propertyType("intField").typeName(invocation.kspResolver))
                 .isEqualTo(TypeName.INT)
-            assertThat(subject.propertyType("listOfInts").typeName(resolver))
+            assertThat(subject.propertyType("listOfInts").typeName(invocation.kspResolver))
                 .isEqualTo(
                     ParameterizedTypeName.get(
                         List::class.className(),
                         TypeName.INT.box()
                     )
                 )
-            assertThat(subject.propertyType("mutableMapOfAny").typeName(resolver))
+            assertThat(subject.propertyType("mutableMapOfAny").typeName(invocation.kspResolver))
                 .isEqualTo(
                     ParameterizedTypeName.get(
                         Map::class.className(),
@@ -74,7 +73,7 @@
                         TypeName.OBJECT,
                     )
                 )
-            val typeName = subject.propertyType("nested").typeName(resolver)
+            val typeName = subject.propertyType("nested").typeName(invocation.kspResolver)
             check(typeName is ClassName)
             assertThat(typeName.packageName()).isEqualTo("foo.bar")
             assertThat(typeName.simpleNames()).containsExactly("Baz", "Nested")
@@ -97,22 +96,25 @@
             }
             """.trimIndent()
         )
-        runTest(subjectSrc) { resolver ->
-            val subject = resolver.requireClass("Baz")
-            assertThat(subject.propertyType("intField").typeName(resolver))
-                .isEqualTo(TypeName.INT)
-            assertThat(subject.propertyType("listOfInts").typeName(resolver))
-                .isEqualTo(
-                    ParameterizedTypeName.get(
-                        List::class.className(),
-                        TypeName.INT.box()
-                    )
+        runKspTest(sources = listOf(subjectSrc)) { invocation ->
+            val subject = invocation.kspResolver.requireClass("Baz")
+            assertThat(
+                subject.propertyType("intField").typeName(invocation.kspResolver)
+            ).isEqualTo(TypeName.INT)
+            assertThat(
+                subject.propertyType("listOfInts").typeName(invocation.kspResolver)
+            ).isEqualTo(
+                ParameterizedTypeName.get(
+                    List::class.className(),
+                    TypeName.INT.box()
                 )
-            assertThat(subject.propertyType("incompleteGeneric").typeName(resolver))
-                .isEqualTo(
-                    List::class.className()
-                )
-            assertThat(subject.propertyType("nested").typeName(resolver))
+            )
+            assertThat(
+                subject.propertyType("incompleteGeneric").typeName(invocation.kspResolver)
+            ).isEqualTo(
+                List::class.className()
+            )
+            assertThat(subject.propertyType("nested").typeName(invocation.kspResolver))
                 .isEqualTo(
                     ClassName.get("", "Baz", "Nested")
                 )
@@ -132,25 +134,31 @@
             }
             """.trimIndent()
         )
-        runTest(subjectSrc, succeed = false) { resolver ->
-            val subject = resolver.requireClass("Foo")
-            assertThat(subject.propertyType("errorField").typeName(resolver))
-                .isEqualTo(ERROR_TYPE_NAME)
-            assertThat(subject.propertyType("listOfError").typeName(resolver))
-                .isEqualTo(
-                    ParameterizedTypeName.get(
-                        List::class.className(),
-                        ERROR_TYPE_NAME
-                    )
+        runKspTest(sources = listOf(subjectSrc)) { invocation ->
+            val subject = invocation.kspResolver.requireClass("Foo")
+            assertThat(
+                subject.propertyType("errorField").typeName(invocation.kspResolver)
+            ).isEqualTo(ERROR_TYPE_NAME)
+            assertThat(
+                subject.propertyType("listOfError").typeName(invocation.kspResolver)
+            ).isEqualTo(
+                ParameterizedTypeName.get(
+                    List::class.className(),
+                    ERROR_TYPE_NAME
                 )
-            assertThat(subject.propertyType("mutableMapOfDontExist").typeName(resolver))
-                .isEqualTo(
-                    ParameterizedTypeName.get(
-                        Map::class.className(),
-                        String::class.className(),
-                        ERROR_TYPE_NAME
-                    )
+            )
+            assertThat(
+                subject.propertyType("mutableMapOfDontExist").typeName(invocation.kspResolver)
+            ).isEqualTo(
+                ParameterizedTypeName.get(
+                    Map::class.className(),
+                    String::class.className(),
+                    ERROR_TYPE_NAME
                 )
+            )
+            invocation.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -181,8 +189,7 @@
         // methodName -> returnType, ...paramTypes
         val golden = mutableMapOf<String, List<TypeName>>()
         runKaptTest(
-            sources = listOf(src),
-            succeed = true
+            sources = listOf(src)
         ) { invocation ->
             val env = (invocation.processingEnv as JavacProcessingEnv)
             val subject = env.delegate.elementUtils.getTypeElement("Subject")
@@ -196,8 +203,7 @@
         }
         val kspResults = mutableMapOf<String, List<TypeName>>()
         runKspTest(
-            sources = listOf(src),
-            succeed = true
+            sources = listOf(src)
         ) { invocation ->
             val env = (invocation.processingEnv as KspProcessingEnv)
             val subject = env.resolver.requireClass("Subject")
@@ -220,21 +226,6 @@
         assertThat(kspResults).containsExactlyEntriesIn(golden)
     }
 
-    private fun runTest(
-        vararg sources: Source,
-        succeed: Boolean = true,
-        handler: (Resolver) -> Unit
-    ) {
-        runKspTest(
-            sources = sources.toList(),
-            succeed = succeed
-        ) {
-            handler(
-                (it.processingEnv as KspProcessingEnv).resolver
-            )
-        }
-    }
-
     private fun KSClassDeclaration.requireProperty(name: String) = getDeclaredProperties().first {
         it.simpleName.asString() == name
     }
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
index 1e8a5a2..f6666c4 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
@@ -25,7 +25,7 @@
 import androidx.room.compiler.processing.util.className
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.getField
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -152,7 +152,7 @@
             class Sub1 : Base<Int, String>()
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val sub = invocation.processingEnv.requireTypeElement("Sub1")
             val base = invocation.processingEnv.requireTypeElement("Base")
             val t = base.getField("t")
@@ -197,13 +197,13 @@
     private fun runModifierTest(vararg inputs: ModifierTestInput) {
         // we'll run the test twice. once it is in source and once it is coming from a dependency.
         val sources = inputs.map(ModifierTestInput::source)
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = sources
         ) { invocation ->
             assertModifiers(invocation, inputs)
         }
         val classpath = compileFiles(sources)
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = emptyList(),
             classpath = listOf(classpath)
         ) { invocation ->
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt
new file mode 100644
index 0000000..8c694265
--- /dev/null
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.ksp
+
+import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
+import org.junit.Test
+import kotlin.reflect.KClass
+
+class KspReflectiveAnnotationBoxTest {
+    enum class TestEnum {
+        VAL1,
+        VAL2
+    }
+
+    annotation class TestAnnotation(
+        val strProp: String = "abc",
+        val intProp: Int = 3,
+        val enumProp: TestEnum = TestEnum.VAL2,
+        val enumArrayProp: Array<TestEnum> = [TestEnum.VAL1, TestEnum.VAL2, TestEnum.VAL1],
+        val annProp: TestAnnotation2 = TestAnnotation2(3),
+        val annArrayProp: Array<TestAnnotation2> = [TestAnnotation2(1), TestAnnotation2(5)],
+        val typeProp: KClass<*> = Int::class,
+        val typeArrayProp: Array<KClass<*>> = [Int::class, String::class]
+    )
+
+    annotation class TestAnnotation2(
+        val intProp: Int = 0
+    )
+
+    @Test
+    @TestAnnotation // putting annotation here to read it back easily :)
+    fun simple() {
+        runKspTest(sources = emptyList()) { invocation ->
+            val box = KspReflectiveAnnotationBox(
+                env = invocation.processingEnv as KspProcessingEnv,
+                annotationClass = TestAnnotation::class.java,
+                annotation = getAnnotationOnMethod("simple")
+            )
+            assertThat(box.value.strProp).isEqualTo("abc")
+            assertThat(box.value.intProp).isEqualTo(3)
+            assertThat(box.value.enumProp).isEqualTo(TestEnum.VAL2)
+            assertThat(box.value.enumArrayProp).isEqualTo(
+                arrayOf(TestEnum.VAL1, TestEnum.VAL2, TestEnum.VAL1)
+            )
+            box.getAsAnnotationBox<TestAnnotation2>("annProp").let {
+                assertThat(it.value.intProp).isEqualTo(3)
+            }
+            box.getAsAnnotationBoxArray<TestAnnotation2>("annArrayProp").let {
+                assertThat(
+                    it.map { it.value.intProp }
+                ).containsExactly(1, 5)
+            }
+            box.getAsType("typeProp")?.let {
+                assertThat(it is KspType).isTrue()
+                assertThat(it.typeName).isEqualTo(TypeName.INT)
+            }
+            box.getAsTypeList("typeArrayProp").let {
+                assertThat(it.all { it is KspType }).isTrue()
+                assertThat(it.map { it.typeName }).containsExactly(
+                    TypeName.INT, ClassName.get(String::class.java)
+                )
+            }
+        }
+    }
+
+    private inline fun <reified T : Annotation> getAnnotationOnMethod(methodName: String): T {
+        return KspReflectiveAnnotationBoxTest::class.java.getMethod(methodName).annotations
+            .first {
+                it is TestAnnotation
+            } as T
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeElementTest.kt
index 437bdc3..869c1b1 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeElementTest.kt
@@ -19,11 +19,12 @@
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.getAllFieldNames
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.runKspTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
@@ -51,9 +52,8 @@
             class InFooBar
             """.trimIndent()
         )
-        runKspTest(
-            sources = listOf(src1, src2),
-            succeed = true
+        runProcessorTest(
+            sources = listOf(src1, src2)
         ) { invocation ->
             invocation.processingEnv.requireTypeElement("TopLevel").let {
                 assertThat(it.packageName).isEqualTo("")
@@ -67,16 +67,19 @@
                 assertThat(it.qualifiedName).isEqualTo("foo.bar.InFooBar")
                 assertThat(it.className).isEqualTo(ClassName.get("foo.bar", "InFooBar"))
             }
-            invocation.processingEnv.requireTypeElement("java.lang.Integer").let {
-                // always return kotlin types, this is what compiler does
-                assertThat(it.packageName).isEqualTo("kotlin")
-                assertThat(it.name).isEqualTo("Int")
-                assertThat(it.qualifiedName).isEqualTo("kotlin.Int")
-            }
-            invocation.processingEnv.requireTypeElement("kotlin.Int").let {
-                assertThat(it.packageName).isEqualTo("kotlin")
-                assertThat(it.name).isEqualTo("Int")
-                assertThat(it.qualifiedName).isEqualTo("kotlin.Int")
+            if (invocation.isKsp) {
+                // these are KSP specific tests, typenames are tested elsewhere
+                invocation.processingEnv.requireTypeElement("java.lang.Integer").let {
+                    // always return kotlin types, this is what compiler does
+                    assertThat(it.packageName).isEqualTo("kotlin")
+                    assertThat(it.name).isEqualTo("Int")
+                    assertThat(it.qualifiedName).isEqualTo("kotlin.Int")
+                }
+                invocation.processingEnv.requireTypeElement("kotlin.Int").let {
+                    assertThat(it.packageName).isEqualTo("kotlin")
+                    assertThat(it.name).isEqualTo("Int")
+                    assertThat(it.qualifiedName).isEqualTo("kotlin.Int")
+                }
             }
         }
     }
@@ -93,7 +96,7 @@
             interface MyInterface {}
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             invocation.processingEnv.requireTypeElement("foo.bar.Baz").let {
                 assertThat(it.superType).isEqualTo(
                     invocation.processingEnv.requireType("foo.bar.AbstractClass")
@@ -106,7 +109,16 @@
                 assertThat(it.isAbstract()).isFalse()
             }
             invocation.processingEnv.requireTypeElement("foo.bar.AbstractClass").let {
-                assertThat(it.superType).isNull()
+                assertThat(it.superType).let {
+                    // KSP does not return Object / Any as super class
+                    if (invocation.isKsp) {
+                        it.isNull()
+                    } else {
+                        it.isEqualTo(
+                            invocation.processingEnv.requireType(TypeName.OBJECT)
+                        )
+                    }
+                }
                 assertThat(it.isAbstract()).isTrue()
                 assertThat(it.isInterface()).isFalse()
                 assertThat(it.type).isEqualTo(
@@ -134,7 +146,7 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             invocation.processingEnv.requireTypeElement("foo.bar.Outer").let {
                 assertThat(it.className).isEqualTo(ClassName.get("foo.bar", "Outer"))
                 assertThat(it.enclosingTypeElement).isNull()
@@ -163,7 +175,7 @@
             private class PrivateClass
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             fun getModifiers(element: XTypeElement): Set<String> {
                 val result = mutableSetOf<String>()
                 if (element.isAbstract()) result.add("abstract")
@@ -188,11 +200,18 @@
             assertThat(getModifiers("MyObject"))
                 .containsExactly("final", "public", "object")
             assertThat(getModifiers("MyInterface"))
-                .containsExactly("interface", "public")
+                .containsExactly("abstract", "interface", "public")
             assertThat(getModifiers("Final"))
                 .containsExactly("final", "public")
             assertThat(getModifiers("PrivateClass"))
-                .containsExactly("private", "final")
+                .containsExactlyElementsIn(
+                    if (invocation.isKsp) {
+                        listOf("private", "final")
+                    } else {
+                        // java does not support top level private classes.
+                        listOf("final")
+                    }
+                )
         }
     }
 
@@ -205,7 +224,7 @@
             interface MyInterface
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             invocation.processingEnv.requireTypeElement("MyClass").let {
                 assertThat(it.kindName()).isEqualTo("class")
             }
@@ -228,7 +247,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val baseClass = invocation.processingEnv.requireTypeElement("BaseClass")
             assertThat(baseClass.getAllFieldNames()).containsExactly("genericProp")
             val subClass = invocation.processingEnv.requireTypeElement("SubClass")
@@ -269,7 +288,7 @@
             ) : BaseClass(value)
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val baseClass = invocation.processingEnv.requireTypeElement("BaseClass")
             assertThat(baseClass.getAllFieldNames()).containsExactly("value")
             val subClass = invocation.processingEnv.requireTypeElement("SubClass")
@@ -288,6 +307,28 @@
     }
 
     @Test
+    fun fieldsInInterfaces() {
+        val src = Source.kotlin(
+            "Foo.kt",
+            """
+            interface MyInterface {
+                var x:Int
+            }
+            """.trimIndent()
+        )
+        runProcessorTest(sources = listOf(src)) { invocation ->
+            val element = invocation.processingEnv.requireTypeElement("MyInterface")
+            assertThat(element.getAllFieldsIncludingPrivateSupers()).isEmpty()
+            element.getMethod("getX").let {
+                assertThat(it.isAbstract()).isTrue()
+            }
+            element.getMethod("setX").let {
+                assertThat(it.isAbstract()).isTrue()
+            }
+        }
+    }
+
+    @Test
     fun declaredAndInstanceMethods() {
         val src = Source.kotlin(
             "Foo.kt",
@@ -317,8 +358,9 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val base = invocation.processingEnv.requireTypeElement("Base")
+            val objectMethodNames = invocation.objectMethodNames()
             assertThat(base.getDeclaredMethods().names()).containsExactly(
                 "baseFun", "suspendFun", "privateBaseFun", "staticBaseFun"
             )
@@ -327,7 +369,9 @@
             assertThat(sub.getDeclaredMethods().names()).containsExactly(
                 "baseFun", "subFun", "privateSubFun", "staticFun"
             )
-            assertThat(sub.getAllNonPrivateInstanceMethods().names()).containsExactly(
+            assertThat(
+                sub.getAllNonPrivateInstanceMethods().names() - objectMethodNames
+            ).containsExactly(
                 "baseFun", "suspendFun", "subFun"
             )
         }
@@ -371,9 +415,12 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
+            val objectMethodNames = invocation.objectMethodNames()
             val klass = invocation.processingEnv.requireTypeElement("SubClass")
-            assertThat(klass.getAllMethods().names()).containsExactly(
+            assertThat(
+                klass.getAllMethods().names() - objectMethodNames
+            ).containsExactly(
                 "baseMethod", "overriddenMethod", "baseCompanionMethod",
                 "interfaceMethod", "subMethod", "privateSubMethod", "subCompanionMethod"
             )
@@ -395,15 +442,18 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
+            val objectMethodNames = invocation.objectMethodNames()
             invocation.processingEnv.requireTypeElement("JustGetter").let { base ->
                 assertThat(base.getDeclaredMethods().names()).containsExactly(
                     "getX"
                 )
-                assertThat(base.getAllMethods().names()).containsExactly(
+                assertThat(base.getAllMethods().names() - objectMethodNames).containsExactly(
                     "getX"
                 )
-                assertThat(base.getAllNonPrivateInstanceMethods().names()).containsExactly(
+                assertThat(
+                    base.getAllNonPrivateInstanceMethods().names() - objectMethodNames
+                ).containsExactly(
                     "getX"
                 )
             }
@@ -411,10 +461,12 @@
                 assertThat(sub.getDeclaredMethods().names()).containsExactly(
                     "getY", "setY"
                 )
-                assertThat(sub.getAllMethods().names()).containsExactly(
+                assertThat(sub.getAllMethods().names() - objectMethodNames).containsExactly(
                     "getX", "getY", "setY"
                 )
-                assertThat(sub.getAllNonPrivateInstanceMethods().names()).containsExactly(
+                assertThat(
+                    sub.getAllNonPrivateInstanceMethods().names() - objectMethodNames
+                ).containsExactly(
                     "getX", "getY", "setY"
                 )
             }
@@ -438,7 +490,9 @@
             class SubClass : CompanionSubject()
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        // KAPT is a bit aggressive in adding fields, specifically, it adds companionProp and
+        // Companion as static fields which are not really fields from room's perspective.
+        runKspTest(sources = listOf(src)) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("CompanionSubject")
             assertThat(subject.getAllFieldNames()).containsExactly(
                 "mutableStatic", "immutableStatic"
@@ -471,7 +525,7 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             invocation.processingEnv.requireTypeElement("JustGetter").let { base ->
                 assertThat(base.getDeclaredMethods().names()).containsExactly(
                     "getX"
@@ -520,7 +574,7 @@
             abstract class AbstractExplicit(x:Int)
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subjects = listOf(
                 "MyInterface", "NoExplicitConstructor", "Base", "ExplicitConstructor",
                 "BaseWithSecondary", "Sub", "SubWith3Constructors",
@@ -572,11 +626,11 @@
             interface MyInterface {
                 fun notJvmDefault()
                 @JvmDefault
-                fun jvmDefault()
+                fun jvmDefault() {}
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("MyInterface")
             assertThat(subject.getMethod("notJvmDefault").isJavaDefault()).isFalse()
             assertThat(subject.getMethod("jvmDefault").isJavaDefault()).isTrue()
@@ -623,7 +677,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subjects = listOf(
                 "MyInterface", "NoExplicitConstructor", "Base", "ExplicitConstructor",
                 "BaseWithSecondary", "Sub", "SubWith3Constructors",
@@ -653,6 +707,15 @@
         }
     }
 
+    /**
+     * it is good to exclude methods coming from Object when testing as they differ between KSP
+     * and KAPT but irrelevant for Room.
+     */
+    private fun XTestInvocation.objectMethodNames() = processingEnv
+        .requireTypeElement("java.lang.Object")
+        .getAllMethods()
+        .names()
+
     private fun List<XMethodElement>.names() = map {
         it.name
     }
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
index db0fc7dc..8609c80 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
@@ -18,19 +18,21 @@
 
 import androidx.room.compiler.processing.XNullability.NONNULL
 import androidx.room.compiler.processing.XNullability.NULLABLE
+import androidx.room.compiler.processing.asDeclaredType
 import androidx.room.compiler.processing.isDeclared
 import androidx.room.compiler.processing.util.Source
-import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.className
+import androidx.room.compiler.processing.util.getField
+import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth.assertThat
 import com.google.devtools.ksp.getClassDeclarationByName
 import com.google.devtools.ksp.getDeclaredFunctions
-import com.google.devtools.ksp.symbol.KSPropertyDeclaration
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeVariableName
 import com.squareup.javapoet.WildcardTypeName
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -50,7 +52,7 @@
             interface MyInterface {}
             """.trimIndent()
         )
-        runKspTest(listOf(src), succeed = true) {
+        runKspTest(listOf(src)) {
             val subject = it.processingEnv.requireType("foo.bar.Baz")
             assertThat(subject.typeName).isEqualTo(
                 ClassName.get("foo.bar", "Baz")
@@ -83,23 +85,24 @@
         val src = Source.kotlin(
             "foo.kt",
             """
-            package foo.bar;
-            val errorType : IDontExist = TODO()
-            val listOfErrorType : List<IDontExist> = TODO()
+            class Subject {
+                val errorType : IDontExist = TODO()
+                val listOfErrorType : List<IDontExist> = TODO()
+            }
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = false
+            listOf(src)
         ) { invocation ->
-            invocation.requireDeclaredPropertyType("errorType").let { type ->
+            val subject = invocation.processingEnv.requireTypeElement("Subject")
+            subject.getField("errorType").type.asDeclaredType().let { type ->
                 assertThat(type.isError()).isTrue()
                 assertThat(type.typeArguments).isEmpty()
                 assertThat(type.typeName).isEqualTo(ERROR_TYPE_NAME)
                 assertThat(type.asTypeElement().className).isEqualTo(ERROR_TYPE_NAME)
             }
 
-            invocation.requireDeclaredPropertyType("listOfErrorType").let { type ->
+            subject.getField("listOfErrorType").type.asDeclaredType().let { type ->
                 assertThat(type.isError()).isFalse()
                 assertThat(type.typeArguments).hasSize(1)
                 type.typeArguments.single().let { typeArg ->
@@ -107,6 +110,9 @@
                     assertThat(typeArg.typeName).isEqualTo(ERROR_TYPE_NAME)
                 }
             }
+            invocation.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -115,16 +121,17 @@
         val src = Source.kotlin(
             "foo.kt",
             """
-            package foo.bar;
-            val listOfNullableStrings : List<String?> = TODO()
-            val listOfInts : List<Int> = TODO()
+            class Subject {
+                val listOfNullableStrings : List<String?> = TODO()
+                val listOfInts : List<Int> = TODO()
+            }
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
-            invocation.requireDeclaredPropertyType("listOfNullableStrings").let { type ->
+            val subject = invocation.processingEnv.requireTypeElement("Subject")
+            subject.getField("listOfNullableStrings").type.asDeclaredType().let { type ->
                 assertThat(type.nullability).isEqualTo(NONNULL)
                 assertThat(type.typeArguments).hasSize(1)
                 assertThat(type.asTypeElement().className).isEqualTo(
@@ -140,7 +147,7 @@
                 }
             }
 
-            invocation.requireDeclaredPropertyType("listOfInts").let { type ->
+            subject.getField("listOfInts").type.asDeclaredType().let { type ->
                 assertThat(type.nullability).isEqualTo(NONNULL)
                 assertThat(type.typeArguments).hasSize(1)
                 type.typeArguments.single().let { typeArg ->
@@ -163,35 +170,36 @@
         val src = Source.kotlin(
             "foo.kt",
             """
-            package foo.bar;
-            val listOfNullableStrings : List<String?> = TODO()
-            val listOfNullableStrings_2 : List<String?> = TODO()
-            val listOfNonNullStrings : List<String> = TODO()
-            val listOfNonNullStrings_2 : List<String> = TODO()
-            val nullableString : String? = TODO()
-            val nonNullString : String = TODO()
+            class Subject {
+                val listOfNullableStrings : List<String?> = TODO()
+                val listOfNullableStrings_2 : List<String?> = TODO()
+                val listOfNonNullStrings : List<String> = TODO()
+                val listOfNonNullStrings_2 : List<String> = TODO()
+                val nullableString : String? = TODO()
+                val nonNullString : String = TODO()
+            }
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
-            val nullableStringList = invocation
-                .requireDeclaredPropertyType("listOfNullableStrings")
-            val nonNullStringList = invocation
-                .requireDeclaredPropertyType("listOfNonNullStrings")
+            val subject = invocation.processingEnv.requireTypeElement("Subject")
+            val nullableStringList = subject.getField("listOfNullableStrings")
+                .type.asDeclaredType()
+            val nonNullStringList = subject.getField("listOfNonNullStrings")
+                .type.asDeclaredType()
             assertThat(nullableStringList).isNotEqualTo(nonNullStringList)
             assertThat(nonNullStringList).isNotEqualTo(nullableStringList)
 
-            val nullableStringList_2 = invocation
-                .requireDeclaredPropertyType("listOfNullableStrings_2")
-            val nonNullStringList_2 = invocation
-                .requireDeclaredPropertyType("listOfNonNullStrings_2")
+            val nullableStringList_2 = subject.getField("listOfNullableStrings_2")
+                .type.asDeclaredType()
+            val nonNullStringList_2 = subject.getField("listOfNonNullStrings_2")
+                .type.asDeclaredType()
             assertThat(nullableStringList).isEqualTo(nullableStringList_2)
             assertThat(nonNullStringList).isEqualTo(nonNullStringList_2)
 
-            val nullableString = invocation.requirePropertyType("nullableString")
-            val nonNullString = invocation.requirePropertyType("nonNullString")
+            val nullableString = subject.getField("nullableString").type
+            val nonNullString = subject.getField("nonNullString").type
             assertThat(nullableString).isEqualTo(
                 nullableStringList.typeArguments.single()
             )
@@ -212,33 +220,34 @@
         val src = Source.kotlin(
             "foo.kt",
             """
-            package foo.bar;
-            val simple : Int = 0
-            val list : List<String> = TODO()
-            val map : Map<String, String> = TODO()
-            val listOfMaps : List<Map<String, String>> = TODO()
+            class Subject {
+                val simple : Int = 0
+                val list : List<String> = TODO()
+                val map : Map<String, String> = TODO()
+                val listOfMaps : List<Map<String, String>> = TODO()
+            }
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
-            invocation.requirePropertyType("simple").let {
+            val subject = invocation.processingEnv.requireTypeElement("Subject")
+            subject.getField("simple").type.let {
                 assertThat(it.rawType.typeName).isEqualTo(TypeName.INT)
             }
-            invocation.requireDeclaredPropertyType("list").let { list ->
+            subject.getField("list").type.asDeclaredType().let { list ->
                 assertThat(list.rawType).isNotEqualTo(list)
                 assertThat(list.typeArguments).isNotEmpty()
                 assertThat(list.rawType.typeName)
                     .isEqualTo(ClassName.get("java.util", "List"))
             }
-            invocation.requireDeclaredPropertyType("map").let { map ->
+            subject.getField("map").type.asDeclaredType().let { map ->
                 assertThat(map.rawType).isNotEqualTo(map)
                 assertThat(map.typeArguments).hasSize(2)
                 assertThat(map.rawType.typeName)
                     .isEqualTo(ClassName.get("java.util", "Map"))
             }
-            invocation.requireDeclaredPropertyType("listOfMaps").let { listOfMaps ->
+            subject.getField("listOfMaps").type.asDeclaredType().let { listOfMaps ->
                 assertThat(listOfMaps.rawType).isNotEqualTo(listOfMaps)
                 assertThat(listOfMaps.typeArguments).hasSize(1)
             }
@@ -257,7 +266,7 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val resolver = (invocation.processingEnv as KspProcessingEnv).resolver
             val voidMethod = resolver.getClassDeclarationByName("foo.bar.Baz")!!
                 .getDeclaredFunctions()
@@ -278,21 +287,23 @@
         val src = Source.kotlin(
             "foo.kt",
             """
-            val intProp : Int = 0
-            val nullableIntProp : Int? = null
-            val longProp : Long = 0
-            val nullableLongProp : Long? = null
-            val byteProp : Byte = 0
-            val nullableByteProp :Byte? = null
-            val errorProp : IDontExist = TODO()
-            val nullableErrorProp : IDontExist? = TODO()
+            class Subject {
+                val intProp : Int = 0
+                val nullableIntProp : Int? = null
+                val longProp : Long = 0
+                val nullableLongProp : Long? = null
+                val byteProp : Byte = 0
+                val nullableByteProp :Byte? = null
+                val errorProp : IDontExist = TODO()
+                val nullableErrorProp : IDontExist? = TODO()
+            }
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = false
+            listOf(src)
         ) { invocation ->
-            fun mapProp(name: String) = invocation.requirePropertyType(name).let {
+            val subject = invocation.processingEnv.requireTypeElement("Subject")
+            fun mapProp(name: String) = subject.getField(name).type.let {
                 listOf(
                     "isInt" to it.isInt(),
                     "isLong" to it.isLong(),
@@ -313,6 +324,9 @@
             assertThat(mapProp("nullableByteProp")).containsExactly("isByte")
             assertThat(mapProp("errorProp")).containsExactly("isError")
             assertThat(mapProp("nullableErrorProp")).containsExactly("isError")
+            invocation.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -321,23 +335,25 @@
         val src = Source.kotlin(
             "foo.kt",
             """
-            val intProp : Int = 3 // kotlin default value is unrelated, will be ignored
-            val nullableIntProp : Int? = null
-            val longProp : Long = 3
-            val nullableLongProp : Long? = null
-            val floatProp = 3f
-            val byteProp : Byte = 0
-            val nullableByteProp :Byte? = null
-            val errorProp : IDontExist = TODO()
-            val nullableErrorProp : IDontExist? = TODO()
-            val stringProp : String = "abc"
+            class Subject {
+                val intProp : Int = 3 // kotlin default value is unrelated, will be ignored
+                val nullableIntProp : Int? = null
+                val longProp : Long = 3
+                val nullableLongProp : Long? = null
+                val floatProp = 3f
+                val byteProp : Byte = 0
+                val nullableByteProp :Byte? = null
+                val errorProp : IDontExist = TODO()
+                val nullableErrorProp : IDontExist? = TODO()
+                val stringProp : String = "abc"
+            }
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = false
+            listOf(src)
         ) { invocation ->
-            fun getDefaultValue(name: String) = invocation.requirePropertyType(name).defaultValue()
+            val subject = invocation.processingEnv.requireTypeElement("Subject")
+            fun getDefaultValue(name: String) = subject.getField(name).type.defaultValue()
             // javac types do not check nullability but checking it is more correct
             // since KSP is an opt-in by the developer, it is better for it to be more strict about
             // types.
@@ -351,6 +367,9 @@
             assertThat(getDefaultValue("errorProp")).isEqualTo("null")
             assertThat(getDefaultValue("nullableErrorProp")).isEqualTo("null")
             assertThat(getDefaultValue("stringProp")).isEqualTo("null")
+            invocation.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -359,43 +378,45 @@
         val src = Source.kotlin(
             "foo.kt",
             """
-            val intProp : Int = 3
-            val longProp : Long = 3
-            val stringProp : String = "abc"
-            val listProp : List<String> = TODO()
+            class Subject {
+                val intProp : Int = 3
+                val longProp : Long = 3
+                val stringProp : String = "abc"
+                val listProp : List<String> = TODO()
+            }
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
+            val subject = invocation.processingEnv.requireTypeElement("Subject")
             assertThat(
-                invocation.requirePropertyType("stringProp").isTypeOf(
+                subject.getField("stringProp").type.isTypeOf(
                     String::class
                 )
             ).isTrue()
             assertThat(
-                invocation.requirePropertyType("intProp").isTypeOf(
+                subject.getField("intProp").type.isTypeOf(
                     Int::class
                 )
             ).isTrue()
             assertThat(
-                invocation.requirePropertyType("longProp").isTypeOf(
+                subject.getField("longProp").type.isTypeOf(
                     Long::class
                 )
             ).isTrue()
             assertThat(
-                invocation.requirePropertyType("listProp").isTypeOf(
+                subject.getField("listProp").type.isTypeOf(
                     List::class
                 )
             ).isTrue()
             assertThat(
-                invocation.requirePropertyType("listProp").isTypeOf(
+                subject.getField("listProp").type.isTypeOf(
                     Set::class
                 )
             ).isFalse()
             assertThat(
-                invocation.requirePropertyType("listProp").isTypeOf(
+                subject.getField("listProp").type.isTypeOf(
                     Iterable::class
                 )
             ).isFalse()
@@ -407,23 +428,25 @@
         val src = Source.kotlin(
             "foo.kt",
             """
-            val intProp : Int = 3
-            val intProp2 : Int = 4
-            val longProp : Long = 0L
-            val nullableLong : Long? = null
-            val listOfStrings1 : List<String> = TODO()
-            val listOfStrings2 : List<String> = TODO()
-            val listOfInts : List<Int> = TODO()
-            val listOfNullableStrings : List<String?> = TODO()
+            class Subject {
+                val intProp : Int = 3
+                val intProp2 : Int = 4
+                val longProp : Long = 0L
+                val nullableLong : Long? = null
+                val listOfStrings1 : List<String> = TODO()
+                val listOfStrings2 : List<String> = TODO()
+                val listOfInts : List<Int> = TODO()
+                val listOfNullableStrings : List<String?> = TODO()
+            }
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
+            val subject = invocation.processingEnv.requireTypeElement("Subject")
             fun check(prop1: String, prop2: String): Boolean {
-                return invocation.requirePropertyType(prop1).isSameType(
-                    invocation.requirePropertyType(prop2)
+                return subject.getField(prop1).type.isSameType(
+                    subject.getField(prop2).type
                 )
             }
             assertThat(check("intProp", "intProp2")).isTrue()
@@ -450,29 +473,26 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
-            val env = (invocation.processingEnv as KspProcessingEnv)
             val classNames = listOf("Bar", "Bar_NullableFoo")
-            val typeArgs = classNames.associateWith {
-                env.resolver.findClass(it)!!
-                    .asStarProjectedType()
-                    .arguments
+            val typeArgs = classNames.associateWith { className ->
+                invocation.processingEnv
+                    .requireType(className)
+                    .asDeclaredType()
+                    .typeArguments
                     .single()
-                    .type
-                    .let { typeRef ->
-                        env.wrap(
-                            ksType = typeRef!!.resolve(),
-                            allowPrimitives = false
-                        )
-                    }
             }
-            assertThat(typeArgs["Bar"]!!.typeName)
-                .isEqualTo(ClassName.get("", "Foo"))
+            val typeName = typeArgs["Bar"]!!.typeName
+            assertThat(typeName)
+                .isEqualTo(
+                    TypeVariableName.get("T", ClassName.get("", "Foo"))
+                )
             assertThat(typeArgs["Bar"]!!.nullability).isEqualTo(NONNULL)
             assertThat(typeArgs["Bar_NullableFoo"]!!.typeName)
-                .isEqualTo(ClassName.get("", "Foo"))
+                .isEqualTo(
+                    TypeVariableName.get("T", ClassName.get("", "Foo"))
+                )
             assertThat(typeArgs["Bar_NullableFoo"]!!.nullability).isEqualTo(NULLABLE)
         }
     }
@@ -491,20 +511,12 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
-            val env = (invocation.processingEnv as KspProcessingEnv)
-            val method = env.resolver
-                .findClass("foo.bar.Baz")
-                ?.getDeclaredFunctions()
-                ?.first {
-                    it.simpleName.asString() == "wildcardMethod"
-                } ?: throw AssertionError("cannot find test method")
-            val paramType = env.wrap(
-                ksType = method.parameters.first().type.resolve(),
-                allowPrimitives = false
-            )
+
+            val method = invocation.processingEnv.requireTypeElement("foo.bar.Baz")
+                .getMethod("wildcardMethod")
+            val paramType = method.parameters.first().type
             check(paramType.isDeclared())
             val arg1 = paramType.typeArguments.single()
             assertThat(arg1.typeName)
@@ -516,29 +528,4 @@
             assertThat(arg1.extendsBound()).isNull()
         }
     }
-
-    private fun XTestInvocation.requirePropertyType(name: String): KspType {
-        val prop = requireProperty(name)
-        return (processingEnv as KspProcessingEnv).wrap(prop.type)
-    }
-
-    private fun XTestInvocation.requireDeclaredPropertyType(name: String): KspDeclaredType {
-        val prop = requireProperty(name)
-        val result =
-            (processingEnv as KspProcessingEnv).wrap(
-                ksType = prop.type.resolve(),
-                allowPrimitives = false
-            )
-        check(result is KspDeclaredType)
-        return result
-    }
-
-    private fun XTestInvocation.requireProperty(name: String): KSPropertyDeclaration {
-        kspResolver.getAllFiles().forEach { file ->
-            return file.declarations.first {
-                it.simpleName.asString() == name
-            } as KSPropertyDeclaration
-        }
-        throw IllegalStateException("cannot find any property with name $name")
-    }
 }
\ No newline at end of file
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithDefaults.java b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithDefaults.java
new file mode 100644
index 0000000..26361dc
--- /dev/null
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithDefaults.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.testcode;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+
+public @interface JavaAnnotationWithDefaults {
+    String stringVal() default "foo";
+    String[] stringArrayVal() default {"x", "y"};
+    Class<?> typeVal() default HashMap.class;
+    Class[] typeArrayVal() default {LinkedHashMap.class};
+    int intVal() default 3;
+    JavaEnum enumVal() default JavaEnum.DEFAULT;
+    JavaEnum[] enumArrayVal() default {JavaEnum.VAL1, JavaEnum.VAL2};
+    OtherAnnotation otherAnnotationVal() default @OtherAnnotation("def");
+    OtherAnnotation[] otherAnnotationArrayVal() default {@OtherAnnotation("v1")};
+}
diff --git a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaEnum.java
similarity index 82%
rename from compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt
rename to room/compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaEnum.java
index 3de9f0d..c0a037b 100644
--- a/compose/ui/ui-util/src/jvmMain/kotlin/androidx/compose/ui/util/JvmMathHelpers.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaEnum.java
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.util
+package androidx.room.compiler.processing.testcode;
 
-actual fun Float.toStringAsFixed(digits: Int): String = String.format("%.${digits}f", this)
+public enum JavaEnum {
+    VAL1,
+    VAL2,
+    DEFAULT
+}
diff --git a/room/compiler/build.gradle b/room/compiler/build.gradle
index fc529d1..6cca78d 100644
--- a/room/compiler/build.gradle
+++ b/room/compiler/build.gradle
@@ -114,6 +114,7 @@
     implementation(INTELLIJ_ANNOTATIONS)
     testImplementation(GOOGLE_COMPILE_TESTING)
     testImplementation projectOrArtifact(":paging:paging-common")
+    testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementation(JUNIT)
     testImplementation(JSR250)
     testImplementation(MOCKITO_CORE)
diff --git a/room/compiler/src/main/kotlin/androidx/room/log/RLog.kt b/room/compiler/src/main/kotlin/androidx/room/log/RLog.kt
index d18778e..0ce7b73 100644
--- a/room/compiler/src/main/kotlin/androidx/room/log/RLog.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/log/RLog.kt
@@ -75,9 +75,9 @@
         messager.printMessage(WARNING, msg.safeFormat(args), defaultElement)
     }
 
-    class CollectingMessager : XMessager {
+    class CollectingMessager : XMessager() {
         private val messages = mutableMapOf<Diagnostic.Kind, MutableList<Pair<String, XElement?>>>()
-        override fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+        override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
             messages.getOrPut(
                 kind,
                 {
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt
index 291bd76..9d0d99b 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt
@@ -60,7 +60,7 @@
             ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_ENTITY_FIELDS
         )
 
-        val adapter = context.typeAdapterStore.findColumnTypeAdapter(member, affinity)
+        val adapter = context.typeAdapterStore.findColumnTypeAdapter(member, affinity, false)
         val adapterAffinity = adapter?.typeAffinity ?: affinity
         val nonNull = Field.calcNonNull(member, fieldParent)
 
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index f04871d..4196bae 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -260,7 +260,7 @@
         if (output.isError()) {
             return null
         }
-        val adapter = findColumnTypeAdapter(output, affinity)
+        val adapter = findColumnTypeAdapter(output, affinity, true)
         if (adapter != null) {
             // two way is better
             return adapter
@@ -314,7 +314,11 @@
      * Finds a two way converter, if you need 1 way, use findStatementValueBinder or
      * findCursorValueReader.
      */
-    fun findColumnTypeAdapter(out: XType, affinity: SQLTypeAffinity?): ColumnTypeAdapter? {
+    fun findColumnTypeAdapter(
+        out: XType,
+        affinity: SQLTypeAffinity?,
+        skipEnumConverter: Boolean
+    ): ColumnTypeAdapter? {
         if (out.isError()) {
             return null
         }
@@ -338,9 +342,11 @@
         if (adapterByTypeConverter != null) {
             return adapterByTypeConverter
         }
-        val enumAdapter = createEnumTypeAdapter(out)
-        if (enumAdapter != null) {
-            return enumAdapter
+        if (!skipEnumConverter) {
+            val enumAdapter = createEnumTypeAdapter(out)
+            if (enumAdapter != null) {
+                return enumAdapter
+            }
         }
         return null
     }
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt b/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
index 7a3664c..2e53e25 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
@@ -25,8 +25,8 @@
  */
 object BoxedBooleanToBoxedIntConverter {
     fun create(processingEnvironment: XProcessingEnv): List<TypeConverter> {
-        val tBoolean = processingEnvironment.requireType("java.lang.Boolean")
-        val tInt = processingEnvironment.requireType("java.lang.Integer")
+        val tBoolean = processingEnvironment.requireType("java.lang.Boolean").makeNullable()
+        val tInt = processingEnvironment.requireType("java.lang.Integer").makeNullable()
         return listOf(
             object : TypeConverter(tBoolean, tInt) {
                 override fun convert(
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
index 6fc5cc5..e47d917 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
@@ -34,7 +34,7 @@
 
             return primitiveAdapters.map {
                 BoxedPrimitiveColumnTypeAdapter(
-                    it.out.boxed(),
+                    it.out.boxed().makeNullable(),
                     it
                 )
             }
diff --git a/room/compiler/src/main/kotlin/androidx/room/util/SimpleJavaVersion.kt b/room/compiler/src/main/kotlin/androidx/room/util/SimpleJavaVersion.kt
index 8041f42..5655d9b 100644
--- a/room/compiler/src/main/kotlin/androidx/room/util/SimpleJavaVersion.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/util/SimpleJavaVersion.kt
@@ -23,8 +23,11 @@
  * [androidx.room.RoomProcessor.methodParametersVisibleInClassFiles] check only. If you want to use
  * this class, consider expanding the implementation or use a different library.
  */
-data class SimpleJavaVersion(val major: Int, val minor: Int, val update: Int? = null) :
-    Comparable<SimpleJavaVersion> {
+data class SimpleJavaVersion(
+    val major: Int,
+    val minor: Int,
+    val update: Int? = null
+) : Comparable<SimpleJavaVersion> {
 
     override fun compareTo(other: SimpleJavaVersion): Int {
         return compareValuesBy(
@@ -59,13 +62,23 @@
 
             val parts = version.split('.')
 
-            // There are valid JDK version strings with more than 3 parts when split by dots.
-            // For example: "11.0.6+10-post-Ubuntu-1ubuntu118.04.1".
-            if (parts.size < 3) {
-                return null
+            // There are valid JDK version strings with no parts split by dots.
+            // For example: "15+36".
+            if (parts.size == 1) {
+                return try {
+                    val major = parts[0].substringBeforeNonDigitChar()
+                    SimpleJavaVersion(major.toInt(), 0)
+                } catch (e: NumberFormatException) {
+                    null
+                }
             }
 
             if (parts[0] == "1") {
+                // All 3 parts are needed when JDK versions strings where major version is 1.
+                // For example: "1.8.0_202-release-1483-b39-5396753"
+                if (parts.size < 3) {
+                    return null
+                }
                 val major = parts[1]
                 val minorAndUpdate = parts[2].substringBefore('-').split('_')
                 if (minorAndUpdate.size != 2) {
@@ -82,13 +95,23 @@
                 }
             } else {
                 return try {
-                    SimpleJavaVersion(parts[0].toInt(), parts[1].toInt())
+                    val minor = parts[1].substringBeforeNonDigitChar()
+                    SimpleJavaVersion(parts[0].toInt(), minor.toInt())
                 } catch (e: NumberFormatException) {
                     null
                 }
             }
         }
 
+        private fun String.substringBeforeNonDigitChar(): String {
+            val nonDigitIndex = indexOfFirst { !it.isDigit() }
+            return if (nonDigitIndex == -1) {
+                this
+            } else {
+                substring(0, nonDigitIndex)
+            }
+        }
+
         /**
          * Parses the Java version from the given string (e.g.,
          * "1.8.0_202-release-1483-b39-5396753"), throwing [IllegalArgumentException] if it
diff --git a/room/compiler/src/test/kotlin/androidx/room/log/RLogTest.kt b/room/compiler/src/test/kotlin/androidx/room/log/RLogTest.kt
index b87099d..71a4ce0 100644
--- a/room/compiler/src/test/kotlin/androidx/room/log/RLogTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/log/RLogTest.kt
@@ -16,20 +16,22 @@
 
 package androidx.room.log
 
+import androidx.room.compiler.processing.XElement
 import androidx.room.compiler.processing.XMessager
 import androidx.room.vo.Warning
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.Mockito.mock
+import javax.tools.Diagnostic
 
 @RunWith(JUnit4::class)
 class RLogTest {
-
-    val messager = mock(XMessager::class.java)
-
     @Test
     fun testSafeFormat() {
+        val messager = object : XMessager() {
+            override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+            }
+        }
         val logger = RLog(messager, emptySet(), null)
 
         // UnknownFormatConversionException
diff --git a/room/compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt b/room/compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
index 1f1d6cb..756af79 100644
--- a/room/compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
@@ -39,6 +39,7 @@
 
 @RunWith(Parameterized::class)
 class DaoProcessorTest(val enableVerification: Boolean) {
+
     companion object {
         const val DAO_PREFIX = """
             package foo.bar;
@@ -349,6 +350,40 @@
             .withWarningCount(0)
     }
 
+    @Test
+    fun testDeleteQueryWithVoidReturn() {
+        singleDao(
+            """
+                @Dao interface MyDao {
+                    @Query("DELETE FROM User")
+                    abstract void deleteAllIds();
+                }
+                """
+        ) { dao, _ ->
+            assertThat(dao.queryMethods.size, `is`(1))
+            val method = dao.queryMethods.first()
+            assertThat(method.name, `is`("deleteAllIds"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun testSelectQueryWithVoidReturn() {
+        singleDao(
+            """
+                @Dao interface MyDao {
+                    @Query("SELECT * FROM User")
+                    abstract void getAllIds();
+                }
+                """
+        ) { dao, _ ->
+            assertThat(dao.queryMethods.size, `is`(1))
+            val method = dao.queryMethods.first()
+            assertThat(method.name, `is`("getAllIds"))
+        }.failsToCompile().withErrorContaining(
+            "Not sure how to convert a Cursor to this method's return type (void)"
+        )
+    }
+
     fun singleDao(
         vararg inputs: String,
         classpathFiles: Set<File> = emptySet(),
diff --git a/room/compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt b/room/compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
index 4096f31..704310f 100644
--- a/room/compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
@@ -105,7 +105,7 @@
     fun bind() {
         simpleRun { invocation ->
             val adapter = TypeAdapterStore.create(Context(invocation.processingEnv))
-                .findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv), null)!!
+                .findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv), null, false)!!
             adapter.bindToStmt("st", "6", "inp", scope)
             assertThat(scope.generate().toString().trim(), `is`(bindCode))
             generateCode(invocation, false)
@@ -120,7 +120,7 @@
         simpleRun { invocation ->
             val adapter = TypeAdapterStore.create(Context(invocation.processingEnv))
                 .findColumnTypeAdapter(
-                    input.getBoxedTypeMirror(invocation.processingEnv), null
+                    input.getBoxedTypeMirror(invocation.processingEnv), null, false
                 )!!
             adapter.bindToStmt("st", "6", "inp", scope)
             assertThat(
@@ -162,7 +162,7 @@
     fun read() {
         simpleRun { invocation ->
             val adapter = TypeAdapterStore.create(Context(invocation.processingEnv))
-                .findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv), null)!!
+                .findColumnTypeAdapter(input.getTypeMirror(invocation.processingEnv), null, false)!!
             adapter.readFromCursor("out", "crs", "9", scope)
             assertThat(scope.generate().toString().trim(), `is`(cursorCode))
             generateCode(invocation, false)
@@ -177,7 +177,7 @@
         simpleRun { invocation ->
             val adapter = TypeAdapterStore.create(Context(invocation.processingEnv))
                 .findColumnTypeAdapter(
-                    input.getBoxedTypeMirror(invocation.processingEnv), null
+                    input.getBoxedTypeMirror(invocation.processingEnv), null, false
                 )!!
             adapter.readFromCursor("out", "crs", "9", scope)
             assertThat(
diff --git a/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt b/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
index f7ad69b..3b8ff54 100644
--- a/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
@@ -19,9 +19,10 @@
 import COMMON
 import androidx.paging.DataSource
 import androidx.paging.PagingSource
-import androidx.room.Entity
 import androidx.room.compiler.processing.XProcessingEnv
-import androidx.room.compiler.processing.asDeclaredType
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.ext.GuavaUtilConcurrentTypeNames
 import androidx.room.ext.L
 import androidx.room.ext.LifecyclesTypeNames
@@ -46,12 +47,7 @@
 import androidx.room.solver.types.CompositeAdapter
 import androidx.room.solver.types.EnumColumnTypeAdapter
 import androidx.room.solver.types.TypeConverter
-import androidx.room.testing.TestInvocation
-import androidx.room.testing.TestProcessor
-import com.google.common.truth.Truth
-import com.google.testing.compile.CompileTester
-import com.google.testing.compile.JavaFileObjects
-import com.google.testing.compile.JavaSourcesSubjectFactory
+import androidx.room.testing.context
 import com.squareup.javapoet.TypeName
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.instanceOf
@@ -61,8 +57,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import simpleRun
 import testCodeGenScope
+import toSources
 
 @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
 @RunWith(JUnit4::class)
@@ -73,22 +69,25 @@
 
     @Test
     fun testDirect() {
-        singleRun { invocation ->
+        runProcessorTest { invocation ->
             val store = TypeAdapterStore.create(Context(invocation.processingEnv))
             val primitiveType = invocation.processingEnv.requireType(TypeName.INT)
-            val adapter = store.findColumnTypeAdapter(primitiveType, null)
+            val adapter = store.findColumnTypeAdapter(primitiveType, null, false)
             assertThat(adapter, notNullValue())
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testJavaLangBoolean() {
-        singleRun { invocation ->
-            val store = TypeAdapterStore.create(Context(invocation.processingEnv))
+        runProcessorTest { invocation ->
+            val store = TypeAdapterStore.create(
+                Context(invocation.processingEnv)
+            )
             val boolean = invocation
                 .processingEnv
                 .requireType("java.lang.Boolean")
-            val adapter = store.findColumnTypeAdapter(boolean, null)
+                .makeNullable()
+            val adapter = store.findColumnTypeAdapter(boolean, null, false)
             assertThat(adapter, notNullValue())
             assertThat(adapter, instanceOf(CompositeAdapter::class.java))
             val composite = adapter as CompositeAdapter
@@ -100,39 +99,40 @@
                 composite.columnTypeAdapter.out.typeName,
                 `is`(TypeName.INT.box())
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testJavaLangEnumCompilesWithoutError() {
-        simpleRun(
-            JavaFileObjects.forSourceString(
-                "foo.bar.Fruit",
-                """ package foo.bar;
+        val enumSrc = Source.java(
+            "foo.bar.Fruit",
+            """ package foo.bar;
                 import androidx.room.*;
                 enum Fruit {
                     APPLE,
                     BANANA,
                     STRAWBERRY}
                 """.trimMargin()
-            )
+        )
+        runProcessorTest(
+            sources = listOf(enumSrc)
         ) { invocation ->
             val store = TypeAdapterStore.create(Context(invocation.processingEnv))
             val enum = invocation
                 .processingEnv
                 .requireType("foo.bar.Fruit")
-            val adapter = store.findColumnTypeAdapter(enum, null)
+            val adapter = store.findColumnTypeAdapter(enum, null, false)
             assertThat(adapter, notNullValue())
             assertThat(adapter, instanceOf(EnumColumnTypeAdapter::class.java))
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testVia1TypeAdapter() {
-        singleRun { invocation ->
+        runProcessorTest { invocation ->
             val store = TypeAdapterStore.create(Context(invocation.processingEnv))
             val booleanType = invocation.processingEnv.requireType(TypeName.BOOLEAN)
-            val adapter = store.findColumnTypeAdapter(booleanType, null)
+            val adapter = store.findColumnTypeAdapter(booleanType, null, false)
             assertThat(adapter, notNullValue())
             assertThat(adapter, instanceOf(CompositeAdapter::class.java))
             val bindScope = testCodeGenScope()
@@ -160,18 +160,41 @@
                     """.trimIndent()
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testVia2TypeAdapters() {
-        singleRun { invocation ->
+        val point = Source.java(
+            "foo.bar.Point",
+            """
+            package foo.bar;
+            import androidx.room.*;
+            @Entity
+            public class Point {
+                public int x, y;
+                public Point(int x, int y) {
+                    this.x = x;
+                    this.y = y;
+                }
+                public static Point fromBoolean(boolean val) {
+                    return val ? new Point(1, 1) : new Point(0, 0);
+                }
+                public static boolean toBoolean(Point point) {
+                    return point.x > 0;
+                }
+            }
+            """
+        )
+        runProcessorTest(
+            sources = listOf(point)
+        ) { invocation ->
             val store = TypeAdapterStore.create(
                 Context(invocation.processingEnv),
                 pointTypeConverters(invocation.processingEnv)
             )
             val pointType = invocation.processingEnv.requireType("foo.bar.Point")
-            val adapter = store.findColumnTypeAdapter(pointType, null)
+            val adapter = store.findColumnTypeAdapter(pointType, null, false)
             assertThat(adapter, notNullValue())
             assertThat(adapter, instanceOf(CompositeAdapter::class.java))
 
@@ -204,17 +227,17 @@
                     """.trimIndent()
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testDate() {
-        singleRun { (processingEnv) ->
+        runProcessorTest { invocation ->
             val store = TypeAdapterStore.create(
-                Context(processingEnv),
-                dateTypeConverters(processingEnv)
+                invocation.context,
+                dateTypeConverters(invocation.processingEnv)
             )
-            val tDate = processingEnv.requireType("java.util.Date")
+            val tDate = invocation.processingEnv.requireType("java.util.Date")
             val adapter = store.findCursorValueReader(tDate, SQLTypeAffinity.INTEGER)
             assertThat(adapter, notNullValue())
             assertThat(adapter?.typeMirror(), `is`(tDate))
@@ -234,19 +257,19 @@
                     """.trimIndent()
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testIntList() {
-        singleRun { invocation ->
+        runProcessorTest { invocation ->
             val binders = createIntListToStringBinders(invocation)
             val store = TypeAdapterStore.create(
                 Context(invocation.processingEnv), binders[0],
                 binders[1]
             )
 
-            val adapter = store.findColumnTypeAdapter(binders[0].from, null)
+            val adapter = store.findColumnTypeAdapter(binders[0].from, null, false)
             assertThat(adapter, notNullValue())
 
             val bindScope = testCodeGenScope()
@@ -272,15 +295,15 @@
             )
             assertThat(converter, notNullValue())
             assertThat(store.reverse(converter!!), `is`(binders[1]))
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testOneWayConversion() {
-        singleRun { invocation ->
+        runProcessorTest { invocation ->
             val binders = createIntListToStringBinders(invocation)
             val store = TypeAdapterStore.create(Context(invocation.processingEnv), binders[0])
-            val adapter = store.findColumnTypeAdapter(binders[0].from, null)
+            val adapter = store.findColumnTypeAdapter(binders[0].from, null, false)
             assertThat(adapter, nullValue())
 
             val stmtBinder = store.findStatementValueBinder(binders[0].from, null)
@@ -298,7 +321,9 @@
     @Test
     fun testMissingRx2Room() {
         @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.PUBLISHER, COMMON.RX2_FLOWABLE)) { invocation ->
+        runProcessorTest(
+            sources = listOf(COMMON.PUBLISHER, COMMON.RX2_FLOWABLE).toSources()
+        ) { invocation ->
             val publisherElement = invocation.processingEnv
                 .requireTypeElement(ReactiveStreamsTypeNames.PUBLISHER)
             assertThat(publisherElement, notNullValue())
@@ -308,13 +333,18 @@
                 },
                 `is`(true)
             )
-        }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_ROOM_RXJAVA2_ARTIFACT)
+            invocation.assertCompilationResult {
+                hasError(ProcessorErrors.MISSING_ROOM_RXJAVA2_ARTIFACT)
+            }
+        }
     }
 
     @Test
     fun testMissingRx3Room() {
         @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.PUBLISHER, COMMON.RX3_FLOWABLE)) { invocation ->
+        runProcessorTest(
+            sources = listOf(COMMON.PUBLISHER, COMMON.RX3_FLOWABLE).toSources()
+        ) { invocation ->
             val publisherElement = invocation.processingEnv
                 .requireTypeElement(ReactiveStreamsTypeNames.PUBLISHER)
             assertThat(publisherElement, notNullValue())
@@ -324,7 +354,10 @@
                 },
                 `is`(true)
             )
-        }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_ROOM_RXJAVA3_ARTIFACT)
+            invocation.assertCompilationResult {
+                hasError(ProcessorErrors.MISSING_ROOM_RXJAVA3_ARTIFACT)
+            }
+        }
     }
 
     @Test
@@ -334,8 +367,9 @@
             COMMON.RX3_FLOWABLE to COMMON.RX3_ROOM
         ).forEach { (rxTypeSrc, rxRoomSrc) ->
             @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(COMMON.PUBLISHER, rxTypeSrc, rxRoomSrc)) {
-                invocation ->
+            runProcessorTest(
+                sources = listOf(COMMON.PUBLISHER, rxTypeSrc, rxRoomSrc).toSources()
+            ) { invocation ->
                 val publisher = invocation.processingEnv
                     .requireTypeElement(ReactiveStreamsTypeNames.PUBLISHER)
                 assertThat(publisher, notNullValue())
@@ -345,7 +379,7 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
@@ -356,8 +390,9 @@
             Triple(COMMON.RX3_FLOWABLE, COMMON.RX3_ROOM, RxJava3TypeNames.FLOWABLE)
         ).forEach { (rxTypeSrc, rxRoomSrc, rxTypeClassName) ->
             @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(COMMON.PUBLISHER, rxTypeSrc, rxRoomSrc)) {
-                invocation ->
+            runProcessorTest(
+                sources = listOf(COMMON.PUBLISHER, rxTypeSrc, rxRoomSrc).toSources()
+            ) { invocation ->
                 val flowable = invocation.processingEnv.requireTypeElement(rxTypeClassName)
                 assertThat(
                     RxQueryResultBinderProvider.getAll(invocation.context).any {
@@ -365,7 +400,7 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
@@ -376,8 +411,9 @@
             Triple(COMMON.RX3_OBSERVABLE, COMMON.RX3_ROOM, RxJava3TypeNames.OBSERVABLE)
         ).forEach { (rxTypeSrc, rxRoomSrc, rxTypeClassName) ->
             @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(rxTypeSrc, rxRoomSrc)) {
-                invocation ->
+            runProcessorTest(
+                sources = listOf(rxTypeSrc, rxRoomSrc).toSources()
+            ) { invocation ->
                 val observable = invocation.processingEnv.requireTypeElement(rxTypeClassName)
                 assertThat(observable, notNullValue())
                 assertThat(
@@ -386,7 +422,7 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
@@ -397,8 +433,7 @@
             Triple(COMMON.RX3_SINGLE, COMMON.RX3_ROOM, RxJava3TypeNames.SINGLE)
         ).forEach { (rxTypeSrc, _, rxTypeClassName) ->
             @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(rxTypeSrc)) {
-                invocation ->
+            runProcessorTest(sources = listOf(rxTypeSrc).toSources()) { invocation ->
                 val single = invocation.processingEnv.requireTypeElement(rxTypeClassName)
                 assertThat(single, notNullValue())
                 assertThat(
@@ -407,7 +442,7 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
@@ -417,9 +452,7 @@
             Triple(COMMON.RX2_MAYBE, COMMON.RX2_ROOM, RxJava2TypeNames.MAYBE),
             Triple(COMMON.RX3_MAYBE, COMMON.RX3_ROOM, RxJava3TypeNames.MAYBE)
         ).forEach { (rxTypeSrc, _, rxTypeClassName) ->
-            @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(rxTypeSrc)) {
-                invocation ->
+            runProcessorTest(sources = listOf(rxTypeSrc).toSources()) { invocation ->
                 val maybe = invocation.processingEnv.requireTypeElement(rxTypeClassName)
                 assertThat(
                     RxCallableInsertMethodBinderProvider.getAll(invocation.context).any {
@@ -427,7 +460,7 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
@@ -437,9 +470,7 @@
             Triple(COMMON.RX2_COMPLETABLE, COMMON.RX2_ROOM, RxJava2TypeNames.COMPLETABLE),
             Triple(COMMON.RX3_COMPLETABLE, COMMON.RX3_ROOM, RxJava3TypeNames.COMPLETABLE)
         ).forEach { (rxTypeSrc, _, rxTypeClassName) ->
-            @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(rxTypeSrc)) {
-                invocation ->
+            runProcessorTest(sources = listOf(rxTypeSrc).toSources()) { invocation ->
                 val completable = invocation.processingEnv.requireTypeElement(rxTypeClassName)
                 assertThat(
                     RxCallableInsertMethodBinderProvider.getAll(invocation.context).any {
@@ -447,14 +478,13 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
     @Test
     fun testFindInsertListenableFuture() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.LISTENABLE_FUTURE)) {
+        runProcessorTest(sources = listOf(COMMON.LISTENABLE_FUTURE).toSources()) {
             invocation ->
             val future = invocation.processingEnv
                 .requireTypeElement(GuavaUtilConcurrentTypeNames.LISTENABLE_FUTURE)
@@ -464,14 +494,12 @@
                 ),
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testFindDeleteOrUpdateSingle() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.RX2_SINGLE)) {
-            invocation ->
+        runProcessorTest(sources = listOf(COMMON.RX2_SINGLE).toSources()) { invocation ->
             val single = invocation.processingEnv.requireTypeElement(RxJava2TypeNames.SINGLE)
             assertThat(single, notNullValue())
             assertThat(
@@ -480,13 +508,12 @@
                 },
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testFindDeleteOrUpdateMaybe() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.RX2_MAYBE)) {
+        runProcessorTest(sources = listOf(COMMON.RX2_MAYBE).toSources()) {
             invocation ->
             val maybe = invocation.processingEnv.requireTypeElement(RxJava2TypeNames.MAYBE)
             assertThat(maybe, notNullValue())
@@ -496,13 +523,12 @@
                 },
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testFindDeleteOrUpdateCompletable() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.RX2_COMPLETABLE)) {
+        runProcessorTest(sources = listOf(COMMON.RX2_COMPLETABLE).toSources()) {
             invocation ->
             val completable = invocation.processingEnv
                 .requireTypeElement(RxJava2TypeNames.COMPLETABLE)
@@ -513,14 +539,14 @@
                 },
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testFindDeleteOrUpdateListenableFuture() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.LISTENABLE_FUTURE)) {
-            invocation ->
+        runProcessorTest(
+            sources = listOf(COMMON.LISTENABLE_FUTURE).toSources()
+        ) { invocation ->
             val future = invocation.processingEnv
                 .requireTypeElement(GuavaUtilConcurrentTypeNames.LISTENABLE_FUTURE)
             assertThat(future, notNullValue())
@@ -529,14 +555,14 @@
                     .matches(future.asDeclaredType()),
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testFindLiveData() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.COMPUTABLE_LIVE_DATA, COMMON.LIVE_DATA)) {
-            invocation ->
+        runProcessorTest(
+            sources = listOf(COMMON.COMPUTABLE_LIVE_DATA, COMMON.LIVE_DATA).toSources()
+        ) { invocation ->
             val liveData = invocation.processingEnv
                 .requireTypeElement(LifecyclesTypeNames.LIVE_DATA)
             assertThat(liveData, notNullValue())
@@ -546,12 +572,12 @@
                 ),
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun findPagingSourceIntKey() {
-        simpleRun { invocation ->
+        runProcessorTest { invocation ->
             val pagingSourceElement = invocation.processingEnv
                 .requireTypeElement(PagingSource::class)
             val intType = invocation.processingEnv.requireType(Integer::class)
@@ -561,7 +587,7 @@
             assertThat(pagingSourceIntIntType, notNullValue())
             assertThat(
                 PagingSourceQueryResultBinderProvider(invocation.context)
-                    .matches(pagingSourceIntIntType.asDeclaredType()),
+                    .matches(pagingSourceIntIntType),
                 `is`(true)
             )
         }
@@ -569,7 +595,7 @@
 
     @Test
     fun findPagingSourceStringKey() {
-        simpleRun { invocation ->
+        runProcessorTest { invocation ->
             val pagingSourceElement = invocation.processingEnv
                 .requireTypeElement(PagingSource::class)
             val stringType = invocation.processingEnv.requireType(String::class)
@@ -579,15 +605,18 @@
             assertThat(pagingSourceIntIntType, notNullValue())
             assertThat(
                 PagingSourceQueryResultBinderProvider(invocation.context)
-                    .matches(pagingSourceIntIntType.asDeclaredType()),
+                    .matches(pagingSourceIntIntType),
                 `is`(true)
             )
-        }.failsToCompile().withErrorContaining(ProcessorErrors.PAGING_SPECIFY_PAGING_SOURCE_TYPE)
+            invocation.assertCompilationResult {
+                hasError(ProcessorErrors.PAGING_SPECIFY_PAGING_SOURCE_TYPE)
+            }
+        }
     }
 
     @Test
     fun findDataSource() {
-        simpleRun {
+        runProcessorTest {
             invocation ->
             val dataSource = invocation.processingEnv.requireTypeElement(DataSource::class)
             assertThat(dataSource, notNullValue())
@@ -597,12 +626,15 @@
                 ),
                 `is`(true)
             )
-        }.failsToCompile().withErrorContaining(ProcessorErrors.PAGING_SPECIFY_DATA_SOURCE_TYPE)
+            invocation.assertCompilationResult {
+                hasError(ProcessorErrors.PAGING_SPECIFY_DATA_SOURCE_TYPE)
+            }
+        }
     }
 
     @Test
     fun findPositionalDataSource() {
-        simpleRun {
+        runProcessorTest {
             invocation ->
             @Suppress("DEPRECATION")
             val dataSource = invocation.processingEnv
@@ -614,13 +646,12 @@
                 ),
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun findDataSourceFactory() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.DATA_SOURCE_FACTORY)) {
+        runProcessorTest(sources = listOf(COMMON.DATA_SOURCE_FACTORY).toSources()) {
             invocation ->
             val pagedListProvider = invocation.processingEnv
                 .requireTypeElement(PagingTypeNames.DATA_SOURCE_FACTORY)
@@ -631,14 +662,13 @@
                 ),
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
-    private fun createIntListToStringBinders(invocation: TestInvocation): List<TypeConverter> {
+    private fun createIntListToStringBinders(invocation: XTestInvocation): List<TypeConverter> {
         val intType = invocation.processingEnv.requireType(Integer::class)
         val listElement = invocation.processingEnv.requireTypeElement(java.util.List::class)
         val listOfInts = invocation.processingEnv.getDeclaredType(listElement, intType)
-
         val intListConverter = object : TypeConverter(
             listOfInts,
             invocation.context.COMMON_TYPES.STRING
@@ -676,53 +706,6 @@
         return listOf(intListConverter, stringToIntListConverter)
     }
 
-    fun singleRun(handler: (TestInvocation) -> Unit): CompileTester {
-        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
-            .that(
-                listOf(
-                    JavaFileObjects.forSourceString(
-                        "foo.bar.DummyClass",
-                        """
-                        package foo.bar;
-                        import androidx.room.*;
-                        @Entity
-                        public class DummyClass {}
-                        """
-                    ),
-                    JavaFileObjects.forSourceString(
-                        "foo.bar.Point",
-                        """
-                        package foo.bar;
-                        import androidx.room.*;
-                        @Entity
-                        public class Point {
-                            public int x, y;
-                            public Point(int x, int y) {
-                                this.x = x;
-                                this.y = y;
-                            }
-                            public static Point fromBoolean(boolean val) {
-                                return val ? new Point(1, 1) : new Point(0, 0);
-                            }
-                            public static boolean toBoolean(Point point) {
-                                return point.x > 0;
-                            }
-                        }
-                        """
-                    )
-                )
-            )
-            .processedWith(
-                TestProcessor.builder()
-                    .forAnnotations(Entity::class)
-                    .nextRunHandler { invocation ->
-                        handler(invocation)
-                        true
-                    }
-                    .build()
-            )
-    }
-
     fun pointTypeConverters(env: XProcessingEnv): List<TypeConverter> {
         val tPoint = env.requireType("foo.bar.Point")
         val tBoolean = env.requireType(TypeName.BOOLEAN)
@@ -759,8 +742,8 @@
     }
 
     fun dateTypeConverters(env: XProcessingEnv): List<TypeConverter> {
-        val tDate = env.requireType("java.util.Date")
-        val tLong = env.requireType("java.lang.Long")
+        val tDate = env.requireType("java.util.Date").makeNullable()
+        val tLong = env.requireType("java.lang.Long").makeNullable()
         return listOf(
             object : TypeConverter(tDate, tLong) {
                 override fun convert(
diff --git a/room/compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt b/room/compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
index 2d00be6..25c629a 100644
--- a/room/compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
@@ -16,46 +16,37 @@
 
 package androidx.room.testing
 
-import androidx.room.Query
-import com.google.common.truth.Truth
-import com.google.testing.compile.JavaFileObjects
-import com.google.testing.compile.JavaSourceSubjectFactory
-import org.hamcrest.CoreMatchers.`is`
-import org.hamcrest.MatcherAssert.assertThat
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import java.util.concurrent.atomic.AtomicBoolean
 
 @RunWith(JUnit4::class)
 class InProcessorTest {
     @Test
     fun testInProcessorTestRuns() {
-        val didRun = AtomicBoolean(false)
-        Truth.assertAbout(JavaSourceSubjectFactory.javaSource())
-            .that(
-                JavaFileObjects.forSourceString(
-                    "foo.bar.MyClass",
-                    """
-                        package foo.bar;
-                        abstract public class MyClass {
-                        @androidx.room.Query("foo")
-                        abstract public void setFoo(String foo);
-                        }
-                        """
-                )
-            )
-            .processedWith(
-                TestProcessor.builder()
-                    .nextRunHandler { invocation ->
-                        didRun.set(true)
-                        assertThat(invocation.annotations.size, `is`(1))
-                        true
-                    }
-                    .forAnnotations(Query::class)
-                    .build()
-            )
-            .compilesWithoutError()
-        assertThat(didRun.get(), `is`(true))
+        val source = Source.java(
+            qName = "foo.bar.MyClass",
+            code = """
+                package foo.bar;
+                abstract public class MyClass {
+                @androidx.room.Query("foo")
+                abstract public void setFoo(String foo);
+                }
+            """.trimIndent()
+        )
+        var runCount = 0
+        runProcessorTest(sources = listOf(source)) {
+            assertThat(
+                it.processingEnv.findTypeElement("foo.bar.MyClass")
+            ).isNotNull()
+            runCount++
+        }
+        // run 3 times: javac, kapt, ksp
+        assertThat(
+            runCount
+        ).isEqualTo(3)
     }
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt b/room/compiler/src/test/kotlin/androidx/room/testing/XTestInvocationExt.kt
similarity index 71%
copy from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
copy to room/compiler/src/test/kotlin/androidx/room/testing/XTestInvocationExt.kt
index f9cb2fe..62e386a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/testing/XTestInvocationExt.kt
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.focus
+package androidx.room.testing
 
-@RequiresOptIn("The Focus API is experimental and is likely to change in the future.")
-annotation class ExperimentalFocus
\ No newline at end of file
+import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.processor.Context
+
+val XTestInvocation.context
+    get() = getOrPutUserData(Context::class) {
+        Context(processingEnv)
+    }
\ No newline at end of file
diff --git a/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt b/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt
index b186bff..febe6e2 100644
--- a/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt
@@ -19,6 +19,7 @@
 import androidx.room.compiler.processing.XElement
 import androidx.room.compiler.processing.XFieldElement
 import androidx.room.compiler.processing.XType
+import androidx.room.compiler.processing.util.Source
 import androidx.room.ext.GuavaUtilConcurrentTypeNames
 import androidx.room.ext.KotlinTypeNames
 import androidx.room.ext.LifecyclesTypeNames
@@ -314,4 +315,25 @@
     return System.getProperty("java.class.path")!!.split(pathSeparator).map { File(it) }.toSet()
 }
 
-fun String.toJFO(qName: String): JavaFileObject = JavaFileObjects.forSourceLines(qName, this)
\ No newline at end of file
+fun String.toJFO(qName: String): JavaFileObject = JavaFileObjects.forSourceLines(qName, this)
+
+/**
+ * Convenience method to convert JFO's to the Source objects in XProcessing so that we can
+ * convert room tests to the common API w/o major code refactor
+ */
+fun JavaFileObject.toSource(): Source {
+    val uri = this.toUri()
+    // parse name from uri
+    val contents = this.openReader(true).use {
+        it.readText()
+    }
+    val qName = uri.path.replace('/', '.')
+    val javaExt = ".java"
+    check(qName.endsWith(javaExt)) {
+        "expected a java source file, $qName does not seem like one"
+    }
+
+    return Source.java(qName.dropLast(javaExt.length), contents)
+}
+
+fun Collection<JavaFileObject>.toSources() = map { it.toSource() }
diff --git a/room/compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt b/room/compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt
index 830aeba..e930fc9 100644
--- a/room/compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.util
 
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.fail
 import org.junit.Test
 
@@ -23,37 +24,39 @@
 
     @Test
     fun testTryParse() {
-        assert(SimpleJavaVersion.tryParse("11.0.1+13-LTS") == SimpleJavaVersion(11, 0, null))
-        assert(
-            SimpleJavaVersion.tryParse("11.0.6+10-post-Ubuntu-1ubuntu118.04.1")
-                == SimpleJavaVersion(11, 0, null)
-        )
-        assert(
-            SimpleJavaVersion.tryParse("1.8.0_202-release-1483-b39-5396753")
-                == SimpleJavaVersion(8, 0, 202)
-        )
-        assert(
-            SimpleJavaVersion.tryParse("1.8.0_181-google-v7-238857965-238857965")
-                == SimpleJavaVersion(8, 0, 181)
-        )
-        assert(SimpleJavaVersion.tryParse("a.b.c") == null)
+        assertThat(SimpleJavaVersion.tryParse("1.8.0_181-google-v7-238857965-238857965"))
+            .isEqualTo(SimpleJavaVersion(8, 0, 181))
+        assertThat(SimpleJavaVersion.tryParse("1.8.0_202-release-1483-b39-5396753"))
+            .isEqualTo(SimpleJavaVersion(8, 0, 202))
+        assertThat(SimpleJavaVersion.tryParse("11.0.1+13-LTS"))
+            .isEqualTo(SimpleJavaVersion(11, 0, null))
+        assertThat(SimpleJavaVersion.tryParse("11.0.6+10-post-Ubuntu-1ubuntu118.04.1"))
+            .isEqualTo(SimpleJavaVersion(11, 0, null))
+        assertThat(SimpleJavaVersion.tryParse("11.0.8+10-b944.6842174"))
+            .isEqualTo(SimpleJavaVersion(11, 0, null))
+        assertThat(SimpleJavaVersion.tryParse("14.1-ea"))
+            .isEqualTo(SimpleJavaVersion(14, 1, null))
+        assertThat(SimpleJavaVersion.tryParse("15+13"))
+            .isEqualTo(SimpleJavaVersion(15, 0, null))
+        assertThat(SimpleJavaVersion.tryParse("a.b.c")).isNull()
     }
 
     @Test
     fun testParse() {
-        assert(SimpleJavaVersion.parse("11.0.1+13-LTS") == SimpleJavaVersion(11, 0, null))
-        assert(
-            SimpleJavaVersion.parse("11.0.6+10-post-Ubuntu-1ubuntu118.04.1")
-                == SimpleJavaVersion(11, 0, null)
-        )
-        assert(
-            SimpleJavaVersion.parse("1.8.0_202-release-1483-b39-5396753")
-                == SimpleJavaVersion(8, 0, 202)
-        )
-        assert(
-            SimpleJavaVersion.parse("1.8.0_181-google-v7-238857965-238857965")
-                == SimpleJavaVersion(8, 0, 181)
-        )
+        assertThat(SimpleJavaVersion.parse("1.8.0_181-google-v7-238857965-238857965"))
+            .isEqualTo(SimpleJavaVersion(8, 0, 181))
+        assertThat(SimpleJavaVersion.parse("1.8.0_202-release-1483-b39-5396753"))
+            .isEqualTo(SimpleJavaVersion(8, 0, 202))
+        assertThat(SimpleJavaVersion.parse("11.0.1+13-LTS"))
+            .isEqualTo(SimpleJavaVersion(11, 0, null))
+        assertThat(SimpleJavaVersion.parse("11.0.6+10-post-Ubuntu-1ubuntu118.04.1"))
+            .isEqualTo(SimpleJavaVersion(11, 0, null))
+        assertThat(SimpleJavaVersion.parse("11.0.8+10-b944.6842174"))
+            .isEqualTo(SimpleJavaVersion(11, 0, null))
+        assertThat(SimpleJavaVersion.parse("14.1-ea"))
+            .isEqualTo(SimpleJavaVersion(14, 1, null))
+        assertThat(SimpleJavaVersion.parse("15+13"))
+            .isEqualTo(SimpleJavaVersion(15, 0, null))
         try {
             SimpleJavaVersion.parse("a.b.c")
             fail("Expected IllegalArgumentException")
@@ -64,19 +67,19 @@
 
     @Test
     fun testComparison() {
-        assert(SimpleJavaVersion(11, 1) > SimpleJavaVersion(8, 2))
-        assert(SimpleJavaVersion(8, 2) < SimpleJavaVersion(11, 1))
-        assert(SimpleJavaVersion(8, 1) == SimpleJavaVersion(8, 1))
+        assertThat(SimpleJavaVersion(11, 1)).isGreaterThan(SimpleJavaVersion(8, 2))
+        assertThat(SimpleJavaVersion(8, 2)).isLessThan(SimpleJavaVersion(11, 1))
+        assertThat(SimpleJavaVersion(8, 1)).isEqualTo(SimpleJavaVersion(8, 1))
 
-        assert(SimpleJavaVersion(8, 2, 1) > SimpleJavaVersion(8, 1, 2))
-        assert(SimpleJavaVersion(8, 1, 2) < SimpleJavaVersion(8, 2, 1))
-        assert(SimpleJavaVersion(8, 1, null) == SimpleJavaVersion(8, 1, null))
+        assertThat(SimpleJavaVersion(8, 2, 1)).isGreaterThan(SimpleJavaVersion(8, 1, 2))
+        assertThat(SimpleJavaVersion(8, 1, 2)).isLessThan(SimpleJavaVersion(8, 2, 1))
+        assertThat(SimpleJavaVersion(8, 1, null)).isEqualTo(SimpleJavaVersion(8, 1, null))
 
-        assert(SimpleJavaVersion(8, 1, 2) > SimpleJavaVersion(8, 1, 1))
-        assert(SimpleJavaVersion(8, 1, 1) < SimpleJavaVersion(8, 1, 2))
-        assert(SimpleJavaVersion(8, 1, 1) == SimpleJavaVersion(8, 1, 1))
+        assertThat(SimpleJavaVersion(8, 1, 2)).isGreaterThan(SimpleJavaVersion(8, 1, 1))
+        assertThat(SimpleJavaVersion(8, 1, 1)).isLessThan(SimpleJavaVersion(8, 1, 2))
+        assertThat(SimpleJavaVersion(8, 1, 1)).isEqualTo(SimpleJavaVersion(8, 1, 1))
 
-        assert(SimpleJavaVersion(8, 1, 0) > SimpleJavaVersion(8, 1, null))
-        assert(SimpleJavaVersion(8, 1, null) < SimpleJavaVersion(8, 1, 0))
+        assertThat(SimpleJavaVersion(8, 1, 0)).isGreaterThan(SimpleJavaVersion(8, 1, null))
+        assertThat(SimpleJavaVersion(8, 1, null)).isLessThan(SimpleJavaVersion(8, 1, 0))
     }
 }
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
new file mode 100644
index 0000000..8ad0e02
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.integration.kotlintestapp.test
+
+import androidx.arch.core.executor.testing.CountingTaskExecutorRule
+import androidx.room.Dao
+import androidx.room.Database
+import androidx.room.Entity
+import androidx.room.Insert
+import androidx.room.PrimaryKey
+import androidx.room.Query
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.Update
+import androidx.sqlite.db.SimpleSQLiteQuery
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.CopyOnWriteArrayList
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QueryInterceptorTest {
+    @Rule
+    @JvmField
+    val countingTaskExecutorRule = CountingTaskExecutorRule()
+    lateinit var mDatabase: QueryInterceptorTestDatabase
+    var queryAndArgs = CopyOnWriteArrayList<Pair<String, ArrayList<Any>>>()
+
+    @Entity(tableName = "queryInterceptorTestDatabase")
+    data class QueryInterceptorEntity(@PrimaryKey val id: String, val description: String)
+
+    @Dao
+    interface QueryInterceptorDao {
+        @Query("DELETE FROM queryInterceptorTestDatabase WHERE id=:id")
+        fun delete(id: String)
+
+        @Insert
+        fun insert(item: QueryInterceptorEntity)
+
+        @Update
+        fun update(vararg item: QueryInterceptorEntity)
+    }
+
+    @Database(
+        version = 1,
+        entities = [
+            QueryInterceptorEntity::class
+        ],
+        exportSchema = false
+    )
+    abstract class QueryInterceptorTestDatabase : RoomDatabase() {
+        abstract fun queryInterceptorDao(): QueryInterceptorDao
+    }
+
+    @Before
+    fun setUp() {
+        mDatabase = Room.inMemoryDatabaseBuilder(
+            ApplicationProvider.getApplicationContext(),
+            QueryInterceptorTestDatabase::class.java
+        ).setQueryCallback(
+            RoomDatabase.QueryCallback { sqlQuery, bindArgs ->
+                val argTrace = ArrayList<Any>()
+                argTrace.addAll(bindArgs)
+                queryAndArgs.add(Pair(sqlQuery, argTrace))
+            },
+            MoreExecutors.directExecutor()
+        ).build()
+    }
+
+    @After
+    fun tearDown() {
+        mDatabase.close()
+    }
+
+    @Test
+    fun testInsert() {
+        mDatabase.queryInterceptorDao().insert(
+            QueryInterceptorEntity("Insert", "Inserted a placeholder query")
+        )
+
+        assertQueryLogged(
+            "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+                "VALUES (?,?)",
+            listOf("Insert", "Inserted a placeholder query")
+        )
+        assertTransactionQueries()
+    }
+
+    @Test
+    fun testDelete() {
+        mDatabase.queryInterceptorDao().delete("Insert")
+        assertQueryLogged(
+            "DELETE FROM queryInterceptorTestDatabase WHERE id=?",
+            listOf("Insert")
+        )
+        assertTransactionQueries()
+    }
+
+    @Test
+    fun testUpdate() {
+        mDatabase.queryInterceptorDao().insert(
+            QueryInterceptorEntity("Insert", "Inserted a placeholder query")
+        )
+        mDatabase.queryInterceptorDao().update(
+            QueryInterceptorEntity("Insert", "Updated the placeholder query")
+        )
+
+        assertQueryLogged(
+            "UPDATE OR ABORT `queryInterceptorTestDatabase` SET `id` " +
+                "= ?,`description` = ? " +
+                "WHERE `id` = ?",
+            listOf("Insert", "Updated the placeholder query", "Insert")
+        )
+        assertTransactionQueries()
+    }
+
+    @Test
+    fun testCompileStatement() {
+        assertEquals(queryAndArgs.size, 0)
+        mDatabase.queryInterceptorDao().insert(
+            QueryInterceptorEntity("Insert", "Inserted a placeholder query")
+        )
+        mDatabase.openHelper.writableDatabase.compileStatement(
+            "DELETE FROM queryInterceptorTestDatabase WHERE id=?"
+        ).execute()
+        assertQueryLogged("DELETE FROM queryInterceptorTestDatabase WHERE id=?", emptyList())
+    }
+
+    @Test
+    fun testLoggingSupportSQLiteQuery() {
+        mDatabase.openHelper.writableDatabase.query(
+            SimpleSQLiteQuery(
+                "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+                    "VALUES (?,?)",
+                arrayOf<Any>("3", "Description")
+            )
+        )
+        assertQueryLogged(
+            "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+                "VALUES (?,?)",
+            listOf("3", "Description")
+        )
+    }
+
+    @Test
+    fun testNullBindArgument() {
+        mDatabase.openHelper.writableDatabase.query(
+            SimpleSQLiteQuery(
+                "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+                    "VALUES (?,?)",
+                arrayOf("ID", null)
+            )
+        )
+        assertQueryLogged(
+            "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`," +
+                "`description`) VALUES (?,?)",
+            listOf("ID", null)
+        )
+    }
+
+    @Test
+    fun testCallbackCalledOnceAfterCloseAndReOpen() {
+        val dbBuilder = Room.inMemoryDatabaseBuilder(
+            ApplicationProvider.getApplicationContext(),
+            QueryInterceptorTestDatabase::class.java
+        ).setQueryCallback(
+            RoomDatabase.QueryCallback { sqlQuery, bindArgs ->
+                val argTrace = ArrayList<Any>()
+                argTrace.addAll(bindArgs)
+                queryAndArgs.add(Pair(sqlQuery, argTrace))
+            },
+            MoreExecutors.directExecutor()
+        )
+
+        dbBuilder.build().close()
+
+        mDatabase = dbBuilder.build()
+
+        mDatabase.queryInterceptorDao().insert(
+            QueryInterceptorEntity("Insert", "Inserted a placeholder query")
+        )
+
+        assertQueryLogged(
+            "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+                "VALUES (?,?)",
+            listOf("Insert", "Inserted a placeholder query")
+        )
+        assertTransactionQueries()
+    }
+
+    private fun assertQueryLogged(
+        query: String,
+        expectedArgs: List<String?>
+    ) {
+        val filteredQueries = queryAndArgs.filter {
+            it.first == query
+        }
+        assertThat(filteredQueries).hasSize(1)
+        assertThat(expectedArgs).containsExactlyElementsIn(filteredQueries[0].second)
+    }
+
+    private fun assertTransactionQueries() {
+        assertNotNull(
+            queryAndArgs.any {
+                it.equals("BEGIN TRANSACTION")
+            }
+        )
+        assertNotNull(
+            queryAndArgs.any {
+                it.equals("TRANSACTION SUCCESSFUL")
+            }
+        )
+        assertNotNull(
+            queryAndArgs.any {
+                it.equals("END TRANSACTION")
+            }
+        )
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/UserDao.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/UserDao.java
index 92a810a..fcb0a14 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/UserDao.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/UserDao.java
@@ -105,6 +105,9 @@
     @Delete
     public abstract int deleteAll(User[] users);
 
+    @Delete
+    public abstract void deleteUser(User[] users);
+
     @Query("delete from user")
     public abstract int deleteEverything();
 
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EnumColumnTypeAdapterTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EnumColumnTypeAdapterTest.java
index 94cccaf..13b47e4 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EnumColumnTypeAdapterTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/EnumColumnTypeAdapterTest.java
@@ -29,10 +29,13 @@
 import androidx.room.Query;
 import androidx.room.Room;
 import androidx.room.RoomDatabase;
+import androidx.room.TypeConverter;
+import androidx.room.TypeConverters;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,7 +45,6 @@
 public class EnumColumnTypeAdapterTest {
 
     private EnumColumnTypeAdapterDatabase mDb;
-    private EnumColumnTypeAdapterDatabase mDbComplex;
 
     @Entity
     public static class EntityWithEnum {
@@ -50,6 +52,14 @@
         public Long id;
         public Fruit fruit;
     }
+
+    @Entity
+    public static class EntityWithOneWayEnum {
+        @PrimaryKey
+        public Long id;
+        public Color color;
+    }
+
     @Entity
     public static class ComplexEntityWithEnum {
         @PrimaryKey
@@ -57,6 +67,11 @@
         public Season mSeason;
     }
 
+    public enum Color {
+        RED,
+        GREEN
+    }
+
     public enum Fruit {
         BANANA,
         STRAWBERRY,
@@ -86,6 +101,26 @@
     }
 
     @Dao
+    public interface SampleDaoWithOneWayConverter {
+        @Query("INSERT INTO EntityWithOneWayEnum (id, color) VALUES (:id, :colorInt)")
+        long insert(long id, int colorInt);
+
+        @Query("SELECT * FROM EntityWithOneWayEnum WHERE id = :id")
+        EntityWithOneWayEnum getValueWithId(long id);
+    }
+
+    public static class ColorTypeConverter {
+        @TypeConverter
+        public Color fromIntToColorEnum(int colorInt) {
+            if (colorInt == 1) {
+                return Color.RED;
+            } else {
+                return Color.GREEN;
+            }
+        }
+    }
+
+    @Dao
     public interface SampleDaoWithComplexEnum {
         @Query("INSERT INTO ComplexEntityWithEnum (id, mSeason) VALUES (:id, :season)")
         long insertComplex(long id, Season season);
@@ -94,10 +129,13 @@
         ComplexEntityWithEnum getComplexValueWithId(long id);
     }
 
-    @Database(entities = {EntityWithEnum.class, ComplexEntityWithEnum.class}, version = 1,
+    @Database(entities = {EntityWithEnum.class, ComplexEntityWithEnum.class,
+            EntityWithOneWayEnum.class}, version = 1,
             exportSchema = false)
+    @TypeConverters(ColorTypeConverter.class)
     public abstract static class EnumColumnTypeAdapterDatabase extends RoomDatabase {
         public abstract EnumColumnTypeAdapterTest.SampleDao dao();
+        public abstract EnumColumnTypeAdapterTest.SampleDaoWithOneWayConverter oneWayDao();
         public abstract EnumColumnTypeAdapterTest.SampleDaoWithComplexEnum complexDao();
     }
 
@@ -108,10 +146,11 @@
                 context,
                 EnumColumnTypeAdapterDatabase.class)
                 .build();
-        mDbComplex = Room.inMemoryDatabaseBuilder(
-                context,
-                EnumColumnTypeAdapterDatabase.class)
-                .build();
+    }
+
+    @After
+    public void teardown() {
+        mDb.close();
     }
 
     @Test
@@ -124,9 +163,16 @@
     }
 
     @Test
+    public void writeOneWayEnumToDatabase() {
+        final long id2 = mDb.oneWayDao().insert(1, 1);
+        assertThat(mDb.oneWayDao().getValueWithId(1).color, is(equalTo(Color.RED)));
+
+    }
+
+    @Test
     public void filterOutComplexEnumTest() {
-        final long id1 = mDbComplex.complexDao().insertComplex(1, Season.AUTUMN);
-        assertThat(mDbComplex.complexDao().getComplexValueWithId(1).mSeason,
+        final long id1 = mDb.complexDao().insertComplex(1, Season.AUTUMN);
+        assertThat(mDb.complexDao().getComplexValueWithId(1).mSeason,
                 is(equalTo(Season.AUTUMN)));
     }
 }
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java
index 2a0d33e..d95285a 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java
@@ -295,6 +295,17 @@
     }
 
     @Test
+    public void deleteUser() {
+        User[] users = TestUtil.createUsersArray(3, 5, 7, 9);
+        mUserDao.insertAll(users);
+        assertThat(mUserDao.loadByIds(3, 5, 7, 9), is(users));
+        // Using the void return value version of delete
+        mUserDao.deleteUser(new User[]{users[0], users[3],
+                TestUtil.createUser(9)});
+        assertThat(mUserDao.loadByIds(3, 5, 7, 9), is(new User[]{users[1], users[2]}));
+    }
+
+    @Test
     public void deleteEverything() {
         User user = TestUtil.createUser(3);
         mUserDao.insert(user);
diff --git a/room/runtime/api/current.txt b/room/runtime/api/current.txt
index e77a927..061b0ad 100644
--- a/room/runtime/api/current.txt
+++ b/room/runtime/api/current.txt
@@ -87,6 +87,7 @@
     method public androidx.room.RoomDatabase.Builder<T!> fallbackToDestructiveMigrationOnDowngrade();
     method public androidx.room.RoomDatabase.Builder<T!> openHelperFactory(androidx.sqlite.db.SupportSQLiteOpenHelper.Factory?);
     method public androidx.room.RoomDatabase.Builder<T!> setJournalMode(androidx.room.RoomDatabase.JournalMode);
+    method public androidx.room.RoomDatabase.Builder<T!> setQueryCallback(androidx.room.RoomDatabase.QueryCallback, java.util.concurrent.Executor);
     method public androidx.room.RoomDatabase.Builder<T!> setQueryExecutor(java.util.concurrent.Executor);
     method public androidx.room.RoomDatabase.Builder<T!> setTransactionExecutor(java.util.concurrent.Executor);
   }
@@ -115,6 +116,10 @@
     method public void onOpenPrepackagedDatabase(androidx.sqlite.db.SupportSQLiteDatabase);
   }
 
+  public static interface RoomDatabase.QueryCallback {
+    method public void onQuery(String, java.util.List<java.lang.Object!>);
+  }
+
 }
 
 package androidx.room.migration {
diff --git a/room/runtime/api/public_plus_experimental_current.txt b/room/runtime/api/public_plus_experimental_current.txt
index a35e99f..e1cefb2 100644
--- a/room/runtime/api/public_plus_experimental_current.txt
+++ b/room/runtime/api/public_plus_experimental_current.txt
@@ -88,6 +88,7 @@
     method public androidx.room.RoomDatabase.Builder<T!> fallbackToDestructiveMigrationOnDowngrade();
     method public androidx.room.RoomDatabase.Builder<T!> openHelperFactory(androidx.sqlite.db.SupportSQLiteOpenHelper.Factory?);
     method public androidx.room.RoomDatabase.Builder<T!> setJournalMode(androidx.room.RoomDatabase.JournalMode);
+    method public androidx.room.RoomDatabase.Builder<T!> setQueryCallback(androidx.room.RoomDatabase.QueryCallback, java.util.concurrent.Executor);
     method public androidx.room.RoomDatabase.Builder<T!> setQueryExecutor(java.util.concurrent.Executor);
     method public androidx.room.RoomDatabase.Builder<T!> setTransactionExecutor(java.util.concurrent.Executor);
   }
@@ -116,6 +117,10 @@
     method public void onOpenPrepackagedDatabase(androidx.sqlite.db.SupportSQLiteDatabase);
   }
 
+  public static interface RoomDatabase.QueryCallback {
+    method public void onQuery(String, java.util.List<java.lang.Object!>);
+  }
+
 }
 
 package androidx.room.migration {
diff --git a/room/runtime/api/restricted_current.txt b/room/runtime/api/restricted_current.txt
index b875d71..4b2d12a 100644
--- a/room/runtime/api/restricted_current.txt
+++ b/room/runtime/api/restricted_current.txt
@@ -130,6 +130,7 @@
     method public androidx.room.RoomDatabase.Builder<T!> fallbackToDestructiveMigrationOnDowngrade();
     method public androidx.room.RoomDatabase.Builder<T!> openHelperFactory(androidx.sqlite.db.SupportSQLiteOpenHelper.Factory?);
     method public androidx.room.RoomDatabase.Builder<T!> setJournalMode(androidx.room.RoomDatabase.JournalMode);
+    method public androidx.room.RoomDatabase.Builder<T!> setQueryCallback(androidx.room.RoomDatabase.QueryCallback, java.util.concurrent.Executor);
     method public androidx.room.RoomDatabase.Builder<T!> setQueryExecutor(java.util.concurrent.Executor);
     method public androidx.room.RoomDatabase.Builder<T!> setTransactionExecutor(java.util.concurrent.Executor);
   }
@@ -158,6 +159,10 @@
     method public void onOpenPrepackagedDatabase(androidx.sqlite.db.SupportSQLiteDatabase);
   }
 
+  public static interface RoomDatabase.QueryCallback {
+    method public void onQuery(String, java.util.List<java.lang.Object!>);
+  }
+
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class RoomOpenHelper extends androidx.sqlite.db.SupportSQLiteOpenHelper.Callback {
     ctor public RoomOpenHelper(androidx.room.DatabaseConfiguration, androidx.room.RoomOpenHelper.Delegate, String, String);
     ctor public RoomOpenHelper(androidx.room.DatabaseConfiguration, androidx.room.RoomOpenHelper.Delegate, String);
diff --git a/room/runtime/build.gradle b/room/runtime/build.gradle
index 0a0769d..fbce4b2 100644
--- a/room/runtime/build.gradle
+++ b/room/runtime/build.gradle
@@ -55,8 +55,12 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(project(":internal-testutils-truth")) // for assertThrows
+    androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
+
 }
 
 android.libraryVariants.all { variant ->
diff --git a/room/runtime/src/androidTest/java/androidx/room/AutoCloserTest.kt b/room/runtime/src/androidTest/java/androidx/room/AutoCloserTest.kt
new file mode 100644
index 0000000..87e9c17
--- /dev/null
+++ b/room/runtime/src/androidTest/java/androidx/room/AutoCloserTest.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room
+
+import androidx.arch.core.executor.ArchTaskExecutor
+import androidx.arch.core.executor.testing.CountingTaskExecutorRule
+import androidx.sqlite.db.SupportSQLiteDatabase
+import androidx.sqlite.db.SupportSQLiteOpenHelper
+import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
+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
+import java.io.IOException
+import java.util.concurrent.TimeUnit
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+public class AutoCloserTest {
+
+    @get:Rule
+    public val countingTaskExecutorRule: CountingTaskExecutorRule = CountingTaskExecutorRule()
+
+    private lateinit var autoCloser: AutoCloser
+
+    private lateinit var callback: Callback
+
+    private class Callback(var throwOnOpen: Boolean = false) :
+        SupportSQLiteOpenHelper.Callback(1) {
+        override fun onCreate(db: SupportSQLiteDatabase) {}
+
+        override fun onOpen(db: SupportSQLiteDatabase) {
+            if (throwOnOpen) {
+                throw IOException()
+            }
+        }
+
+        override fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) {}
+    }
+
+    @Before
+    public fun setUp() {
+        callback = Callback()
+
+        val delegateOpenHelper = FrameworkSQLiteOpenHelperFactory()
+            .create(
+                SupportSQLiteOpenHelper.Configuration
+                    .builder(ApplicationProvider.getApplicationContext())
+                    .callback(callback)
+                    .name("name")
+                    .build()
+            )
+
+        val autoCloseExecutor = ArchTaskExecutor.getIOThreadExecutor()
+
+        autoCloser = AutoCloser(
+            1,
+            TimeUnit.MILLISECONDS,
+            autoCloseExecutor
+        ).also {
+            it.init(delegateOpenHelper)
+        }
+    }
+
+    @After
+    public fun cleanUp() {
+        assertThat(countingTaskExecutorRule.isIdle).isTrue()
+    }
+
+    @Test
+    public fun refCountsCounted() {
+        autoCloser.incrementCountAndEnsureDbIsOpen()
+        assertThat(autoCloser.refCountForTest).isEqualTo(1)
+
+        autoCloser.incrementCountAndEnsureDbIsOpen()
+        assertThat(autoCloser.refCountForTest).isEqualTo(2)
+
+        autoCloser.decrementCountAndScheduleClose()
+        assertThat(autoCloser.refCountForTest).isEqualTo(1)
+
+        autoCloser.executeRefCountingFunction {
+            assertThat(autoCloser.refCountForTest).isEqualTo(2)
+        }
+        assertThat(autoCloser.refCountForTest).isEqualTo(1)
+
+        autoCloser.decrementCountAndScheduleClose()
+        assertThat(autoCloser.refCountForTest).isEqualTo(0)
+
+        // TODO(rohitsat): remove these sleeps and add a hook in AutoCloser to confirm that the
+        // scheduled tasks are done.
+        Thread.sleep(5)
+        countingTaskExecutorRule.drainTasks(10, TimeUnit.MILLISECONDS)
+    }
+
+    @Test
+    public fun executeRefCountingFunctionPropagatesFailure() {
+        assertThrows<IOException> {
+            autoCloser.executeRefCountingFunction {
+                throw IOException()
+            }
+        }
+
+        assertThat(autoCloser.refCountForTest).isEqualTo(0)
+
+        // TODO(rohitsat): remove these sleeps and add a hook in AutoCloser to confirm that the
+        // scheduled tasks are done.
+        Thread.sleep(5)
+        countingTaskExecutorRule.drainTasks(10, TimeUnit.MILLISECONDS)
+    }
+
+    @Test
+    public fun dbNotClosedWithRefCountIncremented() {
+        autoCloser.incrementCountAndEnsureDbIsOpen()
+
+        Thread.sleep(10)
+
+        assertThat(autoCloser.delegateDatabase!!.isOpen).isTrue()
+
+        autoCloser.decrementCountAndScheduleClose()
+
+        // TODO(rohitsat): remove these sleeps and add a hook in AutoCloser to confirm that the
+        // scheduled tasks are done.
+        Thread.sleep(10)
+        assertThat(autoCloser.delegateDatabase).isNull()
+    }
+
+    @Test
+    public fun getDelegatedDatabaseReturnsUnwrappedDatabase() {
+        assertThat(autoCloser.delegateDatabase).isNull()
+
+        val db = autoCloser.incrementCountAndEnsureDbIsOpen()
+        db.beginTransaction()
+        // Beginning a transaction on the unwrapped db shouldn't increment our ref count.
+        assertThat(autoCloser.refCountForTest).isEqualTo(1)
+        db.endTransaction()
+
+        autoCloser.delegateDatabase!!.beginTransaction()
+        assertThat(autoCloser.refCountForTest).isEqualTo(1)
+        autoCloser.delegateDatabase!!.endTransaction()
+        autoCloser.decrementCountAndScheduleClose()
+
+        autoCloser.executeRefCountingFunction {
+            assertThat(autoCloser.refCountForTest).isEqualTo(1)
+        }
+
+        // TODO(rohitsat): remove these sleeps and add a hook in AutoCloser to confirm that the
+        // scheduled tasks are done.
+        Thread.sleep(5)
+        countingTaskExecutorRule.drainTasks(10, TimeUnit.MILLISECONDS)
+    }
+
+    @Test
+    public fun refCountStaysIncrementedWhenErrorIsEncountered() {
+        callback.throwOnOpen = true
+        assertThrows<IOException> {
+            autoCloser.incrementCountAndEnsureDbIsOpen()
+        }
+
+        assertThat(autoCloser.refCountForTest).isEqualTo(1)
+
+        autoCloser.decrementCountAndScheduleClose()
+        callback.throwOnOpen = false
+
+        // TODO(rohitsat): remove these sleeps and add a hook in AutoCloser to confirm that the
+        // scheduled tasks are done.
+        Thread.sleep(5)
+        countingTaskExecutorRule.drainTasks(10, TimeUnit.MILLISECONDS)
+    }
+}
\ No newline at end of file
diff --git a/room/runtime/src/androidTest/java/androidx/room/migration/TableInfoTest.java b/room/runtime/src/androidTest/java/androidx/room/migration/TableInfoTest.java
index 0a20b99..b71367d 100644
--- a/room/runtime/src/androidTest/java/androidx/room/migration/TableInfoTest.java
+++ b/room/runtime/src/androidTest/java/androidx/room/migration/TableInfoTest.java
@@ -364,6 +364,7 @@
         return result;
     }
 
+    @SuppressWarnings("unchecked")
     private static <T> Set<T> toSet(T... ts) {
         final HashSet<T> result = new HashSet<T>();
         for (T t : ts) {
diff --git a/room/runtime/src/main/java/androidx/room/AutoCloser.java b/room/runtime/src/main/java/androidx/room/AutoCloser.java
new file mode 100644
index 0000000..eda2363
--- /dev/null
+++ b/room/runtime/src/main/java/androidx/room/AutoCloser.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.arch.core.util.Function;
+import androidx.room.util.SneakyThrow;
+import androidx.sqlite.db.SupportSQLiteDatabase;
+import androidx.sqlite.db.SupportSQLiteOpenHelper;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * AutoCloser is responsible for automatically opening (using
+ * delegateOpenHelper) and closing (on a timer started when there are no remaining references) a
+ * SupportSqliteDatabase.
+ *
+ * It is important to ensure that the ref count is incremented when using a returned database.
+ */
+final class AutoCloser {
+
+    @Nullable
+    private SupportSQLiteOpenHelper mDelegateOpenHelper = null;
+
+    @NonNull
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    // Package private for access from mAutoCloser
+    @NonNull
+    final Object mLock = new Object();
+
+    // Package private for access from mAutoCloser
+    final long mAutoCloseTimeoutInMs;
+
+    // Package private for access from mExecuteAutoCloser
+    @NonNull
+    final Executor mExecutor;
+
+    // Package private for access from mAutoCloser
+    @GuardedBy("mLock")
+    int mRefCount = 0;
+
+    // Package private for access from mAutoCloser
+    @GuardedBy("mLock")
+    long mLastDecrementRefCountTimeStamp = SystemClock.uptimeMillis();
+
+    // The unwrapped SupportSqliteDatabase
+    // Package private for access from mAutoCloser
+    @GuardedBy("mLock")
+    @Nullable
+    SupportSQLiteDatabase mDelegateDatabase;
+
+    private final Runnable mExecuteAutoCloser = new Runnable() {
+        @Override
+        public void run() {
+            mExecutor.execute(mAutoCloser);
+        }
+    };
+
+    // Package private for access from mExecuteAutoCloser
+    @NonNull
+    final Runnable mAutoCloser = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                if (SystemClock.uptimeMillis() - mLastDecrementRefCountTimeStamp
+                        < mAutoCloseTimeoutInMs) {
+                    // An increment + decrement beat us to closing the db. We
+                    // will not close the database, and there should be at least
+                    // one more auto-close scheduled.
+                    return;
+                }
+
+                if (mRefCount != 0) {
+                    // An increment beat us to closing the db. We don't close the
+                    // db, and another closer will be scheduled once the ref
+                    // count is decremented.
+                    return;
+                }
+
+                if (mDelegateDatabase != null) {
+                    try {
+                        mDelegateDatabase.close();
+                    } catch (IOException e) {
+                        SneakyThrow.reThrow(e);
+                    }
+                    mDelegateDatabase = null;
+                }
+            }
+        }
+    };
+
+
+    /**
+     * Construct an AutoCloser.
+     *
+     * @param autoCloseTimeoutAmount time for auto close timer
+     * @param autoCloseTimeUnit      time unit for autoCloseTimeoutAmount
+     * @param autoCloseExecutor      the executor on which the auto close operation will happen
+     */
+    AutoCloser(long autoCloseTimeoutAmount,
+            @NonNull TimeUnit autoCloseTimeUnit,
+            @NonNull Executor autoCloseExecutor) {
+        mAutoCloseTimeoutInMs = autoCloseTimeUnit.toMillis(autoCloseTimeoutAmount);
+        mExecutor = autoCloseExecutor;
+    }
+
+    /**
+     * Since we need to construct the AutoCloser in the RoomDatabase.Builder, we need to set the
+     * delegateOpenHelper after construction.
+     *
+     * @param delegateOpenHelper the open helper that is used to create
+     *                           new SupportSqliteDatabases
+     */
+    public void init(@NonNull SupportSQLiteOpenHelper delegateOpenHelper) {
+        if (mDelegateOpenHelper != null) {
+            Log.e(Room.LOG_TAG, "AutoCloser initialized multiple times. This is probably a bug in"
+                    + " the room code.");
+            return;
+        }
+        this.mDelegateOpenHelper = delegateOpenHelper;
+    }
+
+    /**
+     * Execute a ref counting function. The function will receive an unwrapped open database and
+     * this database will stay open until at least after function returns. If there are no more
+     * references in use for the db once function completes, an auto close operation will be
+     * scheduled.
+     */
+    @Nullable
+    public <V> V executeRefCountingFunction(@NonNull Function<SupportSQLiteDatabase, V> function) {
+        try {
+            SupportSQLiteDatabase db = incrementCountAndEnsureDbIsOpen();
+            return function.apply(db);
+        } finally {
+            decrementCountAndScheduleClose();
+        }
+    }
+
+    /**
+     * Confirms that autoCloser is no longer running and confirms that mDelegateDatabase is set
+     * and open. mDelegateDatabase will not be auto closed until
+     * decrementRefCountAndScheduleClose is called. decrementRefCountAndScheduleClose must be
+     * called once for each call to incrementCountAndEnsureDbIsOpen.
+     *
+     * If this throws an exception, decrementCountAndScheduleClose must still be called!
+     *
+     * @return the *unwrapped* SupportSQLiteDatabase.
+     */
+    @NonNull
+    public SupportSQLiteDatabase incrementCountAndEnsureDbIsOpen() {
+        //TODO(rohitsat): avoid synchronized(mLock) when possible. We should be able to avoid it
+        // when refCount is not hitting zero or if there is no auto close scheduled if we use
+        // Atomics.
+        synchronized (mLock) {
+            // If there is a scheduled autoclose operation, we should remove it from the handler.
+            mHandler.removeCallbacks(mExecuteAutoCloser);
+
+            mRefCount++;
+
+            if (mDelegateDatabase != null && mDelegateDatabase.isOpen()) {
+                return mDelegateDatabase;
+            } else if (mDelegateDatabase != null) {
+                // This shouldn't happen
+                throw new IllegalStateException("mDelegateDatabase is closed but non-null");
+            }
+
+            // Get the database while holding `mLock` so no other threads try to create it or
+            // destroy it.
+            if (mDelegateOpenHelper != null) {
+                mDelegateDatabase = mDelegateOpenHelper.getWritableDatabase();
+            } else {
+                throw new IllegalStateException("AutoCloser has not beeninitialized. This "
+                        + "shouldn't happen, but if it does it means there's a bug in our code");
+            }
+
+            return mDelegateDatabase;
+        }
+    }
+
+    /**
+     * Decrements the ref count and schedules a close if there are no other references to the db.
+     * This must only be called after a corresponding incrementCountAndEnsureDbIsOpen call.
+     */
+    public void decrementCountAndScheduleClose() {
+        //TODO(rohitsat): avoid synchronized(mLock) when possible
+        synchronized (mLock) {
+            if (mRefCount <= 0) {
+                throw new IllegalStateException("ref count is 0 or lower but we're supposed to "
+                        + "decrement");
+            }
+
+            // decrement refCount
+            mRefCount--;
+
+            // if refcount is zero, schedule close operation
+            if (mRefCount == 0) {
+                if (mDelegateDatabase == null) {
+                    // No db to close, this can happen due to exceptions when creating db...
+                    return;
+                }
+                mHandler.postDelayed(mExecuteAutoCloser, mAutoCloseTimeoutInMs);
+            }
+        }
+    }
+
+    /**
+     * Returns the underlying database. This does not ensure that the database is open; the
+     * caller is responsible for ensuring that the database is open and the ref count is non-zero.
+     *
+     * This is primarily meant for use cases where we don't want to open the database (isOpen) or
+     * we know that the database is already open (KeepAliveCursor).
+     */
+    @Nullable // Since the db might be closed
+    public SupportSQLiteDatabase getDelegateDatabase() {
+        synchronized (mLock) {
+            return mDelegateDatabase;
+        }
+    }
+
+    /**
+     * Returns the current ref count for this auto closer. This is only visible for testing.
+     *
+     * @return current ref count
+     */
+    @VisibleForTesting
+    public int getRefCountForTest() {
+        synchronized (mLock) {
+            return mRefCount;
+        }
+    }
+}
diff --git a/room/runtime/src/main/java/androidx/room/QueryInterceptorDatabase.java b/room/runtime/src/main/java/androidx/room/QueryInterceptorDatabase.java
new file mode 100644
index 0000000..c5ef6bc
--- /dev/null
+++ b/room/runtime/src/main/java/androidx/room/QueryInterceptorDatabase.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteTransactionListener;
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.sqlite.db.SupportSQLiteDatabase;
+import androidx.sqlite.db.SupportSQLiteQuery;
+import androidx.sqlite.db.SupportSQLiteStatement;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.Executor;
+
+
+/**
+ * Implements {@link SupportSQLiteDatabase} for SQLite queries.
+ */
+final class QueryInterceptorDatabase implements SupportSQLiteDatabase {
+
+    private final SupportSQLiteDatabase mDelegate;
+    private final RoomDatabase.QueryCallback mQueryCallback;
+    private final Executor mQueryCallbackExecutor;
+
+    QueryInterceptorDatabase(@NonNull SupportSQLiteDatabase supportSQLiteDatabase,
+            @NonNull RoomDatabase.QueryCallback queryCallback, @NonNull Executor
+            queryCallbackExecutor) {
+        mDelegate = supportSQLiteDatabase;
+        mQueryCallback = queryCallback;
+        mQueryCallbackExecutor = queryCallbackExecutor;
+    }
+
+    @NonNull
+    @Override
+    public SupportSQLiteStatement compileStatement(@NonNull String sql) {
+        return new QueryInterceptorStatement(mDelegate.compileStatement(sql),
+                mQueryCallback, sql, mQueryCallbackExecutor);
+    }
+
+    @Override
+    public void beginTransaction() {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("BEGIN EXCLUSIVE TRANSACTION",
+                Collections.emptyList()));
+        mDelegate.beginTransaction();
+    }
+
+    @Override
+    public void beginTransactionNonExclusive() {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("BEGIN DEFERRED TRANSACTION",
+                Collections.emptyList()));
+        mDelegate.beginTransactionNonExclusive();
+    }
+
+    @Override
+    public void beginTransactionWithListener(@NonNull SQLiteTransactionListener
+            transactionListener) {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("BEGIN EXCLUSIVE TRANSACTION",
+                Collections.emptyList()));
+        mDelegate.beginTransactionWithListener(transactionListener);
+    }
+
+    @Override
+    public void beginTransactionWithListenerNonExclusive(
+            @NonNull SQLiteTransactionListener transactionListener) {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("BEGIN DEFERRED TRANSACTION",
+                Collections.emptyList()));
+        mDelegate.beginTransactionWithListenerNonExclusive(transactionListener);
+    }
+
+    @Override
+    public void endTransaction() {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("END TRANSACTION",
+                Collections.emptyList()));
+        mDelegate.endTransaction();
+    }
+
+    @Override
+    public void setTransactionSuccessful() {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery("TRANSACTION SUCCESSFUL",
+                Collections.emptyList()));
+        mDelegate.setTransactionSuccessful();
+    }
+
+    @Override
+    public boolean inTransaction() {
+        return mDelegate.inTransaction();
+    }
+
+    @Override
+    public boolean isDbLockedByCurrentThread() {
+        return mDelegate.isDbLockedByCurrentThread();
+    }
+
+    @Override
+    public boolean yieldIfContendedSafely() {
+        return mDelegate.yieldIfContendedSafely();
+    }
+
+    @Override
+    public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
+        return mDelegate.yieldIfContendedSafely(sleepAfterYieldDelay);
+    }
+
+    @Override
+    public int getVersion() {
+        return mDelegate.getVersion();
+    }
+
+    @Override
+    public void setVersion(int version) {
+        mDelegate.setVersion(version);
+    }
+
+    @Override
+    public long getMaximumSize() {
+        return mDelegate.getMaximumSize();
+    }
+
+    @Override
+    public long setMaximumSize(long numBytes) {
+        return mDelegate.setMaximumSize(numBytes);
+    }
+
+    @Override
+    public long getPageSize() {
+        return mDelegate.getPageSize();
+    }
+
+    @Override
+    public void setPageSize(long numBytes) {
+        mDelegate.setPageSize(numBytes);
+    }
+
+    @NonNull
+    @Override
+    public Cursor query(@NonNull String query) {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query,
+                Collections.emptyList()));
+        return mDelegate.query(query);
+    }
+
+    @NonNull
+    @Override
+    public Cursor query(@NonNull String query, @NonNull Object[] bindArgs) {
+        List<Object> inputArguments = new ArrayList<>();
+        inputArguments.addAll(Arrays.asList(bindArgs));
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query,
+                inputArguments));
+        return mDelegate.query(query, bindArgs);
+    }
+
+    @NonNull
+    @Override
+    public Cursor query(@NonNull SupportSQLiteQuery query) {
+        QueryInterceptorProgram queryInterceptorProgram = new QueryInterceptorProgram();
+        query.bindTo(queryInterceptorProgram);
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query.getSql(),
+                queryInterceptorProgram.getBindArgs()));
+        return mDelegate.query(query);
+    }
+
+    @NonNull
+    @Override
+    public Cursor query(@NonNull SupportSQLiteQuery query,
+            @NonNull CancellationSignal cancellationSignal) {
+        QueryInterceptorProgram queryInterceptorProgram = new QueryInterceptorProgram();
+        query.bindTo(queryInterceptorProgram);
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query.getSql(),
+                queryInterceptorProgram.getBindArgs()));
+        return mDelegate.query(query);
+    }
+
+    @Override
+    public long insert(@NonNull String table, int conflictAlgorithm, @NonNull ContentValues values)
+            throws SQLException {
+        return mDelegate.insert(table, conflictAlgorithm, values);
+    }
+
+    @Override
+    public int delete(@NonNull String table, @NonNull String whereClause,
+            @NonNull Object[] whereArgs) {
+        return mDelegate.delete(table, whereClause, whereArgs);
+    }
+
+    @Override
+    public int update(@NonNull String table, int conflictAlgorithm, @NonNull ContentValues values,
+            @NonNull String whereClause,
+            @NonNull Object[] whereArgs) {
+        return mDelegate.update(table, conflictAlgorithm, values, whereClause,
+                whereArgs);
+    }
+
+    @Override
+    public void execSQL(@NonNull String sql) throws SQLException {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(sql, new ArrayList<>(0)));
+        mDelegate.execSQL(sql);
+    }
+
+    @Override
+    public void execSQL(@NonNull String sql, @NonNull Object[] bindArgs) throws SQLException {
+        List<Object> inputArguments = new ArrayList<>();
+        inputArguments.addAll(Arrays.asList(bindArgs));
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(sql, inputArguments));
+        mDelegate.execSQL(sql, inputArguments.toArray());
+    }
+
+    @Override
+    public boolean isReadOnly() {
+        return mDelegate.isReadOnly();
+    }
+
+    @Override
+    public boolean isOpen() {
+        return mDelegate.isOpen();
+    }
+
+    @Override
+    public boolean needUpgrade(int newVersion) {
+        return mDelegate.needUpgrade(newVersion);
+    }
+
+    @NonNull
+    @Override
+    public String getPath() {
+        return mDelegate.getPath();
+    }
+
+    @Override
+    public void setLocale(@NonNull Locale locale) {
+        mDelegate.setLocale(locale);
+    }
+
+    @Override
+    public void setMaxSqlCacheSize(int cacheSize) {
+        mDelegate.setMaxSqlCacheSize(cacheSize);
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public void setForeignKeyConstraintsEnabled(boolean enable) {
+        mDelegate.setForeignKeyConstraintsEnabled(enable);
+    }
+
+    @Override
+    public boolean enableWriteAheadLogging() {
+        return mDelegate.enableWriteAheadLogging();
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public void disableWriteAheadLogging() {
+        mDelegate.disableWriteAheadLogging();
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public boolean isWriteAheadLoggingEnabled() {
+        return mDelegate.isWriteAheadLoggingEnabled();
+    }
+
+    @NonNull
+    @Override
+    public List<Pair<String, String>> getAttachedDbs() {
+        return mDelegate.getAttachedDbs();
+    }
+
+    @Override
+    public boolean isDatabaseIntegrityOk() {
+        return mDelegate.isDatabaseIntegrityOk();
+    }
+
+    @Override
+    public void close() throws IOException {
+        mDelegate.close();
+    }
+}
diff --git a/room/runtime/src/main/java/androidx/room/QueryInterceptorOpenHelper.java b/room/runtime/src/main/java/androidx/room/QueryInterceptorOpenHelper.java
new file mode 100644
index 0000000..c2ca486
--- /dev/null
+++ b/room/runtime/src/main/java/androidx/room/QueryInterceptorOpenHelper.java
@@ -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.room;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.sqlite.db.SupportSQLiteDatabase;
+import androidx.sqlite.db.SupportSQLiteOpenHelper;
+
+import java.util.concurrent.Executor;
+
+final class QueryInterceptorOpenHelper implements SupportSQLiteOpenHelper {
+
+    private final SupportSQLiteOpenHelper mDelegate;
+    private final RoomDatabase.QueryCallback mQueryCallback;
+    private final Executor mQueryCallbackExecutor;
+
+    QueryInterceptorOpenHelper(@NonNull SupportSQLiteOpenHelper supportSQLiteOpenHelper,
+            @NonNull RoomDatabase.QueryCallback queryCallback, @NonNull Executor
+            queryCallbackExecutor) {
+        mDelegate = supportSQLiteOpenHelper;
+        mQueryCallback = queryCallback;
+        mQueryCallbackExecutor = queryCallbackExecutor;
+    }
+
+    @Nullable
+    @Override
+    public String getDatabaseName() {
+        return mDelegate.getDatabaseName();
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public void setWriteAheadLoggingEnabled(boolean enabled) {
+        mDelegate.setWriteAheadLoggingEnabled(enabled);
+    }
+
+    @Override
+    public SupportSQLiteDatabase getWritableDatabase() {
+        return new QueryInterceptorDatabase(mDelegate.getWritableDatabase(), mQueryCallback,
+                mQueryCallbackExecutor);
+    }
+
+    @Override
+    public SupportSQLiteDatabase getReadableDatabase() {
+        return new QueryInterceptorDatabase(mDelegate.getReadableDatabase(), mQueryCallback,
+                mQueryCallbackExecutor);
+    }
+
+    @Override
+    public void close() {
+        mDelegate.close();
+    }
+}
diff --git a/room/runtime/src/main/java/androidx/room/QueryInterceptorOpenHelperFactory.java b/room/runtime/src/main/java/androidx/room/QueryInterceptorOpenHelperFactory.java
new file mode 100644
index 0000000..5d94cd1
--- /dev/null
+++ b/room/runtime/src/main/java/androidx/room/QueryInterceptorOpenHelperFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room;
+
+import androidx.annotation.NonNull;
+import androidx.sqlite.db.SupportSQLiteOpenHelper;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Implements {@link SupportSQLiteOpenHelper.Factory} to wrap QueryInterceptorOpenHelper.
+ */
+@SuppressWarnings("AcronymName")
+final class QueryInterceptorOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
+
+    private final SupportSQLiteOpenHelper.Factory mDelegate;
+    private final RoomDatabase.QueryCallback mQueryCallback;
+    private final Executor mQueryCallbackExecutor;
+
+    @SuppressWarnings("LambdaLast")
+    QueryInterceptorOpenHelperFactory(@NonNull SupportSQLiteOpenHelper.Factory factory,
+            @NonNull RoomDatabase.QueryCallback queryCallback,
+            @NonNull Executor queryCallbackExecutor) {
+        mDelegate = factory;
+        mQueryCallback = queryCallback;
+        mQueryCallbackExecutor = queryCallbackExecutor;
+    }
+
+    @NonNull
+    @Override
+    public SupportSQLiteOpenHelper create(
+            @NonNull SupportSQLiteOpenHelper.Configuration configuration) {
+        return new QueryInterceptorOpenHelper(mDelegate.create(configuration), mQueryCallback,
+                mQueryCallbackExecutor);
+    }
+}
diff --git a/room/runtime/src/main/java/androidx/room/QueryInterceptorProgram.java b/room/runtime/src/main/java/androidx/room/QueryInterceptorProgram.java
new file mode 100644
index 0000000..2b9c554
--- /dev/null
+++ b/room/runtime/src/main/java/androidx/room/QueryInterceptorProgram.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room;
+
+import androidx.sqlite.db.SupportSQLiteProgram;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A program implementing an {@link SupportSQLiteProgram} API to record bind arguments.
+ */
+final class QueryInterceptorProgram implements SupportSQLiteProgram {
+    private List<Object> mBindArgsCache = new ArrayList<>();
+
+    @Override
+    public void bindNull(int index) {
+        saveArgsToCache(index, null);
+    }
+
+    @Override
+    public void bindLong(int index, long value) {
+        saveArgsToCache(index, value);
+    }
+
+    @Override
+    public void bindDouble(int index, double value) {
+        saveArgsToCache(index, value);
+    }
+
+    @Override
+    public void bindString(int index, String value) {
+        saveArgsToCache(index, value);
+    }
+
+    @Override
+    public void bindBlob(int index, byte[] value) {
+        saveArgsToCache(index, value);
+    }
+
+    @Override
+    public void clearBindings() {
+        mBindArgsCache.clear();
+    }
+
+    @Override
+    public void close() { }
+
+    private void saveArgsToCache(int bindIndex, Object value) {
+        // The index into bind methods are 1...n
+        int index = bindIndex - 1;
+        if (index >= mBindArgsCache.size()) {
+            for (int i = mBindArgsCache.size(); i <= index; i++) {
+                mBindArgsCache.add(null);
+            }
+        }
+        mBindArgsCache.set(index, value);
+    }
+
+    /**
+     * Returns the list of arguments associated with the query.
+     *
+     * @return argument list.
+     */
+    List<Object> getBindArgs() {
+        return mBindArgsCache;
+    }
+}
diff --git a/room/runtime/src/main/java/androidx/room/QueryInterceptorStatement.java b/room/runtime/src/main/java/androidx/room/QueryInterceptorStatement.java
new file mode 100644
index 0000000..8825252
--- /dev/null
+++ b/room/runtime/src/main/java/androidx/room/QueryInterceptorStatement.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room;
+
+import androidx.annotation.NonNull;
+import androidx.sqlite.db.SupportSQLiteStatement;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Implements an instance of {@link SupportSQLiteStatement} for SQLite queries.
+ */
+final class QueryInterceptorStatement implements SupportSQLiteStatement {
+
+    private final SupportSQLiteStatement mDelegate;
+    private final RoomDatabase.QueryCallback mQueryCallback;
+    private final String mSqlStatement;
+    private final List<Object> mBindArgsCache = new ArrayList<>();
+    private final Executor mQueryCallbackExecutor;
+
+    QueryInterceptorStatement(@NonNull SupportSQLiteStatement compileStatement,
+            @NonNull RoomDatabase.QueryCallback queryCallback, String sqlStatement,
+            @NonNull Executor queryCallbackExecutor) {
+        mDelegate = compileStatement;
+        mQueryCallback = queryCallback;
+        mSqlStatement = sqlStatement;
+        mQueryCallbackExecutor = queryCallbackExecutor;
+    }
+
+    @Override
+    public void execute() {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(mSqlStatement, mBindArgsCache));
+        mDelegate.execute();
+    }
+
+    @Override
+    public int executeUpdateDelete() {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(mSqlStatement, mBindArgsCache));
+        return mDelegate.executeUpdateDelete();
+    }
+
+    @Override
+    public long executeInsert() {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(mSqlStatement, mBindArgsCache));
+        return mDelegate.executeInsert();
+    }
+
+    @Override
+    public long simpleQueryForLong() {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(mSqlStatement, mBindArgsCache));
+        return mDelegate.simpleQueryForLong();
+    }
+
+    @Override
+    public String simpleQueryForString() {
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(mSqlStatement, mBindArgsCache));
+        return mDelegate.simpleQueryForString();
+    }
+
+    @Override
+    public void bindNull(int index) {
+        saveArgsToCache(index, mBindArgsCache.toArray());
+        mDelegate.bindNull(index);
+    }
+
+    @Override
+    public void bindLong(int index, long value) {
+        saveArgsToCache(index, value);
+        mDelegate.bindLong(index, value);
+    }
+
+    @Override
+    public void bindDouble(int index, double value) {
+        saveArgsToCache(index, value);
+        mDelegate.bindDouble(index, value);
+    }
+
+    @Override
+    public void bindString(int index, String value) {
+        saveArgsToCache(index, value);
+        mDelegate.bindString(index, value);
+    }
+
+    @Override
+    public void bindBlob(int index, byte[] value) {
+        saveArgsToCache(index, value);
+        mDelegate.bindBlob(index, value);
+    }
+
+    @Override
+    public void clearBindings() {
+        mBindArgsCache.clear();
+        mDelegate.clearBindings();
+    }
+
+    @Override
+    public void close() throws IOException {
+        mDelegate.close();
+    }
+
+    private void saveArgsToCache(int bindIndex, Object value) {
+        int index = bindIndex - 1;
+        if (index >= mBindArgsCache.size()) {
+            // Add null entries to the list until we have the desired # of indices
+            for (int i = mBindArgsCache.size(); i <= index; i++) {
+                mBindArgsCache.add(null);
+            }
+        }
+        mBindArgsCache.set(index, value);
+    }
+}
diff --git a/room/runtime/src/main/java/androidx/room/RoomDatabase.java b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
index 7fbf181..4561876 100644
--- a/room/runtime/src/main/java/androidx/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
@@ -620,6 +620,8 @@
         private final Context mContext;
         private ArrayList<Callback> mCallbacks;
         private PrepackagedDatabaseCallback mPrepackagedDatabaseCallback;
+        private QueryCallback mQueryCallback;
+        private Executor mQueryCallbackExecutor;
         private List<Object> mTypeConverters;
 
         /** The Executor used to run database queries. This should be background-threaded. */
@@ -1092,6 +1094,27 @@
         }
 
         /**
+         * Sets a {@link QueryCallback} to be invoked when queries are executed.
+         * <p>
+         * The callback is invoked whenever a query is executed, note that adding this callback
+         * has a small cost and should be avoided in production builds unless needed.
+         * <p>
+         * A use case for providing a callback is to allow logging executed queries. When the
+         * callback implementation logs then it is recommended to use an immediate executor.
+         *
+         * @param queryCallback The query callback.
+         * @param executor The executor on which the query callback will be invoked.
+         */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder<T> setQueryCallback(@NonNull QueryCallback queryCallback,
+                @NonNull Executor executor) {
+            mQueryCallback = queryCallback;
+            mQueryCallbackExecutor = executor;
+            return this;
+        }
+
+        /**
          * Adds a type converter instance to this database.
          *
          * @param typeConverter The converter. It must be an instance of a class annotated with
@@ -1149,8 +1172,12 @@
                 }
             }
 
+            SupportSQLiteOpenHelper.Factory factory;
+
             if (mFactory == null) {
-                mFactory = new FrameworkSQLiteOpenHelperFactory();
+                factory = new FrameworkSQLiteOpenHelperFactory();
+            } else {
+                factory = mFactory;
             }
 
             if (mCopyFromAssetPath != null
@@ -1170,14 +1197,20 @@
                             + "Builder, but the database can only be created using one of the "
                             + "three configurations.");
                 }
-                mFactory = new SQLiteCopyOpenHelperFactory(mCopyFromAssetPath, mCopyFromFile,
-                        mCopyFromInputStream, mFactory);
+                factory = new SQLiteCopyOpenHelperFactory(mCopyFromAssetPath, mCopyFromFile,
+                        mCopyFromInputStream, factory);
             }
+
+            if (mQueryCallback != null) {
+                factory = new QueryInterceptorOpenHelperFactory(factory, mQueryCallback,
+                        mQueryCallbackExecutor);
+            }
+
             DatabaseConfiguration configuration =
                     new DatabaseConfiguration(
                             mContext,
                             mName,
-                            mFactory,
+                            factory,
                             mMigrationContainer,
                             mCallbacks,
                             mAllowMainThreadQueries,
@@ -1345,4 +1378,21 @@
         public void onOpenPrepackagedDatabase(@NonNull SupportSQLiteDatabase db) {
         }
     }
+
+    /**
+     * Callback interface for when SQLite queries are executed.
+     *
+     * @see RoomDatabase.Builder#setQueryCallback
+     */
+    public interface QueryCallback {
+
+        /**
+         * Called when a SQL query is executed.
+         *
+         * @param sqlQuery The SQLite query statement.
+         * @param bindArgs Arguments of the query if available, empty list otherwise.
+         */
+        void onQuery(@NonNull String sqlQuery, @NonNull List<Object>
+                bindArgs);
+    }
 }
diff --git a/room/runtime/src/test/java/androidx/room/BuilderTest.java b/room/runtime/src/test/java/androidx/room/BuilderTest.java
index ae6d83b..63ce98d 100644
--- a/room/runtime/src/test/java/androidx/room/BuilderTest.java
+++ b/room/runtime/src/test/java/androidx/room/BuilderTest.java
@@ -44,7 +44,7 @@
 import java.util.List;
 import java.util.concurrent.Executor;
 
-@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+@SuppressWarnings({"ArraysAsListWithZeroOrOneArgument", "deprecation"})
 @RunWith(JUnit4.class)
 public class BuilderTest {
     @Test(expected = IllegalArgumentException.class)
diff --git a/room/runtime/src/test/java/androidx/room/InvalidationTrackerTest.java b/room/runtime/src/test/java/androidx/room/InvalidationTrackerTest.java
index 66a7047..e52d47f 100644
--- a/room/runtime/src/test/java/androidx/room/InvalidationTrackerTest.java
+++ b/room/runtime/src/test/java/androidx/room/InvalidationTrackerTest.java
@@ -427,6 +427,7 @@
         assertThat(observer.getInvalidatedTables(), hasItem("a"));
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void failFastCreateLiveData() {
         // assert that sending a bad createLiveData table name fails instantly
diff --git a/room/settings.gradle b/room/settings.gradle
index 37a197fa..6b6675b 100644
--- a/room/settings.gradle
+++ b/room/settings.gradle
@@ -21,5 +21,6 @@
 selectProjectsFromAndroidX({ name ->
     if (name.startsWith(":room")) return true
     if (name.startsWith(":sqlite") && !name.contains("inspection")) return true
+    if (name == ":internal-testutils-truth") return true
     return false
 })
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/view/WindowInsetsControllerPlayground.kt b/samples/Support4Demos/src/main/java/com/example/android/supportv4/view/WindowInsetsControllerPlayground.kt
index 2eb9f74..afe2ed4 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/view/WindowInsetsControllerPlayground.kt
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/view/WindowInsetsControllerPlayground.kt
@@ -15,45 +15,68 @@
  */
 package com.example.android.supportv4.view
 
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
 import android.annotation.SuppressLint
 import android.app.Activity
-import android.os.Build
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
 import android.os.Bundle
 import android.os.SystemClock
 import android.util.Log
+import android.view.MotionEvent
 import android.view.View
+import android.view.ViewConfiguration
 import android.view.ViewGroup
 import android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
+import android.view.animation.LinearInterpolator
 import android.widget.AdapterView
 import android.widget.ArrayAdapter
 import android.widget.Button
 import android.widget.CheckBox
-import android.widget.EditText
+import android.widget.LinearLayout
 import android.widget.Spinner
 import android.widget.TextView
 import android.widget.ToggleButton
 import androidx.annotation.RequiresApi
+import androidx.core.graphics.Insets
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsAnimationCompat
+import androidx.core.view.WindowInsetsAnimationControlListenerCompat
+import androidx.core.view.WindowInsetsAnimationControllerCompat
 import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.WindowInsetsCompat.Type.ime
+import androidx.core.view.WindowInsetsCompat.Type.systemBars
 import androidx.core.view.WindowInsetsControllerCompat
 import com.example.android.supportv4.R
+import java.util.ArrayList
 import kotlin.concurrent.thread
+import kotlin.math.abs
+import kotlin.math.max
+import kotlin.math.min
 
 @SuppressLint("InlinedApi")
-@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+@RequiresApi(21)
 class WindowInsetsControllerPlayground : Activity() {
 
-    private val TAG = WindowInsetsControllerPlayground::class.java.name
+    private val TAG: String = "WindowInsets_Playground"
 
+    val mTransitions = ArrayList<Transition>()
     var currentType: Int? = null
 
     private lateinit var mRoot: View
-    private lateinit var editText: EditText
+    private lateinit var editRow: ViewGroup
     private lateinit var visibility: TextView
-    private lateinit var checkbox: CheckBox
     private lateinit var buttonsRow: ViewGroup
+    private lateinit var fitSystemWindow: CheckBox
+    private lateinit var isDecorView: CheckBox
+    internal lateinit var info: TextView
+    lateinit var graph: View
+
+    val values = mutableListOf(0f)
 
     @SuppressLint("SetTextI18n")
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -62,12 +85,15 @@
         setActionBar(findViewById(R.id.toolbar))
 
         mRoot = findViewById(R.id.root)
-        editText = findViewById(R.id.editText)
+        editRow = findViewById(R.id.editRow)
         visibility = findViewById(R.id.visibility)
-        checkbox = findViewById(R.id.decorFitsSystemWindows)
         buttonsRow = findViewById(R.id.buttonRow)
+        info = findViewById(R.id.info)
+        fitSystemWindow = findViewById(R.id.decorFitsSystemWindows)
+        isDecorView = findViewById(R.id.isDecorView)
+        addPlot()
 
-        WindowCompat.setDecorFitsSystemWindows(window, false)
+        WindowCompat.setDecorFitsSystemWindows(window, fitSystemWindow.isChecked)
         ViewCompat.getWindowInsetsController(mRoot)!!.systemBarsBehavior =
             WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
 
@@ -79,22 +105,22 @@
                 )
         )
 
-        checkbox.apply {
+        fitSystemWindow.apply {
             isChecked = false
             setOnCheckedChangeListener { _, isChecked ->
                 WindowCompat.setDecorFitsSystemWindows(window, isChecked)
             }
         }
 
+        mTransitions.add(Transition(findViewById(R.id.scrollView)))
+        mTransitions.add(Transition(editRow))
+
         setupTypeSpinner()
         setupHideShowButtons()
         setupAppearanceButtons()
 
         ViewCompat.setOnApplyWindowInsetsListener(mRoot) { _: View?, insets: WindowInsetsCompat ->
-            val systemBarInsets = insets.getInsets(
-                ime() or
-                    WindowInsetsCompat.Type.systemBars()
-            )
+            val systemBarInsets = insets.getInsets(ime() or systemBars())
             mRoot.setPadding(
                 systemBarInsets.left,
                 systemBarInsets.top,
@@ -106,6 +132,46 @@
 
             WindowInsetsCompat.CONSUMED
         }
+
+        setupIMEAnimation()
+        setupActionButton()
+
+        isDecorView.setOnCheckedChangeListener { _, _ ->
+            setupIMEAnimation()
+        }
+    }
+
+    private fun addPlot() {
+        val stroke = 20
+        val p2 = Paint()
+        p2.color = Color.RED
+        p2.strokeWidth = 1f
+        p2.style = Paint.Style.FILL
+
+        graph = object : View(this) {
+            override fun onDraw(canvas: Canvas) {
+                super.onDraw(canvas)
+                val mx = (values.maxOrNull() ?: 0f) + 1
+                val mn = values.minOrNull() ?: 0f
+                val ct = values.size.toFloat()
+
+                val h = height - stroke * 2
+                val w = width - stroke * 2
+                values.forEachIndexed { i, f ->
+                    val x = (i / ct) * w + stroke
+                    val y = ((f - mn) / (mx - mn)) * h + stroke
+                    canvas.drawCircle(x, y, stroke.toFloat(), p2)
+                }
+            }
+        }
+        graph.minimumWidth = 300
+        graph.minimumHeight = 100
+        graph.setBackgroundColor(Color.GRAY)
+        val linearLayout = info.parent as LinearLayout
+        linearLayout.addView(
+            graph, linearLayout.indexOfChild(isDecorView),
+            ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200)
+        )
     }
 
     private fun setupAppearanceButtons() {
@@ -163,6 +229,89 @@
         visibilityThreadRunning = false
     }
 
+    private fun setupActionButton() {
+        findViewById<View>(R.id.floating_action_button).setOnClickListener { v: View? ->
+            ViewCompat.getWindowInsetsController(v!!)!!.controlWindowInsetsAnimation(
+                ime(), -1, LinearInterpolator(), null /* cancellationSignal */,
+                object : WindowInsetsAnimationControlListenerCompat {
+                    override fun onReady(
+                        controller: WindowInsetsAnimationControllerCompat,
+                        types: Int
+                    ) {
+                        val anim =
+                            ValueAnimator.ofFloat(0f, 1f)
+                        anim.duration = 1500
+                        anim.addUpdateListener { animation: ValueAnimator ->
+                            controller.setInsetsAndAlpha(
+                                controller.shownStateInsets,
+                                animation.animatedValue as Float,
+                                anim.animatedFraction
+                            )
+                        }
+                        anim.addListener(
+                            object : AnimatorListenerAdapter() {
+                                override fun onAnimationEnd(animation: Animator) {
+                                    super.onAnimationEnd(animation)
+                                    controller.finish(true)
+                                }
+                            })
+                        anim.start()
+                    }
+
+                    override fun onCancelled(
+                        controller: WindowInsetsAnimationControllerCompat?
+                    ) {
+                    }
+
+                    override fun onFinished(
+                        controller: WindowInsetsAnimationControllerCompat
+                    ) {
+                    }
+                }
+            )
+        }
+    }
+
+    private fun setupIMEAnimation() {
+        mRoot.setOnTouchListener(createOnTouchListener())
+        if (isDecorView.isChecked) {
+            ViewCompat.setWindowInsetsAnimationCallback(mRoot, null)
+            ViewCompat.setWindowInsetsAnimationCallback(window.decorView, createAnimationCallback())
+            // Why it doesn't work on the root view?
+        } else {
+            ViewCompat.setWindowInsetsAnimationCallback(window.decorView, null)
+            ViewCompat.setWindowInsetsAnimationCallback(mRoot, createAnimationCallback())
+        }
+    }
+
+    private fun createAnimationCallback(): WindowInsetsAnimationCompat.Callback {
+        return object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
+            override fun onPrepare(animation: WindowInsetsAnimationCompat) {
+                mTransitions.forEach { it.onPrepare(animation) }
+            }
+
+            override fun onProgress(
+                insets: WindowInsetsCompat,
+                runningAnimations: List<WindowInsetsAnimationCompat>
+            ): WindowInsetsCompat {
+                mTransitions.forEach { it.onProgress() }
+                return insets
+            }
+
+            override fun onStart(
+                animation: WindowInsetsAnimationCompat,
+                bounds: WindowInsetsAnimationCompat.Bounds
+            ): WindowInsetsAnimationCompat.Bounds {
+                mTransitions.forEach { obj -> obj.onStart() }
+                return bounds
+            }
+
+            override fun onEnd(animation: WindowInsetsAnimationCompat) {
+                mTransitions.forEach { it.onFinish(animation) }
+            }
+        }
+    }
+
     private fun setupHideShowButtons() {
         findViewById<Button>(R.id.btn_show).apply {
             setOnClickListener { view ->
@@ -180,15 +329,117 @@
         }
     }
 
+    private fun createOnTouchListener(): View.OnTouchListener {
+        return object : View.OnTouchListener {
+            private val mViewConfiguration =
+                ViewConfiguration.get(this@WindowInsetsControllerPlayground)
+            var mAnimationController: WindowInsetsAnimationControllerCompat? = null
+            var mCurrentRequest: WindowInsetsAnimationControlListenerCompat? = null
+            var mRequestedController = false
+            var mDown = 0f
+            var mCurrent = 0f
+            var mDownInsets = Insets.NONE
+            var mShownAtDown = false
+
+            @SuppressLint("ClickableViewAccessibility")
+            override fun onTouch(
+                v: View,
+                event: MotionEvent
+            ): Boolean {
+                mCurrent = event.y
+                when (event.action) {
+                    MotionEvent.ACTION_DOWN -> {
+                        mDown = event.y
+                        val rootWindowInsets = ViewCompat.getRootWindowInsets(v)!!
+                        mDownInsets = rootWindowInsets.getInsets(ime())
+                        mShownAtDown = rootWindowInsets.isVisible(ime())
+                        mRequestedController = false
+                        mCurrentRequest = null
+                    }
+                    MotionEvent.ACTION_MOVE -> {
+                        if (mAnimationController != null) {
+                            updateInset()
+                        } else if (abs(mDown - event.y) > mViewConfiguration.scaledTouchSlop &&
+                            !mRequestedController
+                        ) {
+                            mRequestedController = true
+                            val listener = object : WindowInsetsAnimationControlListenerCompat {
+                                override fun onReady(
+                                    controller: WindowInsetsAnimationControllerCompat,
+                                    types: Int
+                                ) {
+                                    if (mCurrentRequest === this) {
+                                        mAnimationController = controller
+                                        updateInset()
+                                    } else {
+                                        controller.finish(mShownAtDown)
+                                    }
+                                }
+
+                                override fun onFinished(
+                                    controller: WindowInsetsAnimationControllerCompat
+                                ) {
+                                    mAnimationController = null
+                                }
+
+                                override fun onCancelled(
+                                    controller: WindowInsetsAnimationControllerCompat?
+                                ) {
+                                    mAnimationController = null
+                                }
+                            }
+                            mCurrentRequest = listener
+
+                            ViewCompat.getWindowInsetsController(v)!!.controlWindowInsetsAnimation(
+                                ime(),
+                                1000,
+                                LinearInterpolator(),
+                                null /* cancellationSignal */,
+                                listener
+                            )
+                        }
+                    }
+                    MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
+                        if (mAnimationController != null) {
+                            val isCancel =
+                                event.action == MotionEvent.ACTION_CANCEL
+                            mAnimationController!!.finish(
+                                if (isCancel) mShownAtDown else !mShownAtDown
+                            )
+                            mAnimationController = null
+                        }
+                        mRequestedController = false
+                        mCurrentRequest = null
+                    }
+                }
+                return true
+            }
+
+            fun updateInset() {
+                var inset = (mDownInsets.bottom + (mDown - mCurrent)).toInt()
+                val hidden = mAnimationController!!.hiddenStateInsets.bottom
+                val shown = mAnimationController!!.shownStateInsets.bottom
+                val start = if (mShownAtDown) shown else hidden
+                val end = if (mShownAtDown) hidden else shown
+                inset = max(inset, hidden)
+                inset = min(inset, shown)
+                mAnimationController!!.setInsetsAndAlpha(
+                    Insets.of(0, 0, 0, inset),
+                    1f, (inset - start) / (end - start).toFloat()
+                )
+            }
+        }
+    }
+
     private fun setupTypeSpinner() {
         val types = mapOf(
             "IME" to ime(),
             "Navigation" to WindowInsetsCompat.Type.navigationBars(),
-            "System" to WindowInsetsCompat.Type.systemBars(),
+            "System" to systemBars(),
             "Status" to WindowInsetsCompat.Type.statusBars()
         )
         findViewById<Spinner>(R.id.spn_insets_type).apply {
-            adapter = ArrayAdapter<String>(
+            adapter = ArrayAdapter(
                 context, android.R.layout.simple_spinner_dropdown_item,
                 types.keys.toTypedArray()
             )
@@ -209,4 +460,51 @@
             }
         }
     }
+
+    inner class Transition(private val view: View) {
+        private var mEndBottom = 0
+        private var mStartBottom = 0
+        private var mInsetsAnimation: WindowInsetsAnimationCompat? = null
+        private val debug = view.id == R.id.editRow
+
+        @SuppressLint("SetTextI18n")
+        fun onPrepare(animation: WindowInsetsAnimationCompat) {
+            if (animation.typeMask and ime() != 0) {
+                mInsetsAnimation = animation
+            }
+            mStartBottom = view.bottom
+            if (debug) {
+                values.clear()
+                info.text = "Prepare: start=$mStartBottom, end=$mEndBottom"
+            }
+        }
+
+        fun onProgress() {
+            mInsetsAnimation?.let {
+                view.y = (
+                    mStartBottom +
+                        (mEndBottom - mStartBottom) * it.interpolatedFraction - view.height
+                    )
+            }
+            if (debug) {
+                Log.d(TAG, view.y.toString())
+                values.add(view.y)
+                graph.invalidate()
+            }
+        }
+
+        @SuppressLint("SetTextI18n")
+        fun onStart() {
+            mEndBottom = view.bottom
+            if (debug) {
+                info.text = "${info.text}\nStart: start=$mStartBottom, end=$mEndBottom"
+            }
+        }
+
+        fun onFinish(animation: WindowInsetsAnimationCompat) {
+            if (mInsetsAnimation == animation) {
+                mInsetsAnimation = null
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/view/WindowInsetsPlayground.java b/samples/Support4Demos/src/main/java/com/example/android/supportv4/view/WindowInsetsPlayground.java
index 150e7a9..015d195 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/view/WindowInsetsPlayground.java
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/view/WindowInsetsPlayground.java
@@ -20,10 +20,12 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 
 import android.app.Activity;
+import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
 import android.text.Html;
 import android.view.View;
+import android.widget.Button;
 import android.widget.TextView;
 import android.widget.ToggleButton;
 
@@ -35,6 +37,7 @@
 import androidx.core.view.WindowInsetsCompat;
 
 import com.example.android.supportv4.R;
+import com.example.android.supportv4.graphics.DrawableCompatActivity;
 
 @SuppressWarnings("deprecation")
 public class WindowInsetsPlayground extends Activity {
@@ -69,6 +72,10 @@
             getWindow().setStatusBarColor(0x80000000);
             getWindow().setNavigationBarColor(0x80000000);
         }
+
+        Button newAct = findViewById(R.id.newAct);
+        newAct.setOnClickListener(
+                v -> startActivity(new Intent(this, DrawableCompatActivity.class)));
     }
 
     private void setRootWindowInsetsEnabled(boolean enabled) {
diff --git a/samples/Support4Demos/src/main/res/layout/activity_insets_controller.xml b/samples/Support4Demos/src/main/res/layout/activity_insets_controller.xml
index 4684142..0deaab4 100644
--- a/samples/Support4Demos/src/main/res/layout/activity_insets_controller.xml
+++ b/samples/Support4Demos/src/main/res/layout/activity_insets_controller.xml
@@ -21,6 +21,7 @@
     android:layout_height="match_parent"
     android:clipToPadding="false"
     android:id="@+id/root"
+    tools:ignore="HardcodedText"
     tools:targetApi="lollipop">
 
     <Toolbar
@@ -57,12 +58,25 @@
         android:layout_height="wrap_content"
         android:text="setDecorFitsSystemWindows" />
 
+    <CheckBox
+        android:id="@+id/isDecorView"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:checked="true"
+        android:text="On Decor View" />
+
     <TextView
         android:id="@+id/visibility"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="TextView" />
 
+    <TextView
+        android:id="@+id/info"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="TextView" />
+
     <LinearLayout
         android:id="@+id/buttonRow"
         android:layout_width="match_parent"
@@ -115,18 +129,12 @@
     </FrameLayout>
 
     <LinearLayout
+        android:id="@+id/editRow"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingStart="8dp"
+        android:orientation="horizontal"
         android:paddingEnd="8dp"
-        android:orientation="horizontal">
-
-        <EditText
-            android:id="@+id/editText"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_weight="0.9"
-            android:hint="Text message" />
+        android:paddingStart="8dp">
 
         <Button
             android:id="@+id/floating_action_button"
@@ -137,6 +145,13 @@
             android:drawableLeft="@drawable/ic_favorite"
             android:drawableStart="@drawable/ic_favorite" />
 
+        <EditText
+            android:id="@+id/editText"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="0.9"
+            android:hint="Text message" />
+
     </LinearLayout>
 </LinearLayout>
 
diff --git a/samples/Support4Demos/src/main/res/layout/activity_insets_playground.xml b/samples/Support4Demos/src/main/res/layout/activity_insets_playground.xml
index 3fdb522..d62dbbe 100644
--- a/samples/Support4Demos/src/main/res/layout/activity_insets_playground.xml
+++ b/samples/Support4Demos/src/main/res/layout/activity_insets_playground.xml
@@ -15,11 +15,13 @@
   -->
 
 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/insets_root"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fillViewport="true"
-    android:clipToPadding="false">
+    android:clipToPadding="false"
+    tools:ignore="HardcodedText">
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -52,6 +54,12 @@
                 android:textOn="View insets"
                 android:textOff="Root window insets" />
 
+            <Button
+                android:id="@+id/newAct"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="New Act" />
+
         </LinearLayout>
 
         <Space
diff --git a/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedFileTest.java b/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedFileTest.java
index c683a79..b08a7bf 100644
--- a/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedFileTest.java
+++ b/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedFileTest.java
@@ -32,7 +32,6 @@
 import com.google.crypto.tink.integration.android.AndroidKeysetManager;
 import com.google.crypto.tink.streamingaead.AesGcmHkdfStreamingKeyManager;
 import com.google.crypto.tink.streamingaead.StreamingAeadConfig;
-import com.google.crypto.tink.streamingaead.StreamingAeadFactory;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -50,7 +49,6 @@
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class EncryptedFileTest {
-
     private Context mContext;
     private MasterKey mMasterKey;
 
@@ -170,6 +168,7 @@
 
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void testWriteReadEncryptedFileWithAlias() throws Exception {
         final String fileContent = "Don't tell anyone...";
@@ -299,6 +298,7 @@
         Assert.assertTrue("Keyset should have existed.", containsKeyset);
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void tinkTest() throws Exception {
         final String fileContent = "Don't tell anyone...";
@@ -324,8 +324,8 @@
                 .withMasterKeyUri(KEYSTORE_PATH_URI + mMasterKey.getKeyAlias())
                 .build().getKeysetHandle();
 
-        StreamingAead streamingAead = StreamingAeadFactory.getPrimitive(
-                streadmingAeadKeysetHandle);
+        StreamingAead streamingAead = com.google.crypto.tink.streamingaead.StreamingAeadFactory
+                .getPrimitive(streadmingAeadKeysetHandle);
 
         FileInputStream fileInputStream = new FileInputStream(file);
         InputStream inputStream = streamingAead.newDecryptingStream(fileInputStream,
diff --git a/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedSharedPreferencesTest.java b/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedSharedPreferencesTest.java
index d16de8c..2b95874 100644
--- a/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedSharedPreferencesTest.java
+++ b/security/crypto/src/androidTest/java/androidx/security/crypto/EncryptedSharedPreferencesTest.java
@@ -347,6 +347,7 @@
                 sharedPreferences.getAll().size());
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void testWriteSharedPrefsTink() throws Exception {
         String tinkTestPrefs = "TinkTestPrefs";
@@ -406,4 +407,32 @@
                 testValue);
     }
 
+    @Test
+    public void testReentrantCallbackCalls() throws Exception {
+        SharedPreferences encryptedSharedPreferences = EncryptedSharedPreferences
+                .create(mContext,
+                        PREFS_FILE,
+                        mMasterKey,
+                        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+                        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
+
+        encryptedSharedPreferences.registerOnSharedPreferenceChangeListener(
+                new SharedPreferences.OnSharedPreferenceChangeListener() {
+                    @Override
+                    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+                            String key) {
+                        sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
+                    }
+                });
+
+        encryptedSharedPreferences.registerOnSharedPreferenceChangeListener(
+                (sharedPreferences, key) -> {
+                    // No-op
+                });
+
+        SharedPreferences.Editor editor = encryptedSharedPreferences.edit();
+        editor.putString("someKey", "someValue");
+        editor.apply();
+    }
+
 }
diff --git a/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeyTest.java b/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeyTest.java
index a271b22..616227f 100644
--- a/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeyTest.java
+++ b/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeyTest.java
@@ -170,6 +170,7 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
     @Test
     public void testUseOfSchemeAndParamsFails() throws GeneralSecurityException,
diff --git a/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeysTest.java b/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeysTest.java
index 8a3cd6e..7e2ed24 100644
--- a/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeysTest.java
+++ b/security/crypto/src/androidTest/java/androidx/security/crypto/MasterKeysTest.java
@@ -39,6 +39,7 @@
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 
+@SuppressWarnings("deprecation")
 @MediumTest
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
 @RunWith(AndroidJUnit4.class)
diff --git a/security/crypto/src/main/java/androidx/security/crypto/EncryptedSharedPreferences.java b/security/crypto/src/main/java/androidx/security/crypto/EncryptedSharedPreferences.java
index 6a231a6..44b327f 100644
--- a/security/crypto/src/main/java/androidx/security/crypto/EncryptedSharedPreferences.java
+++ b/security/crypto/src/main/java/androidx/security/crypto/EncryptedSharedPreferences.java
@@ -78,7 +78,7 @@
     private static final String NULL_VALUE = "__NULL__";
 
     final SharedPreferences mSharedPreferences;
-    final List<OnSharedPreferenceChangeListener> mListeners;
+    final CopyOnWriteArrayList<OnSharedPreferenceChangeListener> mListeners;
     final String mFileName;
     final String mMasterKeyAlias;
 
@@ -95,7 +95,7 @@
         mMasterKeyAlias = masterKeyAlias;
         mValueAead = aead;
         mKeyDeterministicAead = deterministicAead;
-        mListeners = new ArrayList<>();
+        mListeners = new CopyOnWriteArrayList<>();
     }
 
     /**
diff --git a/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/DynamicAuthTest.java b/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/DynamicAuthTest.java
index c986a44..7f6d550 100644
--- a/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/DynamicAuthTest.java
+++ b/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/DynamicAuthTest.java
@@ -29,7 +29,6 @@
 import androidx.security.identity.IdentityCredentialStore;
 import androidx.security.identity.NoAuthenticationKeyAvailableException;
 import androidx.security.identity.ResultData;
-import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 
@@ -61,9 +60,10 @@
 public class DynamicAuthTest {
     private static final String TAG = "DynamicAuthTest";
 
+    @SuppressWarnings("deprecation")
     @Test
     public void dynamicAuthTest() throws Exception {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         String credentialName = "test";
diff --git a/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/EphemeralKeyTest.java b/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/EphemeralKeyTest.java
index 4f0a62b..f9173ab 100644
--- a/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/EphemeralKeyTest.java
+++ b/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/EphemeralKeyTest.java
@@ -28,7 +28,6 @@
 import androidx.security.identity.IdentityCredential;
 import androidx.security.identity.IdentityCredentialException;
 import androidx.security.identity.IdentityCredentialStore;
-import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 
@@ -57,6 +56,7 @@
 import javax.crypto.spec.SecretKeySpec;
 
 // TODO: For better coverage, use different ECDH and HKDF implementations in test code.
+@SuppressWarnings("deprecation")
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class EphemeralKeyTest {
@@ -64,7 +64,7 @@
 
     @Test
     public void createEphemeralKey() throws IdentityCredentialException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         String credentialName = "ephemeralKeyTest";
diff --git a/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/ProvisioningTest.java b/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/ProvisioningTest.java
index 8ec51a5..363c0b8 100644
--- a/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/ProvisioningTest.java
+++ b/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/ProvisioningTest.java
@@ -40,7 +40,6 @@
 import androidx.security.identity.PersonalizationData;
 import androidx.security.identity.ResultData;
 import androidx.security.identity.WritableIdentityCredential;
-import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 
@@ -71,6 +70,7 @@
  *
  * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
  */
+@SuppressWarnings("deprecation")
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class ProvisioningTest {
@@ -357,7 +357,7 @@
 
     @Test
     public void alreadyPersonalized() throws IdentityCredentialException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         store.deleteCredentialByName("test");
@@ -374,7 +374,7 @@
 
     @Test
     public void nonExistent() throws IdentityCredentialException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         store.deleteCredentialByName("test");
@@ -385,7 +385,7 @@
 
     @Test
     public void defaultStoreSupportsAnyDocumentType() throws IdentityCredentialException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         String[] supportedDocTypes = store.getSupportedDocTypes();
@@ -395,7 +395,7 @@
     @Test
     public void deleteCredential()
             throws IdentityCredentialException, CborException, CertificateEncodingException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         store.deleteCredentialByName("test");
@@ -430,7 +430,7 @@
 
     @Test
     public void testProvisionAndRetrieve() throws IdentityCredentialException, CborException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         store.deleteCredentialByName("test");
@@ -520,7 +520,7 @@
     @Test
     public void testProvisionAndRetrieveMultipleTimes() throws IdentityCredentialException,
             InvalidKeyException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         // This checks we can do multiple getEntries() calls
@@ -577,7 +577,7 @@
 
     @Test
     public void testProvisionAndRetrieveWithFiltering() throws IdentityCredentialException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         store.deleteCredentialByName("test");
@@ -627,7 +627,7 @@
 
     @Test
     public void testProvisionAndRetrieveElementWithNoACP() throws IdentityCredentialException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         store.deleteCredentialByName("test");
@@ -665,7 +665,7 @@
 
     @Test
     public void testProvisionAndRetrieveWithEntryNotInRequest() throws IdentityCredentialException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         store.deleteCredentialByName("test");
@@ -718,7 +718,7 @@
 
     @Test
     public void nonExistentEntries() throws IdentityCredentialException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         store.deleteCredentialByName("test");
@@ -760,7 +760,7 @@
 
     @Test
     public void multipleNamespaces() throws IdentityCredentialException, CborException {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         store.deleteCredentialByName("test");
diff --git a/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/ReaderAuthTest.java b/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/ReaderAuthTest.java
index aba2975..f2446c7 100644
--- a/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/ReaderAuthTest.java
+++ b/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/ReaderAuthTest.java
@@ -38,7 +38,6 @@
 import androidx.security.identity.PersonalizationData;
 import androidx.security.identity.ResultData;
 import androidx.security.identity.WritableIdentityCredential;
-import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 
@@ -84,6 +83,7 @@
         return kpg.generateKeyPair();
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void readerAuth()
             throws IdentityCredentialException, CborException, InvalidAlgorithmParameterException,
@@ -158,7 +158,7 @@
         certChainForBwithC.add(certC);
 
         // Provision the credential.
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
         IdentityCredentialStore store = Util.getIdentityCredentialStore(appContext);
 
         String credentialName = "readerAuthTestCredential";
diff --git a/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/X509CertificateSigningTest.java b/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/X509CertificateSigningTest.java
index 336e84a..53cbedf 100644
--- a/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/X509CertificateSigningTest.java
+++ b/security/identity-credential/src/androidTest/java/androidx/security/identity/cts/X509CertificateSigningTest.java
@@ -28,7 +28,6 @@
 import android.util.AtomicFile;
 import android.util.Log;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 
@@ -49,6 +48,7 @@
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 
+@SuppressWarnings("deprecation")
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class X509CertificateSigningTest {
@@ -57,7 +57,7 @@
 
     @Test
     public void testSigning() {
-        Context appContext = InstrumentationRegistry.getTargetContext();
+        Context appContext = androidx.test.InstrumentationRegistry.getTargetContext();
 
         String keyToSignAlias = "testKeyToSign";
         String keyToSignWithAlias = "testKeyToSignWith";
diff --git a/settings.gradle b/settings.gradle
index 661f378..d398e3e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -413,6 +413,7 @@
 includeProject(":recyclerview:recyclerview-selection", "recyclerview/recyclerview-selection", [BuildType.MAIN])
 includeProject(":remotecallback:remotecallback", "remotecallback/remotecallback", [BuildType.MAIN])
 includeProject(":remotecallback:remotecallback-processor", "remotecallback/processor", [BuildType.MAIN])
+includeProject(":resourceinspection:resourceinspection-processor", "resourceinspection/resourceinspection-processor", [BuildType.MAIN])
 includeProject(":room:integration-tests:room-incremental-annotation-processing", "room/integration-tests/incremental-annotation-processing", [BuildType.MAIN])
 includeProject(":room:integration-tests:room-testapp", "room/integration-tests/testapp", [BuildType.MAIN])
 includeProject(":room:integration-tests:room-testapp-autovalue", "room/integration-tests/autovaluetestapp", [BuildType.MAIN])
@@ -493,6 +494,7 @@
 includeProject(":wear:wear-complications-provider-samples", "wear/wear-complications-provider/samples", [BuildType.MAIN])
 includeProject(":wear:wear-input", "wear/wear-input", [BuildType.MAIN])
 includeProject(":wear:wear-input-testing", "wear/wear-input-testing", [BuildType.MAIN])
+includeProject(":wear:wear-remote-interactions", "wear/wear-remote-interactions", [BuildType.MAIN])
 includeProject(":wear:wear-tiles", "wear/wear-tiles", [BuildType.MAIN])
 includeProject(":wear:wear-tiles-data", "wear/wear-tiles-data", [BuildType.MAIN])
 includeProject(":wear:wear-watchface", "wear/wear-watchface", [BuildType.MAIN])
@@ -564,6 +566,7 @@
 includeProject(":internal-testutils-navigation", "testutils/testutils-navigation", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
 includeProject(":internal-testutils-paging", "testutils/testutils-paging", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":internal-testutils-gradle-plugin", "testutils/testutils-gradle-plugin", [BuildType.MAIN, BuildType.FLAN])
+includeProject(":internal-testutils-mockito", "testutils/testutils-mockito", [BuildType.MAIN])
 
 /////////////////////////////
 //
diff --git a/sharetarget/sharetarget/src/androidTest/java/androidx/sharetarget/ChooserTargetServiceCompatTest.java b/sharetarget/sharetarget/src/androidTest/java/androidx/sharetarget/ChooserTargetServiceCompatTest.java
index e4d5212..068467b 100644
--- a/sharetarget/sharetarget/src/androidTest/java/androidx/sharetarget/ChooserTargetServiceCompatTest.java
+++ b/sharetarget/sharetarget/src/androidTest/java/androidx/sharetarget/ChooserTargetServiceCompatTest.java
@@ -27,7 +27,6 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
-import android.service.chooser.ChooserTarget;
 
 import androidx.core.content.pm.ShortcutInfoCompat;
 import androidx.core.content.pm.ShortcutManagerCompat;
@@ -64,6 +63,7 @@
         when(mShortcutSaver.getShortcutIcon(any())).thenReturn(mTestIcon);
     }
 
+    @SuppressWarnings({"deprecation", "unchecked"})
     @Test
     @SdkSuppress(minSdkVersion = 23)
     public void testConvertShortcutstoChooserTargets() {
@@ -87,7 +87,7 @@
 
         // Need to clone to keep the original order for testing.
         ArrayList<ShortcutHolder> clonedList = (ArrayList<ShortcutHolder>) testShortcuts.clone();
-        List<ChooserTarget> chooserTargets =
+        List<android.service.chooser.ChooserTarget> chooserTargets =
                 ChooserTargetServiceCompat.convertShortcutsToChooserTargets(
                         mShortcutSaver, clonedList);
 
@@ -96,7 +96,7 @@
 
         assertEquals(testShortcuts.size(), chooserTargets.size());
         for (int i = 0; i < chooserTargets.size(); i++) {
-            ChooserTarget ct = chooserTargets.get(i);
+            android.service.chooser.ChooserTarget ct = chooserTargets.get(i);
             ShortcutInfoCompat si = testShortcuts.get(expectedOrder[i]).getShortcut();
             ComponentName cn = testShortcuts.get(expectedOrder[i]).getTargetClass();
 
diff --git a/slices/benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java b/slices/benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java
index de9e87a..5e843ea 100644
--- a/slices/benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java
+++ b/slices/benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java
@@ -21,8 +21,7 @@
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 
-import static junit.framework.Assert.assertEquals;
-
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import android.app.PendingIntent;
diff --git a/slices/test/lint-baseline.xml b/slices/test/lint-baseline.xml
index 7b41818..4dbd5e7 100644
--- a/slices/test/lint-baseline.xml
+++ b/slices/test/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.2.0-alpha15" client="gradle" variant="debug" version="4.2.0-alpha15">
+<issues format="5" by="lint 4.2.0-alpha16" client="gradle" variant="debug" version="4.2.0-alpha16">
 
     <issue
         id="ObsoleteLintCustomCheck"
         message="Lint found an issue registry (`androidx.annotation.experimental.lint.ExperimentalIssueRegistry`) which did not specify the Lint API version it was compiled with.&#xA;&#xA;**This means that the lint checks are likely not compatible.**&#xA;&#xA;If you are the author of this lint check, make your lint `IssueRegistry` class contain&#xA;  override val api: Int = com.android.tools.lint.detector.api.CURRENT_API&#xA;or from Java,&#xA;  @Override public int getApi() { return com.android.tools.lint.detector.api.ApiKt.CURRENT_API; }&#xA;&#xA;If you are just using lint checks from a third party library you have no control over, you can disable these lint checks (if they misbehave) like this:&#xA;&#xA;    android {&#xA;        lintOptions {&#xA;            disable &quot;UnsafeExperimentalUsageError&quot;,&#xA;                    &quot;UnsafeExperimentalUsageWarning&quot;&#xA;        }&#xA;    }&#xA;">
         <location
-            file="../../../../../.gradle/caches/transforms-2/files-2.1/cb45a5ae098b2615e2539f4e9a0ea73e/annotation-experimental-1.0.0/jars/lint.jar"/>
+            file="../../../../../.gradle/caches/transforms-2/files-2.1/597bdf06b2c6543399d6dc2389a6cda6/annotation-experimental-1.0.0/jars/lint.jar"/>
     </issue>
 
     <issue
@@ -59,7 +59,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="277"
+            line="283"
             column="26"/>
     </issue>
 
@@ -70,7 +70,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="321"
+            line="327"
             column="26"/>
     </issue>
 
@@ -81,7 +81,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="364"
+            line="370"
             column="34"/>
     </issue>
 
@@ -92,7 +92,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="475"
+            line="543"
             column="26"/>
     </issue>
 
@@ -103,7 +103,7 @@
         errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="496"
+            line="564"
             column="25"/>
     </issue>
 
@@ -114,7 +114,7 @@
         errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="510"
+            line="578"
             column="25"/>
     </issue>
 
@@ -125,7 +125,7 @@
         errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="556"
+            line="624"
             column="25"/>
     </issue>
 
@@ -136,7 +136,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="603"
+            line="671"
             column="16"/>
     </issue>
 
@@ -147,11 +147,11 @@
         errorLine2="                                                                        ^">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="656"
+            line="724"
             column="73"/>
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="656"
+            line="724"
             column="73"/>
     </issue>
 
@@ -162,7 +162,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="666"
+            line="734"
             column="26"/>
     </issue>
 
@@ -173,7 +173,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="738"
+            line="806"
             column="26"/>
     </issue>
 
@@ -184,7 +184,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="766"
+            line="834"
             column="16"/>
     </issue>
 
@@ -195,7 +195,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="807"
+            line="875"
             column="16"/>
     </issue>
 
@@ -206,7 +206,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="894"
+            line="962"
             column="26"/>
     </issue>
 
@@ -217,7 +217,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="1103"
+            line="1171"
             column="16"/>
     </issue>
 
@@ -228,7 +228,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="1131"
+            line="1199"
             column="26"/>
     </issue>
 
@@ -239,7 +239,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="1193"
+            line="1261"
             column="26"/>
     </issue>
 
@@ -250,7 +250,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="1228"
+            line="1296"
             column="26"/>
     </issue>
 
@@ -261,7 +261,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="1316"
+            line="1384"
             column="16"/>
     </issue>
 
@@ -272,7 +272,7 @@
         errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="1406"
+            line="1474"
             column="19"/>
     </issue>
 
@@ -283,7 +283,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="1440"
+            line="1508"
             column="16"/>
     </issue>
 
@@ -396,6 +396,13 @@
 
     <issue
         id="IconLocation"
+        message="Found bitmap drawable `res/drawable/landscape.jpg` in densityless folder">
+        <location
+            file="src/main/res/drawable/landscape.jpg"/>
+    </issue>
+
+    <issue
+        id="IconLocation"
         message="Found bitmap drawable `res/drawable/mady.jpg` in densityless folder">
         <location
             file="src/main/res/drawable/mady.jpg"/>
@@ -445,6 +452,13 @@
 
     <issue
         id="IconLocation"
+        message="Found bitmap drawable `res/drawable/portrait.jpg` in densityless folder">
+        <location
+            file="src/main/res/drawable/portrait.jpg"/>
+    </issue>
+
+    <issue
+        id="IconLocation"
         message="Found bitmap drawable `res/drawable/reservation.png` in densityless folder">
         <location
             file="src/main/res/drawable/reservation.png"/>
@@ -513,7 +527,7 @@
         errorLine2="                  ~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="153"
+            line="155"
             column="19"/>
     </issue>
 
@@ -524,7 +538,7 @@
         errorLine2="                             ~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="153"
+            line="155"
             column="30"/>
     </issue>
 
@@ -535,7 +549,7 @@
         errorLine2="                                          ~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="153"
+            line="155"
             column="43"/>
     </issue>
 
@@ -546,7 +560,7 @@
         errorLine2="           ~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="173"
+            line="175"
             column="12"/>
     </issue>
 
@@ -557,7 +571,7 @@
         errorLine2="                  ~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/slice/test/SampleSliceProvider.java"
-            line="720"
+            line="788"
             column="19"/>
     </issue>
 
diff --git a/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java b/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java
index 9f87c3e..63b8c7a 100644
--- a/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java
+++ b/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java
@@ -111,6 +111,8 @@
             "contact4",
             "picker",
             "gallery",
+            "galleryimagesizemovie",
+            "galleryimagesizethumbnail",
             "galleryoverlay",
             "indeterminaterange",
             "indeterminaterange2",
@@ -218,6 +220,10 @@
                 return createPicker(sliceUri);
             case "/gallery":
                 return createGallery(sliceUri);
+            case "/galleryimagesizemovie":
+                return createGalleryImageSizeMovie(sliceUri);
+            case "/galleryimagesizethumbnail":
+                return createGalleryImageSizeThumbnails(sliceUri);
             case "/galleryoverlay":
                 return createGalleryOverlay(sliceUri);
             case "/weather":
@@ -415,6 +421,68 @@
         return lb.addGridRow(grb).build();
     }
 
+    private Slice createGalleryImageSizeMovie(Uri sliceUri) {
+        SliceAction primaryAction = SliceAction.create(
+                getBroadcastIntent(ACTION_TOAST, "open movie list"),
+                IconCompat.createWithResource(getContext(), R.drawable.slices_1),
+                LARGE_IMAGE,
+                "Open movie list");
+        ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY)
+                .setAccentColor(0xff4285F4);
+        lb.addRow(new RowBuilder()
+                .setTitle("These movies near you")
+                .setSubtitle("Top rated Movies")
+                .setPrimaryAction(primaryAction))
+                .addAction(SliceAction.create(
+                        getBroadcastIntent(ACTION_TOAST, ""),
+                        IconCompat.createWithResource(getContext(), R.drawable.ic_cast), ICON_IMAGE,
+                        "Share movie list"));
+        GridRowBuilder grb = new GridRowBuilder();
+        grb.addCell(new CellBuilder().addImage(IconCompat.createWithResource(getContext(),
+                R.drawable.portrait), RAW_IMAGE_LARGE)
+                .addTitleText("MovieName the movie"));
+        grb.addCell(new CellBuilder().addImage(IconCompat.createWithResource(getContext(),
+                R.drawable.portrait), RAW_IMAGE_LARGE)
+                .addTitleText("MovieName the movie2"));
+        grb.addCell(new CellBuilder().addImage(IconCompat.createWithResource(getContext(),
+                R.drawable.portrait), RAW_IMAGE_LARGE)
+                .addTitleText("Amazing movie"));
+        grb.addCell(new CellBuilder().addImage(IconCompat.createWithResource(getContext(),
+                R.drawable.portrait), RAW_IMAGE_LARGE)
+                .addTitleText("Cool movie").addText("The Sequel"));
+        return lb.addGridRow(grb).build();
+    }
+
+
+    private Slice createGalleryImageSizeThumbnails(Uri sliceUri) {
+        SliceAction primaryAction = SliceAction.create(
+                getBroadcastIntent(ACTION_TOAST, "open video list"),
+                IconCompat.createWithResource(getContext(), R.drawable.slices_1),
+                LARGE_IMAGE,
+                "Open video list");
+        ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY)
+                .setAccentColor(0xff4285F4);
+        lb.addRow(new RowBuilder()
+                .setTitle("Recommended videos")
+                .setSubtitle("Top rated Videos")
+                .setPrimaryAction(primaryAction))
+                .addAction(SliceAction.create(
+                        getBroadcastIntent(ACTION_TOAST, ""),
+                        IconCompat.createWithResource(getContext(), R.drawable.ic_cast), ICON_IMAGE,
+                        "Share videos"));
+        GridRowBuilder grb = new GridRowBuilder();
+        grb.addCell(new CellBuilder().addImage(IconCompat.createWithResource(getContext(),
+                R.drawable.landscape), RAW_IMAGE_LARGE)
+                .addTitleText("You won't believe this movie"));
+        grb.addCell(new CellBuilder().addImage(IconCompat.createWithResource(getContext(),
+                R.drawable.landscape), RAW_IMAGE_LARGE)
+                .addTitleText("Amazing movie"));
+        grb.addCell(new CellBuilder().addImage(IconCompat.createWithResource(getContext(),
+                R.drawable.landscape), RAW_IMAGE_LARGE)
+                .addTitleText("kittens wow"));
+        return lb.addGridRow(grb).build();
+    }
+
     private Slice createGalleryOverlay(Uri sliceUri) {
         SliceAction primaryAction = SliceAction.create(
                 getBroadcastIntent(ACTION_TOAST, "open photo album"),
diff --git a/slices/test/src/main/res/drawable/landscape.jpg b/slices/test/src/main/res/drawable/landscape.jpg
new file mode 100644
index 0000000..6941f05
--- /dev/null
+++ b/slices/test/src/main/res/drawable/landscape.jpg
Binary files differ
diff --git a/slices/test/src/main/res/drawable/portrait.jpg b/slices/test/src/main/res/drawable/portrait.jpg
new file mode 100644
index 0000000..38f5b71f
--- /dev/null
+++ b/slices/test/src/main/res/drawable/portrait.jpg
Binary files differ
diff --git a/slices/view/api/current.txt b/slices/view/api/current.txt
index 47aa153..222c2f4 100644
--- a/slices/view/api/current.txt
+++ b/slices/view/api/current.txt
@@ -146,6 +146,7 @@
     ctor public SliceView(android.content.Context!, android.util.AttributeSet?);
     ctor public SliceView(android.content.Context!, android.util.AttributeSet?, int);
     ctor @RequiresApi(21) public SliceView(android.content.Context!, android.util.AttributeSet!, int, int);
+    method protected void configureViewPolicy(int);
     method public int getHiddenItemCount();
     method public int getMode();
     method public androidx.slice.Slice? getSlice();
diff --git a/slices/view/api/public_plus_experimental_current.txt b/slices/view/api/public_plus_experimental_current.txt
index 47aa153..222c2f4 100644
--- a/slices/view/api/public_plus_experimental_current.txt
+++ b/slices/view/api/public_plus_experimental_current.txt
@@ -146,6 +146,7 @@
     ctor public SliceView(android.content.Context!, android.util.AttributeSet?);
     ctor public SliceView(android.content.Context!, android.util.AttributeSet?, int);
     ctor @RequiresApi(21) public SliceView(android.content.Context!, android.util.AttributeSet!, int, int);
+    method protected void configureViewPolicy(int);
     method public int getHiddenItemCount();
     method public int getMode();
     method public androidx.slice.Slice? getSlice();
diff --git a/slices/view/api/restricted_current.txt b/slices/view/api/restricted_current.txt
index d40654b..09f2209 100644
--- a/slices/view/api/restricted_current.txt
+++ b/slices/view/api/restricted_current.txt
@@ -122,6 +122,7 @@
     method public int getAccentColor();
     method public CharSequence? getContentDescription();
     method public androidx.slice.SliceItem? getContentIntent();
+    method public android.graphics.Point getFirstImageSize(android.content.Context);
     method public java.util.ArrayList<androidx.slice.widget.GridContent.CellContent!> getGridContent();
     method public int getHeight(androidx.slice.widget.SliceStyle!, androidx.slice.widget.SliceViewPolicy!);
     method public boolean getIsLastIndex();
@@ -143,10 +144,11 @@
     ctor public GridContent.CellContent(androidx.slice.SliceItem!);
     method public java.util.ArrayList<androidx.slice.SliceItem!> getCellItems();
     method public CharSequence? getContentDescription();
-    method public androidx.slice.SliceItem! getContentIntent();
+    method public androidx.slice.SliceItem? getContentIntent();
+    method public androidx.core.graphics.drawable.IconCompat? getImageIcon();
     method public int getImageMode();
     method public androidx.slice.SliceItem? getOverlayItem();
-    method public androidx.slice.SliceItem! getPicker();
+    method public androidx.slice.SliceItem? getPicker();
     method public int getTextCount();
     method public androidx.slice.SliceItem? getTitleItem();
     method public boolean hasImage();
@@ -246,6 +248,7 @@
     ctor public SliceView(android.content.Context!, android.util.AttributeSet?);
     ctor public SliceView(android.content.Context!, android.util.AttributeSet?, int);
     ctor @RequiresApi(21) public SliceView(android.content.Context!, android.util.AttributeSet!, int, int);
+    method protected void configureViewPolicy(int);
     method public int getHiddenItemCount();
     method public int getMode();
     method public androidx.slice.Slice? getSlice();
diff --git a/slices/view/lint-baseline.xml b/slices/view/lint-baseline.xml
index 1199cdc7..999809f 100644
--- a/slices/view/lint-baseline.xml
+++ b/slices/view/lint-baseline.xml
@@ -1982,501 +1982,6 @@
     </issue>
 
     <issue
-        id="UnusedQuantity"
-        message="For language &quot;cs&quot; (Czech) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-cs/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;in&quot; (Indonesian) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-in/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ja&quot; (Japanese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ja/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;km&quot; (Khmer) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-km/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ko&quot; (Korean) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ko/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lo&quot; (Lao) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lo/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lt&quot; (Lithuanian) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lt/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ms&quot; (Malay) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ms/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;my&quot; (Burmese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-my/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;sk&quot; (Slovak) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-sk/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;th&quot; (Thai) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-th/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;vi&quot; (Vietnamese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-vi/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rCN/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rHK/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_min&quot; formatted=&quot;false&quot; msgid=&quot;6996334305156847955&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rTW/strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;in&quot; (Indonesian) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-in/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ja&quot; (Japanese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ja/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;km&quot; (Khmer) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-km/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ko&quot; (Korean) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ko/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lo&quot; (Lao) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lo/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ms&quot; (Malay) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ms/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;my&quot; (Burmese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-my/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;th&quot; (Thai) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-th/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;vi&quot; (Vietnamese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-vi/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rCN/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rHK/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rTW/strings.xml"
-            line="28"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;cs&quot; (Czech) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-cs/strings.xml"
-            line="30"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lt&quot; (Lithuanian) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lt/strings.xml"
-            line="30"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;sk&quot; (Slovak) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_years&quot; formatted=&quot;false&quot; msgid=&quot;6212691832333991589&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-sk/strings.xml"
-            line="30"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;in&quot; (Indonesian) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-in/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ja&quot; (Japanese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ja/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;km&quot; (Khmer) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-km/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ko&quot; (Korean) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ko/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lo&quot; (Lao) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lo/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ms&quot; (Malay) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ms/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;my&quot; (Burmese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-my/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;th&quot; (Thai) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-th/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;vi&quot; (Vietnamese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-vi/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rCN/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rHK/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rTW/strings.xml"
-            line="32"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;cs&quot; (Czech) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-cs/strings.xml"
-            line="36"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lt&quot; (Lithuanian) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lt/strings.xml"
-            line="36"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;sk&quot; (Slovak) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;abc_slice_duration_days&quot; formatted=&quot;false&quot; msgid=&quot;6241698511167107334&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-sk/strings.xml"
-            line="36"
-            column="5"/>
-    </issue>
-
-    <issue
         id="ObsoleteLayoutParam"
         message="Invalid layout param in a `LinearLayout`: `layout_alignStart`"
         errorLine1="            android:layout_alignStart=&quot;@android:id/title&quot;"
diff --git a/slices/view/src/androidTest/java/androidx/slice/widget/CachedSliceLiveDataTest.java b/slices/view/src/androidTest/java/androidx/slice/widget/CachedSliceLiveDataTest.java
index 9c35fc2..e7c7ac7 100644
--- a/slices/view/src/androidTest/java/androidx/slice/widget/CachedSliceLiveDataTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/widget/CachedSliceLiveDataTest.java
@@ -57,6 +57,7 @@
 import java.io.InputStream;
 import java.util.concurrent.CountDownLatch;
 
+@SuppressWarnings("unchecked")
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @SdkSuppress(minSdkVersion = 19)
diff --git a/slices/view/src/androidTest/java/androidx/slice/widget/SliceLiveDataTest.java b/slices/view/src/androidTest/java/androidx/slice/widget/SliceLiveDataTest.java
index 88b0eb6..6e34c93 100644
--- a/slices/view/src/androidTest/java/androidx/slice/widget/SliceLiveDataTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/widget/SliceLiveDataTest.java
@@ -60,6 +60,7 @@
 import java.io.InputStream;
 import java.util.concurrent.CountDownLatch;
 
+@SuppressWarnings("unchecked")
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 @SdkSuppress(minSdkVersion = 19)
diff --git a/slices/view/src/androidTest/java/androidx/slice/widget/SliceStyleTest.java b/slices/view/src/androidTest/java/androidx/slice/widget/SliceStyleTest.java
index 3f98f01..5ae7438 100644
--- a/slices/view/src/androidTest/java/androidx/slice/widget/SliceStyleTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/widget/SliceStyleTest.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
-import android.util.TypedValue;
 import android.util.Xml;
 
 import androidx.slice.SliceItem;
@@ -48,6 +47,7 @@
 
     @Before
     public void setup() {
+        mContext.setTheme(R.style.AppTheme);
         // Empty XML file to initialize empty AttributeSet.
         XmlPullParser parser = mContext.getResources().getXml(R.xml.slice_style_test);
         AttributeSet attributes = Xml.asAttributeSet(parser);
@@ -132,9 +132,7 @@
     }
 
     private int getThemeColor(int colorRes) {
-        TypedValue typedValue = new TypedValue();
-        mContext.getTheme().resolveAttribute(colorRes, typedValue, true);
-        TypedArray arr = mContext.obtainStyledAttributes(typedValue.data, new int[] {colorRes});
+        TypedArray arr = mContext.getTheme().obtainStyledAttributes(new int[] {colorRes});
         int themeColor = arr.getColor(0, -1);
         assertNotSame(-1, themeColor);
         return themeColor;
diff --git a/slices/view/src/androidTest/java/androidx/slice/widget/SliceViewTest.java b/slices/view/src/androidTest/java/androidx/slice/widget/SliceViewTest.java
index f9dcce2..e05b4e0 100644
--- a/slices/view/src/androidTest/java/androidx/slice/widget/SliceViewTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/widget/SliceViewTest.java
@@ -74,6 +74,7 @@
     @Before
     @UiThreadTest
     public void setup() {
+        mContext.setTheme(R.style.AppTheme);
         mSliceView = new SliceView(mContext);
         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
     }
@@ -583,9 +584,9 @@
 
         // Expected colors for checked items.
         int checkedTitleColor = mContext.getResources().getColor(
-                R.color.checkedItemTitleColor, mContext.getTheme());
+                R.color.checkedItemTitleColor);
         int checkedSubtitleColor = mContext.getResources().getColor(
-                R.color.checkedItemSubtitleColor, mContext.getTheme());
+                R.color.checkedItemSubtitleColor);
 
         // Expected colors for unchecked items (the default theme colors).
         int themeTitleColor = getThemeColor(android.R.attr.textColorPrimary);
@@ -697,10 +698,7 @@
     }
 
     private int getThemeColor(int colorRes) {
-        TypedValue typedValue = new TypedValue();
-        mContext.getTheme().resolveAttribute(colorRes, typedValue, true);
-        TypedArray arr = mContext.obtainStyledAttributes(typedValue.data, new int[]{
-                colorRes});
+        TypedArray arr = mContext.getTheme().obtainStyledAttributes(new int[] {colorRes});
         int themeColor = arr.getColor(0, -1);
         assertNotSame(-1, themeColor);
         return themeColor;
diff --git a/slices/view/src/main/java/androidx/slice/widget/GridContent.java b/slices/view/src/main/java/androidx/slice/widget/GridContent.java
index 1fb968d..7aa346a 100644
--- a/slices/view/src/main/java/androidx/slice/widget/GridContent.java
+++ b/slices/view/src/main/java/androidx/slice/widget/GridContent.java
@@ -35,10 +35,15 @@
 import static androidx.slice.core.SliceHints.SUBTYPE_TIME_PICKER;
 import static androidx.slice.core.SliceHints.UNKNOWN_IMAGE;
 
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.SliceItem;
 import androidx.slice.SliceUtils;
 import androidx.slice.core.SliceActionImpl;
@@ -60,9 +65,10 @@
     private final ArrayList<CellContent> mGridContent = new ArrayList<>();
     private SliceItem mSeeMoreItem;
     private int mMaxCellLineCount;
-    private boolean mHasImage;
     private int mLargestImageMode = UNKNOWN_IMAGE;
     private boolean mIsLastIndex;
+    private IconCompat mFirstImage = null;
+    private Point mFirstImageSize = null;
 
     private SliceItem mTitleItem;
 
@@ -113,7 +119,9 @@
                 mAllImages = false;
             }
             mMaxCellLineCount = Math.max(mMaxCellLineCount, cc.getTextCount());
-            mHasImage |= cc.hasImage();
+            if (mFirstImage == null && cc.hasImage()) {
+                mFirstImage = cc.getImageIcon();
+            }
             mLargestImageMode = mLargestImageMode == UNKNOWN_IMAGE
                     ? cc.getImageMode()
                     : Math.max(mLargestImageMode, cc.getImageMode());
@@ -180,6 +188,21 @@
     }
 
     /**
+     * @return the first image dimensions in this row, if there are images.
+     */
+    @NonNull
+    public Point getFirstImageSize(@NonNull Context context) {
+        if (mFirstImage == null) {
+            return new Point(-1, -1);
+        }
+        if (mFirstImageSize == null) {
+            Drawable d = mFirstImage.loadDrawable(context);
+            mFirstImageSize = new Point(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+        }
+        return mFirstImageSize;
+    }
+
+    /**
      * Filters non-cell items out of the list of items and finds content description.
      */
     private List<SliceItem> filterAndProcessItems(List<SliceItem> items) {
@@ -212,7 +235,7 @@
      * @return whether this row contains an image.
      */
     public boolean hasImage() {
-        return mHasImage;
+        return mFirstImage != null;
     }
 
     /**
@@ -244,7 +267,7 @@
         private final ArrayList<SliceItem> mCellItems = new ArrayList<>();
         private SliceItem mContentDescr;
         private int mTextCount;
-        private boolean mHasImage;
+        private IconCompat mImage;
         private SliceItem mOverlayItem;
         private int mImageMode = -1;
         private SliceItem mTitleItem;
@@ -300,7 +323,7 @@
                     } else if (imageCount < 1 && FORMAT_IMAGE.equals(item.getFormat())) {
                         mImageMode = SliceUtils.parseImageMode(item);
                         imageCount++;
-                        mHasImage = true;
+                        mImage = item.getIcon();
                         mCellItems.add(item);
                     }
                 }
@@ -329,6 +352,7 @@
         /**
          * @return the action to activate when this cell is tapped.
          */
+        @Nullable
         public SliceItem getContentIntent() {
             return mContentIntent;
         }
@@ -336,6 +360,7 @@
         /**
          * @return the Picker to use when this cell is tapped.
          */
+        @Nullable
         public SliceItem getPicker() {
             return mPicker;
         }
@@ -386,7 +411,7 @@
          * @return whether this cell contains an image.
          */
         public boolean hasImage() {
-            return mHasImage;
+            return mImage != null;
         }
 
         /**
@@ -396,6 +421,14 @@
             return mImageMode;
         }
 
+        /**
+         * @return the IconCompat of the image.
+         */
+        @Nullable
+        public IconCompat getImageIcon() {
+            return mImage;
+        }
+
         @Nullable
         public CharSequence getContentDescription() {
             return mContentDescr != null ? mContentDescr.getText() : null;
diff --git a/slices/view/src/main/java/androidx/slice/widget/GridRowView.java b/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
index 3d27f6f..598cd88 100644
--- a/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
@@ -30,6 +30,7 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 import static androidx.slice.core.SliceHints.LARGE_IMAGE;
+import static androidx.slice.core.SliceHints.RAW_IMAGE_LARGE;
 import static androidx.slice.core.SliceHints.SUBTYPE_DATE_PICKER;
 import static androidx.slice.core.SliceHints.SUBTYPE_TIME_PICKER;
 import static androidx.slice.widget.EventInfo.ACTION_TYPE_DATE_PICK;
@@ -230,9 +231,17 @@
         }
         ArrayList<GridContent.CellContent> cells = mGridContent.getGridContent();
         if (cells.size() > 1) {
-            int desiredCellWidth = mGridContent.getLargestImageMode() == LARGE_IMAGE
-                    ? mLargeImageHeight
-                    : mSmallImageMinWidth;
+            int desiredCellWidth;
+            switch (mGridContent.getLargestImageMode()) {
+                case LARGE_IMAGE:
+                    desiredCellWidth = mLargeImageHeight;
+                    break;
+                case RAW_IMAGE_LARGE:
+                    desiredCellWidth = mGridContent.getFirstImageSize(getContext()).x;
+                    break;
+                default:
+                    desiredCellWidth = mSmallImageMinWidth;
+            }
             return getWidth() / (desiredCellWidth + mGutter);
         } else {
             return 1;
@@ -262,7 +271,8 @@
             mViewContainer.setContentDescription(contentDescr);
         }
         ArrayList<GridContent.CellContent> cells = mGridContent.getGridContent();
-        if (mGridContent.getLargestImageMode() == LARGE_IMAGE) {
+        if (mGridContent.getLargestImageMode() == LARGE_IMAGE
+                || mGridContent.getLargestImageMode() == RAW_IMAGE_LARGE) {
             mViewContainer.setGravity(Gravity.TOP);
         } else {
             mViewContainer.setGravity(Gravity.CENTER_VERTICAL);
@@ -489,7 +499,8 @@
         LinearLayout.LayoutParams lp;
         if (item.hasHint(SliceHints.HINT_RAW)) {
             iv.setScaleType(ScaleType.CENTER_INSIDE);
-            lp = new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
+            lp = new LinearLayout.LayoutParams(mGridContent.getFirstImageSize(getContext()).x,
+                    mGridContent.getFirstImageSize(getContext()).y);
         } else if (item.hasHint(HINT_LARGE)) {
             iv.setScaleType(hasRoundedImage ? ScaleType.FIT_XY : ScaleType.CENTER_CROP);
             int height = isSingle ? MATCH_PARENT : mLargeImageHeight;
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceStyle.java b/slices/view/src/main/java/androidx/slice/widget/SliceStyle.java
index 67e3d90..60917e0 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceStyle.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceStyle.java
@@ -17,6 +17,7 @@
 package androidx.slice.widget;
 
 import static androidx.slice.core.SliceHints.ICON_IMAGE;
+import static androidx.slice.core.SliceHints.RAW_IMAGE_LARGE;
 import static androidx.slice.core.SliceHints.UNKNOWN_IMAGE;
 import static androidx.slice.widget.SliceView.MODE_LARGE;
 import static androidx.slice.widget.SliceView.MODE_SMALL;
@@ -74,6 +75,7 @@
     private final int mGridBigPicMaxHeight;
     private final int mGridAllImagesHeight;
     private final int mGridImageTextHeight;
+    private final int mGridRawImageTextHeight;
     private final int mGridMaxHeight;
     private final int mGridMinHeight;
 
@@ -158,7 +160,7 @@
 
             mContext = context;
 
-            mImageCornerRadius = (float) a.getDimension(R.styleable.SliceView_imageCornerRadius, 0);
+            mImageCornerRadius = a.getDimension(R.styleable.SliceView_imageCornerRadius, 0);
         } finally {
             a.recycle();
         }
@@ -179,6 +181,8 @@
         mGridBigPicMaxHeight = r.getDimensionPixelSize(R.dimen.abc_slice_big_pic_max_height);
         mGridAllImagesHeight = r.getDimensionPixelSize(R.dimen.abc_slice_grid_image_only_height);
         mGridImageTextHeight = r.getDimensionPixelSize(R.dimen.abc_slice_grid_image_text_height);
+        mGridRawImageTextHeight = r.getDimensionPixelSize(
+                R.dimen.abc_slice_grid_raw_image_text_offset);
         mGridMinHeight = r.getDimensionPixelSize(R.dimen.abc_slice_grid_min_height);
         mGridMaxHeight = r.getDimensionPixelSize(R.dimen.abc_slice_grid_max_height);
 
@@ -362,18 +366,30 @@
         int largestImageMode = grid.getLargestImageMode();
         int height;
         if (grid.isAllImages()) {
-            height = grid.getGridContent().size() == 1
-                    ? isSmall ? mGridBigPicMinHeight : mGridBigPicMaxHeight
-                    : largestImageMode == ICON_IMAGE ? mGridMinHeight
-                            : mGridAllImagesHeight;
+            height = (grid.getGridContent().size() == 1)
+                    ? (isSmall
+                    ? mGridBigPicMinHeight
+                    : mGridBigPicMaxHeight)
+                    : (largestImageMode == ICON_IMAGE
+                            ? mGridMinHeight
+                            : (largestImageMode == RAW_IMAGE_LARGE
+                                    ? grid.getFirstImageSize(mContext).y
+                                    : mGridAllImagesHeight));
         } else {
             boolean twoLines = grid.getMaxCellLineCount() > 1;
             boolean hasImage = grid.hasImage();
             boolean iconImagesOrNone = largestImageMode == ICON_IMAGE
                     || largestImageMode == UNKNOWN_IMAGE;
-            height = (twoLines && !isSmall)
-                    ? hasImage ? mGridMaxHeight : mGridMinHeight
-                    : iconImagesOrNone ? mGridMinHeight : mGridImageTextHeight;
+            height = largestImageMode == RAW_IMAGE_LARGE
+                    ? (grid.getFirstImageSize(mContext).y
+                        + (twoLines ? 2 : 1) * mGridRawImageTextHeight)
+                    : (twoLines && !isSmall)
+                            ? (hasImage
+                            ? mGridMaxHeight
+                            : mGridMinHeight)
+                            : (iconImagesOrNone
+                                    ? mGridMinHeight
+                                    : mGridImageTextHeight);
         }
         int topPadding = grid.isAllImages() && grid.getRowIndex() == 0
                 ? mGridTopPadding : 0;
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceView.java b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
index 1bbd4c7..bc77fc1 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
@@ -345,7 +345,10 @@
         }
     }
 
-    private void configureViewPolicy(int maxHeight) {
+    /**
+     * Sets the maximum height for a slice through the view policy.
+     */
+    protected void configureViewPolicy(int maxHeight) {
         if (mListContent != null && mListContent.isValid() && getMode() != MODE_SHORTCUT) {
             if (maxHeight > 0 && maxHeight < mSliceStyle.getRowMaxHeight()) {
                 if (maxHeight <= mMinTemplateHeight) {
diff --git a/slices/view/src/main/res/values/dimens.xml b/slices/view/src/main/res/values/dimens.xml
index 1f0a371..e5b6e6c 100644
--- a/slices/view/src/main/res/values/dimens.xml
+++ b/slices/view/src/main/res/values/dimens.xml
@@ -61,6 +61,8 @@
     <!-- Height of a grid row displaying only large images; also used for min-width of large images
          to calculate # of images in a row -->
     <dimen name="abc_slice_grid_image_only_height">86dp</dimen>
+    <!-- Height of space to reserve for raw images with text-->
+    <dimen name="abc_slice_grid_raw_image_text_offset">30dp</dimen>
     <!-- Minimum width of a cell displaying an icon / small image; used to calculate #
          of images in a row -->
     <dimen name="abc_slice_grid_image_min_width">60dp</dimen>
diff --git a/studiow b/studiow
index 577071b..2dc12360 100755
--- a/studiow
+++ b/studiow
@@ -1,16 +1,48 @@
 #!/usr/bin/env bash
-if [ -n "$1" ]; then
-  export ANDROIDX_PROJECTS=${1^^}
-else
-  export ANDROIDX_PROJECTS=MAIN
-  echo "Supported projects sets include:"
-  echo "- MAIN for non-Compose Jetpack libraries"
-  echo "- COMPOSE for Compose and dependencies"
-  echo "- FLAN for Fragment, Lifecycle, Activity, and Navigation"
-  echo "- ALL for all libraries"
-  echo
-  echo "No project set specified, using MAIN..."
-fi
-shift
-source gradlew studio "$@"
 
+function usage() {
+  echo "Usage: studiow [<project subset>]"
+  echo
+  echo "Project subsets:"
+  echo " m, main"
+  echo "  Open the project subset MAIN: non-Compose Jetpack libraries"
+  echo
+  echo " c, compose"
+  echo "  Open the project subset COMPOSE"
+  echo
+  echo " f, flan"
+  echo "  Open the project subset FLAN: Fragment, Lifecycle, Activity, and Navigation"
+  echo
+  echo " a, all"
+  echo "  Open the project subset ALL"
+  echo
+  exit 1
+}
+
+subsetArg="$1"
+if [ "$subsetArg" == "" ]; then
+  usage
+fi
+if [ "$subsetArg" == "m" -o "$subsetArg" == "main" ]; then
+  export ANDROIDX_PROJECTS=MAIN
+fi
+if [ "$subsetArg" == "c" -o "$subsetArg" == "compose" ]; then
+  export ANDROIDX_PROJECTS=COMPOSE
+fi
+if [ "$subsetArg" == "f" -o "$subsetArg" == "flan" ]; then
+  export ANDROIDX_PROJECTS=FLAN
+fi
+if [ "$subsetArg" == "a" -o "$subsetArg" == "all" ]; then
+  export ANDROIDX_PROJECTS=ALL
+fi
+if [ "$ANDROIDX_PROJECTS" == "" ]; then
+  echo "Unrecognized project argument: '$subsetArg'"
+  usage
+fi
+
+shift
+if [ "$1" != "" ]; then
+  echo "Unrecognized argument: '$1'"
+  usage
+fi
+source gradlew studio
diff --git a/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/CircularProgressDrawableTest.java b/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/CircularProgressDrawableTest.java
index 14759bd..a554e07 100644
--- a/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/CircularProgressDrawableTest.java
+++ b/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/CircularProgressDrawableTest.java
@@ -32,7 +32,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -46,9 +45,11 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class CircularProgressDrawableTest {
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<CircularProgressDrawableActivity> mActivityTestRule =
-            new ActivityTestRule<>(CircularProgressDrawableActivity.class);
+    public final androidx.test.rule.ActivityTestRule<CircularProgressDrawableActivity>
+            mActivityTestRule =
+                new androidx.test.rule.ActivityTestRule<>(CircularProgressDrawableActivity.class);
 
     private CircularProgressDrawable mDrawableUnderTest;
 
diff --git a/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutInScrollingParentBaseTest.java b/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutInScrollingParentBaseTest.java
index ad2a176..6486134 100644
--- a/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutInScrollingParentBaseTest.java
+++ b/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutInScrollingParentBaseTest.java
@@ -31,7 +31,6 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 import androidx.testutils.PollingCheck;
 import androidx.testutils.SwipeInjector;
 
@@ -41,9 +40,11 @@
 
 public abstract class SwipeRefreshLayoutInScrollingParentBaseTest {
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<? extends SwipeRefreshLayoutInRecyclerViewBaseActivity>
-            mActivityTestRule = new ActivityTestRule<>(getActivityClass());
+    public final androidx.test.rule
+            .ActivityTestRule<? extends SwipeRefreshLayoutInRecyclerViewBaseActivity>
+            mActivityTestRule = new androidx.test.rule.ActivityTestRule<>(getActivityClass());
 
     private RecyclerView mRecyclerView;
     private LinearLayoutManager mLayoutManager;
diff --git a/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutRequestDisallowInterceptBaseTest.java b/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutRequestDisallowInterceptBaseTest.java
index 3fa3f7b..87d8cde 100644
--- a/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutRequestDisallowInterceptBaseTest.java
+++ b/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutRequestDisallowInterceptBaseTest.java
@@ -25,7 +25,6 @@
 import android.view.ViewConfiguration;
 
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 import androidx.testutils.SwipeInjector;
 
 import org.junit.Before;
@@ -34,9 +33,11 @@
 
 public abstract class SwipeRefreshLayoutRequestDisallowInterceptBaseTest {
 
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<? extends SwipeRefreshLayoutInRecyclerViewBaseActivity>
-            mActivityTestRule = new ActivityTestRule<>(getActivityClass());
+    public final androidx.test.rule
+            .ActivityTestRule<? extends SwipeRefreshLayoutInRecyclerViewBaseActivity>
+            mActivityTestRule = new androidx.test.rule.ActivityTestRule<>(getActivityClass());
 
     private RequestDisallowInterceptRecordingRecyclerView mRecyclerView;
     private int mTouchSlop;
diff --git a/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutTest.java b/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutTest.java
index caf7544..bf17eca 100644
--- a/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutTest.java
+++ b/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutTest.java
@@ -38,7 +38,6 @@
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
 import androidx.testutils.PollingCheck;
 
 import org.junit.Before;
@@ -55,9 +54,10 @@
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class SwipeRefreshLayoutTest {
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<SwipeRefreshLayoutActivity> mActivityTestRule =
-            new ActivityTestRule<>(SwipeRefreshLayoutActivity.class);
+    public final androidx.test.rule.ActivityTestRule<SwipeRefreshLayoutActivity> mActivityTestRule =
+            new androidx.test.rule.ActivityTestRule<>(SwipeRefreshLayoutActivity.class);
 
     private static final long TIMEOUT = 1000;
     private static final int INVALID_SIZE = 1000;
diff --git a/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutWithHorizontallyScrollingChildTest.java b/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutWithHorizontallyScrollingChildTest.java
index e041327..66631cd 100644
--- a/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutWithHorizontallyScrollingChildTest.java
+++ b/swiperefreshlayout/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutWithHorizontallyScrollingChildTest.java
@@ -41,7 +41,6 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 import androidx.testutils.SwipeInjector;
 
 import org.junit.Before;
@@ -52,8 +51,8 @@
 
     @Rule
     @SuppressWarnings("deprecation")
-    public final ActivityTestRule<ComponentActivity>
-            mActivityTestRule = new ActivityTestRule<>(ComponentActivity.class);
+    public final androidx.test.rule.ActivityTestRule<ComponentActivity>
+            mActivityTestRule = new androidx.test.rule.ActivityTestRule<>(ComponentActivity.class);
 
     private SwipeRefreshLayout mSwipeRefreshLayout;
     private RecyclerView mRecyclerView;
diff --git a/testutils/testutils-common/src/main/java/androidx/testutils/TestExecutor.kt b/testutils/testutils-common/src/main/java/androidx/testutils/TestExecutor.kt
index f1730b0..f1ac548 100644
--- a/testutils/testutils-common/src/main/java/androidx/testutils/TestExecutor.kt
+++ b/testutils/testutils-common/src/main/java/androidx/testutils/TestExecutor.kt
@@ -20,10 +20,18 @@
 import java.util.concurrent.Executor
 
 class TestExecutor : Executor {
+    /**
+     * If true, adding a new task will drain all existing tasks.
+     */
+    var autoRun: Boolean = false
+
     private val mTasks = LinkedList<Runnable>()
 
     override fun execute(command: Runnable) {
         mTasks.add(command)
+        if (autoRun) {
+            executeAll()
+        }
     }
 
     fun executeAll(): Boolean {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt b/testutils/testutils-mockito/build.gradle
similarity index 70%
copy from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
copy to testutils/testutils-mockito/build.gradle
index f9cb2fe..ea8a7de 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
+++ b/testutils/testutils-mockito/build.gradle
@@ -14,7 +14,16 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.focus
+import static androidx.build.dependencies.DependenciesKt.*
 
-@RequiresOptIn("The Focus API is experimental and is likely to change in the future.")
-annotation class ExperimentalFocus
\ No newline at end of file
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+}
+
+dependencies {
+    api(MOCKITO_CORE, libs.exclude_bytebuddy)
+
+    implementation(KOTLIN_STDLIB)
+}
diff --git a/car/app/app/src/androidTest/AndroidManifest.xml b/testutils/testutils-mockito/src/main/AndroidManifest.xml
similarity index 84%
copy from car/app/app/src/androidTest/AndroidManifest.xml
copy to testutils/testutils-mockito/src/main/AndroidManifest.xml
index 3bc2684..dbe1e34 100644
--- a/car/app/app/src/androidTest/AndroidManifest.xml
+++ b/testutils/testutils-mockito/src/main/AndroidManifest.xml
@@ -14,6 +14,4 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.car.app">
-</manifest>
+<manifest package="androidx.testutils.mockito"/>
diff --git a/testutils/testutils-mockito/src/main/java/androidx/testutils/mockito/MockitoUtils.kt b/testutils/testutils-mockito/src/main/java/androidx/testutils/mockito/MockitoUtils.kt
new file mode 100644
index 0000000..96d4b0a
--- /dev/null
+++ b/testutils/testutils-mockito/src/main/java/androidx/testutils/mockito/MockitoUtils.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.testutils.mockito
+
+import org.mockito.Answers
+import org.mockito.MockSettings
+import org.mockito.Mockito
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.stubbing.Answer
+
+/**
+ * [Answer] variant intended for [MockSettings.defaultAnswer] that logs the unmocked method
+ * that was called, serializing the arguments used, to try and provide a more informative
+ * error message.
+ */
+val ANSWER_THROWS = Answer {
+    when (val name = it.method.name) {
+        // Delegate to the actual toString, since that will probably not be mocked by a test
+        "toString" -> Answers.CALLS_REAL_METHODS.answer(it)
+        else -> {
+            val arguments = it.arguments
+                ?.takeUnless { it.isEmpty() }
+                ?.mapIndexed { index, arg ->
+                    try {
+                        arg?.toString()
+                    } catch (e: Exception) {
+                        "toString[$index] threw ${e.message}"
+                    }
+                }
+                ?.joinToString()
+                ?: "no arguments"
+
+            throw UnsupportedOperationException(
+                "${it.mock::class.java.simpleName}#$name with $arguments should not be called"
+            )
+        }
+    }
+}
+
+fun <Type : Any?> whenever(mock: Type, block: InvocationOnMock.() -> Type) =
+    Mockito.`when`(mock).thenAnswer { block(it) }!!
+
+/**
+ * Spy an existing object and allow mocking within [block]. Once the method returns, the spied
+ * instance is prepped to throw exceptions whenever an unmocked method is called. This can be
+ * used to enforce that only specifically mocked methods are called, avoiding unexpected
+ * results when the behavior under test adds code to call an unexpected method.
+ */
+inline fun <reified T> spyThrowOnUnmocked(value: T?, block: T.() -> Unit = {}): T {
+    val swappingAnswer = object : Answer<Any?> {
+        var delegate: Answer<*> = Answers.RETURNS_DEFAULTS
+
+        override fun answer(invocation: InvocationOnMock?): Any? {
+            return delegate.answer(invocation)
+        }
+    }
+
+    val settings = Mockito.withSettings()
+        .spiedInstance(value)
+        .defaultAnswer(swappingAnswer)
+
+    return Mockito.mock(T::class.java, settings)
+        .apply(block)
+        .also {
+            // To allow Mockito.when() usage inside block, only swap to throwing afterwards
+            swappingAnswer.delegate = ANSWER_THROWS
+        }
+}
+
+/**
+ * [Mockito.mock] equivalent of [spyThrowOnUnmocked] which doesn't spy an existing instance.
+ */
+inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit = {}) =
+    spyThrowOnUnmocked(null, block)
\ No newline at end of file
diff --git a/testutils/testutils-paging/src/main/java/androidx/paging/LoadStateUtils.kt b/testutils/testutils-paging/src/main/java/androidx/paging/LoadStateUtils.kt
index 6e22616..76a4482 100644
--- a/testutils/testutils-paging/src/main/java/androidx/paging/LoadStateUtils.kt
+++ b/testutils/testutils-paging/src/main/java/androidx/paging/LoadStateUtils.kt
@@ -19,23 +19,6 @@
 import androidx.paging.LoadState.NotLoading
 
 /**
- * Converts a list of incremental LoadState updates to local source to a list of expected
- * [CombinedLoadStates] events.
- */
-@OptIn(ExperimentalStdlibApi::class)
-fun List<Pair<LoadType, LoadState>>.toCombinedLoadStatesLocal() = scan(
-    CombinedLoadStates(
-        source = LoadStates(
-            refresh = NotLoading(endOfPaginationReached = false),
-            prepend = NotLoading(endOfPaginationReached = false),
-            append = NotLoading(endOfPaginationReached = false)
-        )
-    )
-) { prev, update ->
-    prev.set(update.first, false, update.second)
-}
-
-/**
  * Test-only local-only LoadStates builder which defaults each state to [NotLoading], with
  * [LoadState.endOfPaginationReached] = `false`
  */
@@ -44,11 +27,14 @@
     prependLocal: LoadState = NotLoading(endOfPaginationReached = false),
     appendLocal: LoadState = NotLoading(endOfPaginationReached = false)
 ) = CombinedLoadStates(
+    refresh = refreshLocal,
+    prepend = prependLocal,
+    append = appendLocal,
     source = LoadStates(
         refresh = refreshLocal,
         prepend = prependLocal,
-        append = appendLocal
-    )
+        append = appendLocal,
+    ),
 )
 
 /**
@@ -56,6 +42,9 @@
  * [LoadState.endOfPaginationReached] = `false`
  */
 fun remoteLoadStatesOf(
+    refresh: LoadState = NotLoading(endOfPaginationReached = false),
+    prepend: LoadState = NotLoading(endOfPaginationReached = false),
+    append: LoadState = NotLoading(endOfPaginationReached = false),
     refreshLocal: LoadState = NotLoading(endOfPaginationReached = false),
     prependLocal: LoadState = NotLoading(endOfPaginationReached = false),
     appendLocal: LoadState = NotLoading(endOfPaginationReached = false),
@@ -63,66 +52,17 @@
     prependRemote: LoadState = NotLoading(endOfPaginationReached = false),
     appendRemote: LoadState = NotLoading(endOfPaginationReached = false)
 ) = CombinedLoadStates(
+    refresh = refresh,
+    prepend = prepend,
+    append = append,
     source = LoadStates(
         refresh = refreshLocal,
         prepend = prependLocal,
-        append = appendLocal
+        append = appendLocal,
     ),
     mediator = LoadStates(
         refresh = refreshRemote,
         prepend = prependRemote,
-        append = appendRemote
-    )
+        append = appendRemote,
+    ),
 )
-
-private fun CombinedLoadStates.set(
-    loadType: LoadType,
-    fromMediator: Boolean,
-    loadState: LoadState
-) = when (loadType) {
-    LoadType.REFRESH ->
-        if (fromMediator) {
-            copy(
-                mediator = mediator?.copy(refresh = loadState)
-                    ?: LoadStates(
-                        refresh = loadState,
-                        prepend = NotLoading(false),
-                        append = NotLoading(false)
-                    )
-            )
-        } else {
-            copy(
-                source = source.copy(refresh = loadState)
-            )
-        }
-    LoadType.PREPEND ->
-        if (fromMediator) {
-            copy(
-                mediator = mediator?.copy(prepend = loadState)
-                    ?: LoadStates(
-                        refresh = NotLoading(false),
-                        prepend = loadState,
-                        append = NotLoading(false)
-                    )
-            )
-        } else {
-            copy(
-                source = source.copy(prepend = loadState)
-            )
-        }
-    LoadType.APPEND ->
-        if (fromMediator) {
-            copy(
-                mediator = mediator?.copy(append = loadState)
-                    ?: LoadStates(
-                        refresh = NotLoading(false),
-                        prepend = NotLoading(false),
-                        append = loadState
-                    )
-            )
-        } else {
-            copy(
-                source = source.copy(append = loadState)
-            )
-        }
-}
diff --git a/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingDataDiffer.kt b/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingDataDiffer.kt
index 7c329b9..014dae3 100644
--- a/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingDataDiffer.kt
+++ b/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingDataDiffer.kt
@@ -28,8 +28,12 @@
         previousList: NullPaddedList<T>,
         newList: NullPaddedList<T>,
         newCombinedLoadStates: CombinedLoadStates,
-        lastAccessedIndex: Int
-    ): Int? = null
+        lastAccessedIndex: Int,
+        onListPresentable: () -> Unit,
+    ): Int? {
+        onListPresentable()
+        return null
+    }
 
     companion object {
         private val noopDifferCallback = object : DifferCallback {
diff --git a/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingSource.kt b/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingSource.kt
index d0113b5..d5f3b92 100644
--- a/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingSource.kt
+++ b/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingSource.kt
@@ -82,7 +82,6 @@
         }
     }
 
-    @OptIn(ExperimentalPagingApi::class)
     override fun getRefreshKey(state: PagingState<Int, Int>): Int? {
         getRefreshKeyCalls.add(state)
         return state.anchorPosition
diff --git a/testutils/testutils-runtime/src/main/java/androidx/fragment/app/StrictFragment.kt b/testutils/testutils-runtime/src/main/java/androidx/fragment/app/StrictFragment.kt
index b7feaa3..e52994e 100644
--- a/testutils/testutils-runtime/src/main/java/androidx/fragment/app/StrictFragment.kt
+++ b/testutils/testutils-runtime/src/main/java/androidx/fragment/app/StrictFragment.kt
@@ -127,7 +127,7 @@
         super.onActivityCreated(savedInstanceState)
         checkActivityNotDestroyed()
         calledOnActivityCreated = true
-        checkState("onActivityCreated", State.ATTACHED, State.CREATED)
+        checkState("onActivityCreated", State.ATTACHED, State.CREATED, State.VIEW_CREATED)
         val fromState = currentState
         currentState = State.ACTIVITY_CREATED
         onStateChanged(fromState)
@@ -198,6 +198,7 @@
         DETACHED,
         ATTACHED,
         CREATED,
+        VIEW_CREATED,
         ACTIVITY_CREATED,
         STARTED,
         RESUMED
diff --git a/testutils/testutils-truth/src/main/java/androidx/testutils/assertions.kt b/testutils/testutils-truth/src/main/java/androidx/testutils/assertions.kt
index 6e86816..1dfc738 100644
--- a/testutils/testutils-truth/src/main/java/androidx/testutils/assertions.kt
+++ b/testutils/testutils-truth/src/main/java/androidx/testutils/assertions.kt
@@ -46,3 +46,22 @@
 }
 
 fun fail(message: String? = null): Nothing = throw AssertionError(message)
+
+// The assertThrows above cannot be used from Java.
+@Suppress("UNCHECKED_CAST")
+fun <T : Throwable?> assertThrows(
+    expectedType: Class<T>,
+    runnable: Runnable
+): ThrowableSubject {
+    try {
+        runnable.run()
+    } catch (t: Throwable) {
+        if (expectedType.isInstance(t)) {
+            return assertThat(t)
+        }
+        throw t
+    }
+    throw AssertionError(
+        "Body completed successfully. Expected ${expectedType.simpleName}"
+    )
+}
\ No newline at end of file
diff --git a/testutils/testutils-truth/src/test/java/androidx/testutils/AssertionsTest.kt b/testutils/testutils-truth/src/test/java/androidx/testutils/AssertionsTest.kt
new file mode 100644
index 0000000..b65e977
--- /dev/null
+++ b/testutils/testutils-truth/src/test/java/androidx/testutils/AssertionsTest.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.testutils
+
+import org.junit.Test
+import java.io.IOException
+
+class AssertionsTest {
+    @Test
+    fun testNoFailureThrowsAssertionError() {
+        try {
+            assertThrows(IOException::class.java) {
+                // No Exception thrown
+            }
+        } catch (e: AssertionError) {
+            return // expected
+        }
+
+        fail("expected assertion error for no failure")
+    }
+
+    @Test
+    fun testIncorrectFailureThrowsAssertionError() {
+        try {
+            assertThrows(IOException::class.java) {
+                throw IllegalStateException()
+            }
+        } catch (e: IllegalStateException) {
+            return // expected
+        }
+
+        fail("expected IllegalStateException to propagate")
+    }
+
+    @Test
+    fun testCorrectFailureTypeIsCaughtAndReturnsAsThrowableSubject() {
+        assertThrows(IOException::class.java) {
+            throw IOException("test123")
+        }.hasMessageThat().contains("test123")
+    }
+}
\ No newline at end of file
diff --git a/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java b/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
index 1ac1834..1e24c25 100644
--- a/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
+++ b/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
@@ -16,8 +16,8 @@
 
 package androidx.tracing;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
diff --git a/transition/README.md b/transition/README.md
index 2e9a808..45f33c5 100644
--- a/transition/README.md
+++ b/transition/README.md
@@ -6,7 +6,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/transition)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/transition/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/transition/)
 
 [Reference documentation](https://developer.android.com/reference/androidx/transition/package-summary)
 
diff --git a/transition/transition/src/androidTest/java/androidx/transition/BaseTest.java b/transition/transition/src/androidTest/java/androidx/transition/BaseTest.java
index 7e934e0..1736a8b 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/BaseTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/BaseTest.java
@@ -17,19 +17,19 @@
 package androidx.transition;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 public abstract class BaseTest {
-
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<TransitionActivity> rule;
+    public final androidx.test.rule.ActivityTestRule<TransitionActivity> rule;
 
+    @SuppressWarnings("deprecation")
     BaseTest() {
-        rule = new ActivityTestRule<>(TransitionActivity.class);
+        rule = new androidx.test.rule.ActivityTestRule<>(TransitionActivity.class);
     }
 
 }
diff --git a/transition/transition/src/androidTest/java/androidx/transition/TransitionActivity.java b/transition/transition/src/androidTest/java/androidx/transition/TransitionActivity.java
index 8ce0767..002c7d4 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/TransitionActivity.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/TransitionActivity.java
@@ -28,6 +28,7 @@
 
     private LinearLayout mRoot;
 
+    @SuppressWarnings("deprecation")
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/vectordrawable/vectordrawable-animated/build.gradle b/vectordrawable/vectordrawable-animated/build.gradle
index c188aac..57a3bf7 100644
--- a/vectordrawable/vectordrawable-animated/build.gradle
+++ b/vectordrawable/vectordrawable-animated/build.gradle
@@ -46,4 +46,5 @@
     mavenGroup = LibraryGroups.VECTORDRAWABLE
     inceptionYear = "2015"
     description = "Android Support AnimatedVectorDrawable"
+    failOnDeprecationWarnings = false
 }
diff --git a/viewpager/viewpager/build.gradle b/viewpager/viewpager/build.gradle
index 656aa08..7194986 100644
--- a/viewpager/viewpager/build.gradle
+++ b/viewpager/viewpager/build.gradle
@@ -35,4 +35,5 @@
     mavenGroup = LibraryGroups.VIEWPAGER
     inceptionYear = "2018"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
+    failOnDeprecationWarnings = false
 }
diff --git a/wear/wear-complications-data/lint-baseline.xml b/wear/wear-complications-data/lint-baseline.xml
index d0c3c4b..dde667a 100644
--- a/wear/wear-complications-data/lint-baseline.xml
+++ b/wear/wear-complications-data/lint-baseline.xml
@@ -45,994 +45,4 @@
             column="1"/>
     </issue>
 
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;cs&quot; (Czech) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-cs/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;in&quot; (Indonesian) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-in/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ja&quot; (Japanese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ja/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;km&quot; (Khmer) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-km/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ko&quot; (Korean) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ko/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lo&quot; (Lao) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lo/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lt&quot; (Lithuanian) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lt/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ms&quot; (Malay) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ms/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;my&quot; (Burmese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-my/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;sk&quot; (Slovak) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-sk/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;th&quot; (Thai) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-th/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;vi&quot; (Vietnamese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-vi/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rCN/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rHK/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_days&quot; formatted=&quot;false&quot; msgid=&quot;8500262093840795448&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rTW/complication_strings.xml"
-            line="4"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;in&quot; (Indonesian) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-in/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ja&quot; (Japanese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ja/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;km&quot; (Khmer) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-km/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ko&quot; (Korean) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ko/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lo&quot; (Lao) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lo/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ms&quot; (Malay) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ms/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;my&quot; (Burmese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-my/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;th&quot; (Thai) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-th/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;vi&quot; (Vietnamese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-vi/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rCN/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rHK/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rTW/complication_strings.xml"
-            line="8"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;cs&quot; (Czech) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-cs/complication_strings.xml"
-            line="10"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lt&quot; (Lithuanian) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lt/complication_strings.xml"
-            line="10"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;sk&quot; (Slovak) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_hours&quot; formatted=&quot;false&quot; msgid=&quot;3258361256003469346&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-sk/complication_strings.xml"
-            line="10"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;in&quot; (Indonesian) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-in/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ja&quot; (Japanese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ja/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;km&quot; (Khmer) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-km/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ko&quot; (Korean) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ko/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lo&quot; (Lao) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lo/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ms&quot; (Malay) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ms/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;my&quot; (Burmese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-my/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;th&quot; (Thai) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-th/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;vi&quot; (Vietnamese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-vi/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rCN/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rHK/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rTW/complication_strings.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;cs&quot; (Czech) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-cs/complication_strings.xml"
-            line="16"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lt&quot; (Lithuanian) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lt/complication_strings.xml"
-            line="16"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;sk&quot; (Slovak) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_short_minutes&quot; formatted=&quot;false&quot; msgid=&quot;3812930575997556650&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-sk/complication_strings.xml"
-            line="16"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;in&quot; (Indonesian) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-in/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ja&quot; (Japanese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ja/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;km&quot; (Khmer) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-km/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ko&quot; (Korean) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ko/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lo&quot; (Lao) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lo/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ms&quot; (Malay) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ms/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;my&quot; (Burmese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-my/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;th&quot; (Thai) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-th/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;vi&quot; (Vietnamese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-vi/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rCN/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rHK/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rTW/complication_strings.xml"
-            line="18"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;in&quot; (Indonesian) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-in/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ja&quot; (Japanese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ja/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;km&quot; (Khmer) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-km/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ko&quot; (Korean) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ko/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lo&quot; (Lao) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lo/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ms&quot; (Malay) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ms/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;my&quot; (Burmese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-my/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;th&quot; (Thai) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-th/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;vi&quot; (Vietnamese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-vi/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rCN/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rHK/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rTW/complication_strings.xml"
-            line="22"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;cs&quot; (Czech) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-cs/complication_strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lt&quot; (Lithuanian) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lt/complication_strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;sk&quot; (Slovak) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_days&quot; formatted=&quot;false&quot; msgid=&quot;345557497041553025&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-sk/complication_strings.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;in&quot; (Indonesian) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-in/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ja&quot; (Japanese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ja/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;km&quot; (Khmer) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-km/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ko&quot; (Korean) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ko/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lo&quot; (Lao) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lo/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;ms&quot; (Malay) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-ms/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;my&quot; (Burmese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-my/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;th&quot; (Thai) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-th/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;vi&quot; (Vietnamese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-vi/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rCN/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rHK/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;zh&quot; (Chinese) the following quantities are not relevant: `one`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-zh-rTW/complication_strings.xml"
-            line="26"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;cs&quot; (Czech) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-cs/complication_strings.xml"
-            line="30"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lt&quot; (Lithuanian) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lt/complication_strings.xml"
-            line="30"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;sk&quot; (Slovak) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_hours&quot; formatted=&quot;false&quot; msgid=&quot;2990178439049007198&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-sk/complication_strings.xml"
-            line="30"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;cs&quot; (Czech) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-cs/complication_strings.xml"
-            line="36"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;lt&quot; (Lithuanian) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-lt/complication_strings.xml"
-            line="36"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedQuantity"
-        message="For language &quot;sk&quot; (Slovak) the following quantities are not relevant: `many`"
-        errorLine1="    &lt;plurals name=&quot;time_difference_words_minutes&quot; formatted=&quot;false&quot; msgid=&quot;9081188175463984403&quot;>"
-        errorLine2="    ^">
-        <location
-            file="src/main/res/values-sk/complication_strings.xml"
-            line="36"
-            column="5"/>
-    </issue>
-
 </issues>
diff --git a/wear/wear-input/src/test/java/androidx/wear/input/WearableButtonsTest.java b/wear/wear-input/src/test/java/androidx/wear/input/WearableButtonsTest.java
index 08c26d8..1c1f420 100644
--- a/wear/wear-input/src/test/java/androidx/wear/input/WearableButtonsTest.java
+++ b/wear/wear-input/src/test/java/androidx/wear/input/WearableButtonsTest.java
@@ -295,6 +295,7 @@
         assertEquals(4, info.getY(), 1.0e-7);
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void testGetButtonsLeftyNoData() {
         Context context = ApplicationProvider.getApplicationContext();
diff --git a/wear/wear-remote-interactions/api/current.txt b/wear/wear-remote-interactions/api/current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/wear/wear-remote-interactions/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/wear/wear-remote-interactions/api/public_plus_experimental_current.txt b/wear/wear-remote-interactions/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/wear/wear-remote-interactions/api/public_plus_experimental_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutDirections.kt b/wear/wear-remote-interactions/api/res-current.txt
similarity index 100%
copy from compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/LayoutDirections.kt
copy to wear/wear-remote-interactions/api/res-current.txt
diff --git a/wear/wear-remote-interactions/api/restricted_current.txt b/wear/wear-remote-interactions/api/restricted_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/wear/wear-remote-interactions/api/restricted_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/wear/wear-remote-interactions/build.gradle b/wear/wear-remote-interactions/build.gradle
new file mode 100644
index 0000000..60b0de0
--- /dev/null
+++ b/wear/wear-remote-interactions/build.gradle
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+}
+
+dependencies {
+    api("androidx.annotation:annotation:1.1.0")
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 23
+    }
+
+    // Use Robolectric 4.+
+    testOptions.unitTests.includeAndroidResources = true
+}
+
+androidx {
+    name = "Android Wear Remote Interactions"
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    mavenGroup = LibraryGroups.WEAR
+    mavenVersion = LibraryVersions.WEAR_REMOTE_INTERACTIONS
+    inceptionYear = "2020"
+    description = "Android Wear Remote Interactions"
+}
\ No newline at end of file
diff --git a/car/app/app/src/androidTest/AndroidManifest.xml b/wear/wear-remote-interactions/src/main/AndroidManifest.xml
similarity index 93%
copy from car/app/app/src/androidTest/AndroidManifest.xml
copy to wear/wear-remote-interactions/src/main/AndroidManifest.xml
index 3bc2684..4d89c3b 100644
--- a/car/app/app/src/androidTest/AndroidManifest.xml
+++ b/wear/wear-remote-interactions/src/main/AndroidManifest.xml
@@ -14,6 +14,7 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
+
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.car.app">
-</manifest>
+    package="androidx.wear.remote.interactions">
+</manifest>
\ No newline at end of file
diff --git a/wear/wear-watchface-client/api/current.txt b/wear/wear-watchface-client/api/current.txt
index ee7fa64..c744f24 100644
--- a/wear/wear-watchface-client/api/current.txt
+++ b/wear/wear-watchface-client/api/current.txt
@@ -2,15 +2,17 @@
 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);
+    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, androidx.wear.complications.data.ComplicationType currentType);
     method public android.graphics.Rect getBounds();
     method public int getBoundsType();
+    method public androidx.wear.complications.data.ComplicationType getCurrentType();
     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.data.ComplicationType currentType;
     property public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
     property public final androidx.wear.complications.data.ComplicationType defaultProviderType;
     property public final boolean isEnabled;
@@ -84,7 +86,9 @@
 
   public interface InteractiveWatchFaceWcsClient extends java.lang.AutoCloseable {
     method public android.os.IBinder asBinder();
+    method public void bringAttentionToComplication(int complicationId);
     method public default static androidx.wear.watchface.client.InteractiveWatchFaceWcsClient createFromBinder(android.os.IBinder binder);
+    method public default Integer? getComplicationIdAt(@Px int x, @Px int y);
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> getComplicationState();
     method public String getInstanceId();
     method public long getPreviewReferenceTimeMillis();
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 5b9d604..3e7223f 100644
--- a/wear/wear-watchface-client/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface-client/api/public_plus_experimental_current.txt
@@ -2,15 +2,17 @@
 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);
+    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, androidx.wear.complications.data.ComplicationType currentType);
     method public android.graphics.Rect getBounds();
     method public int getBoundsType();
+    method public androidx.wear.complications.data.ComplicationType getCurrentType();
     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.data.ComplicationType currentType;
     property public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
     property public final androidx.wear.complications.data.ComplicationType defaultProviderType;
     property public final boolean isEnabled;
@@ -84,7 +86,9 @@
 
   public interface InteractiveWatchFaceWcsClient extends java.lang.AutoCloseable {
     method public android.os.IBinder asBinder();
+    method public void bringAttentionToComplication(int complicationId);
     method public default static androidx.wear.watchface.client.InteractiveWatchFaceWcsClient createFromBinder(android.os.IBinder binder);
+    method public default Integer? getComplicationIdAt(@Px int x, @Px int y);
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> getComplicationState();
     method public String getInstanceId();
     method public long getPreviewReferenceTimeMillis();
diff --git a/wear/wear-watchface-client/api/restricted_current.txt b/wear/wear-watchface-client/api/restricted_current.txt
index be317d9..7961108 100644
--- a/wear/wear-watchface-client/api/restricted_current.txt
+++ b/wear/wear-watchface-client/api/restricted_current.txt
@@ -2,15 +2,17 @@
 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);
+    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, androidx.wear.complications.data.ComplicationType currentType);
     method public android.graphics.Rect getBounds();
     method public int getBoundsType();
+    method public androidx.wear.complications.data.ComplicationType getCurrentType();
     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.data.ComplicationType currentType;
     property public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
     property public final androidx.wear.complications.data.ComplicationType defaultProviderType;
     property public final boolean isEnabled;
@@ -84,7 +86,9 @@
 
   public interface InteractiveWatchFaceWcsClient extends java.lang.AutoCloseable {
     method public android.os.IBinder asBinder();
+    method public void bringAttentionToComplication(int complicationId);
     method public default static androidx.wear.watchface.client.InteractiveWatchFaceWcsClient createFromBinder(android.os.IBinder binder);
+    method public default Integer? getComplicationIdAt(@Px int x, @Px int y);
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> getComplicationState();
     method public String getInstanceId();
     method public long getPreviewReferenceTimeMillis();
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 7a6a332..898a8cfe 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,6 +19,7 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.graphics.Color
 import android.graphics.Rect
 import android.os.Handler
 import android.os.Looper
@@ -32,8 +33,10 @@
 import androidx.wear.complications.SystemProviders
 import androidx.wear.complications.data.ComplicationText
 import androidx.wear.complications.data.ComplicationType
+import androidx.wear.complications.data.LongTextComplicationData
 import androidx.wear.complications.data.ShortTextComplicationData
 import androidx.wear.watchface.DrawMode
+import androidx.wear.watchface.LayerMode
 import androidx.wear.watchface.RenderParameters
 import androidx.wear.watchface.client.DeviceConfig
 import androidx.wear.watchface.client.SystemState
@@ -50,9 +53,11 @@
 import androidx.wear.watchface.samples.GREEN_STYLE
 import androidx.wear.watchface.samples.NO_COMPLICATIONS
 import androidx.wear.watchface.samples.WATCH_HAND_LENGTH_STYLE_SETTING
+import androidx.wear.watchface.style.Layer
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Rule
@@ -151,7 +156,12 @@
             400
         ).get(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)!!
         val bitmap = headlessInstance.takeWatchFaceScreenshot(
-            RenderParameters(DrawMode.INTERACTIVE, RenderParameters.DRAW_ALL_LAYERS, null),
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                RenderParameters.DRAW_ALL_LAYERS,
+                null,
+                Color.RED
+            ),
             100,
             1234567,
             null,
@@ -164,6 +174,44 @@
     }
 
     @Test
+    fun yellowComplicationHighlights() {
+        val headlessInstance = service.createHeadlessWatchFaceClient(
+            ComponentName(
+                "androidx.wear.watchface.samples.test",
+                "androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService"
+            ),
+            DeviceConfig(
+                false,
+                false,
+                0,
+                0
+            ),
+            400,
+            400
+        ).get(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)!!
+        val bitmap = headlessInstance.takeWatchFaceScreenshot(
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                mapOf(
+                    Layer.BASE_LAYER to LayerMode.DRAW,
+                    Layer.COMPLICATIONS to LayerMode.DRAW_HIGHLIGHTED,
+                    Layer.TOP_LAYER to LayerMode.DRAW
+                ),
+                null,
+                Color.YELLOW
+            ),
+            100,
+            1234567,
+            null,
+            complications
+        )
+
+        bitmap.assertAgainstGolden(screenshotRule, "yellowComplicationHighlights")
+
+        headlessInstance.close()
+    }
+
+    @Test
     fun headlessComplicationDetails() {
         val headlessInstance = service.createHeadlessWatchFaceClient(
             exampleWatchFaceComponentName,
@@ -264,7 +312,12 @@
             interactiveInstanceFuture.get(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)!!
 
         val bitmap = interactiveInstance.takeWatchFaceScreenshot(
-            RenderParameters(DrawMode.INTERACTIVE, RenderParameters.DRAW_ALL_LAYERS, null),
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                RenderParameters.DRAW_ALL_LAYERS,
+                null,
+                Color.RED
+            ),
             100,
             1234567,
             null,
@@ -304,7 +357,12 @@
             interactiveInstanceFuture.get(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)!!
 
         val bitmap = interactiveInstance.takeWatchFaceScreenshot(
-            RenderParameters(DrawMode.INTERACTIVE, RenderParameters.DRAW_ALL_LAYERS, null),
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                RenderParameters.DRAW_ALL_LAYERS,
+                null,
+                Color.RED
+            ),
             100,
             1234567,
             null,
@@ -338,6 +396,15 @@
         val interactiveInstance =
             interactiveInstanceFuture.get(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)!!
 
+        interactiveInstance.updateComplicationData(
+            mapOf(
+                EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID to
+                    ShortTextComplicationData.Builder(ComplicationText.plain("Test")).build(),
+                EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID to
+                    LongTextComplicationData.Builder(ComplicationText.plain("Test")).build()
+            )
+        )
+
         assertThat(interactiveInstance.complicationState.size).isEqualTo(2)
 
         val leftComplicationDetails = interactiveInstance.complicationState[
@@ -359,6 +426,9 @@
             ComplicationType.SMALL_IMAGE
         )
         assertTrue(leftComplicationDetails.isEnabled)
+        assertThat(leftComplicationDetails.currentType).isEqualTo(
+            ComplicationType.SHORT_TEXT
+        )
 
         val rightComplicationDetails = interactiveInstance.complicationState[
             EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
@@ -379,6 +449,9 @@
             ComplicationType.SMALL_IMAGE
         )
         assertTrue(rightComplicationDetails.isEnabled)
+        assertThat(rightComplicationDetails.currentType).isEqualTo(
+            ComplicationType.LONG_TEXT
+        )
 
         interactiveInstance.close()
     }
@@ -530,7 +603,12 @@
         )
 
         val bitmap = interactiveInstance.takeWatchFaceScreenshot(
-            RenderParameters(DrawMode.INTERACTIVE, RenderParameters.DRAW_ALL_LAYERS, null),
+            RenderParameters(
+                DrawMode.INTERACTIVE,
+                RenderParameters.DRAW_ALL_LAYERS,
+                null,
+                Color.RED
+            ),
             100,
             1234567,
             null,
@@ -544,6 +622,36 @@
             interactiveInstance.close()
         }
     }
+
+    @Test
+    fun getComplicationIdAt() {
+        val interactiveInstanceFuture =
+            service.getOrCreateWallpaperServiceBackedInteractiveWatchFaceWcsClient(
+                "testId",
+                deviceConfig,
+                systemState,
+                null,
+                complications
+            )
+
+        Mockito.`when`(surfaceHolder.surfaceFrame)
+            .thenReturn(Rect(0, 0, 400, 400))
+
+        // Create the engine which triggers creation of InteractiveWatchFaceWcsClient.
+        createEngine()
+
+        val interactiveInstance =
+            interactiveInstanceFuture.get(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)!!
+
+        assertNull(interactiveInstance.getComplicationIdAt(0, 0))
+        assertThat(interactiveInstance.getComplicationIdAt(85, 165)).isEqualTo(
+            EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID
+        )
+        assertThat(interactiveInstance.getComplicationIdAt(255, 165)).isEqualTo(
+            EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
+        )
+        interactiveInstance.close()
+    }
 }
 
 internal class TestExampleCanvasAnalogWatchFaceService(
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
index 847643a..5c2dda8 100644
--- 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
@@ -18,6 +18,7 @@
 
 import android.graphics.Rect
 import androidx.wear.complications.DefaultComplicationProviderPolicy
+import androidx.wear.complications.data.ComplicationData
 import androidx.wear.complications.data.ComplicationType
 import androidx.wear.watchface.Complication
 import androidx.wear.watchface.data.ComplicationBoundsType
@@ -42,7 +43,10 @@
 
     /** Whether or not the complication is drawn. */
     @get:JvmName("isEnabled")
-    public val isEnabled: Boolean
+    public val isEnabled: Boolean,
+
+    /** The [ComplicationType] of the complication's current [ComplicationData]. */
+    public val currentType: ComplicationType
 ) {
     internal constructor(
         complicationStateWireFormat: ComplicationStateWireFormat
@@ -55,6 +59,7 @@
             complicationStateWireFormat.fallbackSystemProvider
         ),
         ComplicationType.fromWireType(complicationStateWireFormat.defaultProviderType),
-        complicationStateWireFormat.isEnabled
+        complicationStateWireFormat.isEnabled,
+        ComplicationType.fromWireType(complicationStateWireFormat.currentType)
     )
 }
\ No newline at end of file
diff --git a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceWcsClient.kt b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceWcsClient.kt
index 1205802..65d2a76 100644
--- a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceWcsClient.kt
+++ b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceWcsClient.kt
@@ -20,11 +20,13 @@
 import android.os.IBinder
 import android.support.wearable.watchface.SharedMemoryImage
 import androidx.annotation.IntRange
+import androidx.annotation.Px
 import androidx.annotation.RequiresApi
 import androidx.wear.complications.data.ComplicationData
 import androidx.wear.watchface.RenderParameters
 import androidx.wear.watchface.control.IInteractiveWatchFaceWCS
 import androidx.wear.watchface.control.data.WatchfaceScreenshotParams
+import androidx.wear.watchface.data.ComplicationBoundsType
 import androidx.wear.watchface.data.IdAndComplicationDataWireFormat
 import androidx.wear.watchface.style.UserStyle
 import androidx.wear.watchface.style.UserStyleSchema
@@ -107,6 +109,24 @@
 
     /** Returns the associated [IBinder]. Allows this interface to be passed over AIDL. */
     public fun asBinder(): IBinder
+
+    /** Returns the ID of the complication at the given coordinates or `null` if there isn't one.*/
+    @SuppressWarnings("AutoBoxing")
+    public fun getComplicationIdAt(@Px x: Int, @Px y: Int): Int? =
+        complicationState.asSequence().firstOrNull {
+            it.value.isEnabled && when (it.value.boundsType) {
+                ComplicationBoundsType.ROUND_RECT -> it.value.bounds.contains(x, y)
+                ComplicationBoundsType.BACKGROUND -> false
+                ComplicationBoundsType.EDGE -> false
+                else -> false
+            }
+        }?.key
+
+    /**
+     * Requests the specified complication is highlighted for a short period to bring attention to
+     * it.
+     */
+    public fun bringAttentionToComplication(complicationId: Int)
 }
 
 /** Controls a stateful remote interactive watch face with an interface tailored for WCS. */
@@ -177,4 +197,8 @@
     }
 
     override fun asBinder(): IBinder = iInteractiveWatchFaceWcs.asBinder()
+
+    override fun bringAttentionToComplication(complicationId: Int) {
+        iInteractiveWatchFaceWcs.bringAttentionToComplication(complicationId)
+    }
 }
\ No newline at end of file
diff --git a/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/RoundedDrawableTest.java b/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/RoundedDrawableTest.java
index c909f57..a3b5fa2 100644
--- a/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/RoundedDrawableTest.java
+++ b/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/RoundedDrawableTest.java
@@ -63,6 +63,7 @@
                         Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888));
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void colorFilterIsAppliedCorrectly() {
         ColorFilter cf = new ColorFilter();
diff --git a/wear/wear-watchface-data/api/restricted_current.txt b/wear/wear-watchface-data/api/restricted_current.txt
index f95a226..62ee931 100644
--- a/wear/wear-watchface-data/api/restricted_current.txt
+++ b/wear/wear-watchface-data/api/restricted_current.txt
@@ -188,10 +188,11 @@
   }
 
   @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);
+    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, @android.support.wearable.complications.ComplicationData.ComplicationType int);
     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 getCurrentType();
     method @android.support.wearable.complications.ComplicationData.ComplicationType public int getDefaultProviderType();
     method public java.util.List<android.content.ComponentName!>? getDefaultProvidersToTry();
     method public int getFallbackSystemProvider();
@@ -231,9 +232,10 @@
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.VersionedParcelize public class RenderParametersWireFormat implements android.os.Parcelable androidx.versionedparcelable.VersionedParcelable {
-    ctor public RenderParametersWireFormat(int, java.util.List<androidx.wear.watchface.data.RenderParametersWireFormat.LayerParameterWireFormat!>, Integer?);
+    ctor public RenderParametersWireFormat(int, java.util.List<androidx.wear.watchface.data.RenderParametersWireFormat.LayerParameterWireFormat!>, Integer?, @ColorInt int);
     method public int describeContents();
     method public int getDrawMode();
+    method @ColorInt public int getHighlightTint();
     method public Integer? getHighlightedComplicationId();
     method public java.util.List<androidx.wear.watchface.data.RenderParametersWireFormat.LayerParameterWireFormat!> getLayerParameters();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/wear/wear-watchface-data/build.gradle b/wear/wear-watchface-data/build.gradle
index f32aa2f..97a32fd 100644
--- a/wear/wear-watchface-data/build.gradle
+++ b/wear/wear-watchface-data/build.gradle
@@ -48,6 +48,10 @@
     buildFeatures {
         aidl = true
     }
+
+    buildTypes.all {
+        consumerProguardFiles 'proguard-rules.pro'
+    }
 }
 
 androidx {
diff --git a/wear/wear-watchface-data/proguard-rules.pro b/wear/wear-watchface-data/proguard-rules.pro
new file mode 100644
index 0000000..f50b7ac
--- /dev/null
+++ b/wear/wear-watchface-data/proguard-rules.pro
@@ -0,0 +1,16 @@
+# 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.
+
+# Prevent Parcelizer objects from being removed or renamed.
+-keep public class androidx.wear.watchface.**Parcelizer { *; }
\ 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 40eb511..667c27f 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
@@ -32,7 +32,7 @@
 interface IInteractiveWatchFaceWCS {
     // IMPORTANT NOTE: All methods must be given an explicit transaction id that must never change
     // in the future to remain binary backwards compatible.
-    // Next Id: 12
+    // Next Id: 13
 
     /**
      * API version number. This should be incremented every time a new method is added.
@@ -114,4 +114,12 @@
      * @since API version 1.
      */
     oneway void release() = 11;
+
+    /**
+     * Requests the specified complication is highlighted for a short period to bring attention to
+     * it.
+     *
+     * @since API version 1.
+     */
+    oneway void bringAttentionToComplication(in int complicationId) = 12;
 }
diff --git a/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java b/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java
index 9d5b032..6fb369ab 100644
--- a/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java
+++ b/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java
@@ -69,6 +69,10 @@
     @ParcelField(7)
     boolean mIsEnabled;
 
+    @ParcelField(8)
+    @ComplicationData.ComplicationType
+    int mCurrentType;
+
     /** Used by VersionedParcelable. */
     ComplicationStateWireFormat() {
     }
@@ -80,7 +84,8 @@
             @Nullable List<ComponentName> defaultProvidersToTry,
             int fallbackSystemProvider,
             @ComplicationData.ComplicationType int defaultProviderType,
-            boolean isEnabled) {
+            boolean isEnabled,
+            @ComplicationData.ComplicationType int currentType) {
         mBounds = bounds;
         mBoundsType = boundsType;
         mSupportedTypes = supportedTypes;
@@ -88,6 +93,7 @@
         mFallbackSystemProvider = fallbackSystemProvider;
         mDefaultProviderType = defaultProviderType;
         mIsEnabled = isEnabled;
+        mCurrentType = currentType;
     }
 
     @NonNull
@@ -132,6 +138,12 @@
         return mIsEnabled;
     }
 
+    @NonNull
+    @ComplicationData.ComplicationType
+    public int getCurrentType() {
+        return mCurrentType;
+    }
+
     /** Serializes this ComplicationDetails to the specified {@link Parcel}. */
     @Override
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
diff --git a/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/RenderParametersWireFormat.java b/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/RenderParametersWireFormat.java
index 4fec0b0..01a506d 100644
--- a/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/RenderParametersWireFormat.java
+++ b/wear/wear-watchface-data/src/main/java/androidx/wear/watchface/data/RenderParametersWireFormat.java
@@ -20,6 +20,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
@@ -39,7 +40,6 @@
 @VersionedParcelize
 @SuppressLint("BanParcelableUsage") // TODO(b/169214666): Remove Parcelable
 public class RenderParametersWireFormat implements VersionedParcelable, Parcelable {
-    /** */
     private static final int NO_COMPLICATION_ID = -1;
 
     /** Wire format for {@link androidx.wear.watchface.DrawMode}. */
@@ -55,6 +55,12 @@
     int mHighlightedComplicationId;
 
     /**
+     * Specifies the tint for any highlight.
+     */
+    @ParcelField(3)
+    int mHighlightTint;
+
+    /**
      * Wire format for Map<{@link androidx.wear.watchface.style.Layer},
      * {@link androidx.wear.watchface.LayerMode}>.
      *
@@ -66,18 +72,19 @@
     @ParcelField(100)
     List<LayerParameterWireFormat> mLayerParameters;
 
-
     RenderParametersWireFormat() {
     }
 
     public RenderParametersWireFormat(
             int drawMode,
             @NonNull List<LayerParameterWireFormat> layerParameters,
-            @Nullable Integer highlightedComplicationId) {
+            @Nullable Integer highlightedComplicationId,
+            @ColorInt int highlightTint) {
         mDrawMode = drawMode;
         mLayerParameters = layerParameters;
         mHighlightedComplicationId = (highlightedComplicationId != null)
                 ? highlightedComplicationId : NO_COMPLICATION_ID;
+        mHighlightTint = highlightTint;
     }
 
     public int getDrawMode() {
@@ -90,6 +97,11 @@
                 mHighlightedComplicationId;
     }
 
+    @ColorInt
+    public int getHighlightTint() {
+        return mHighlightTint;
+    }
+
     @NonNull
     public List<LayerParameterWireFormat> getLayerParameters() {
         return mLayerParameters;
diff --git a/wear/wear-watchface/api/current.txt b/wear/wear-watchface/api/current.txt
index d3c5678..4fb343b 100644
--- a/wear/wear-watchface/api/current.txt
+++ b/wear/wear-watchface/api/current.txt
@@ -15,7 +15,7 @@
 
   public class CanvasComplicationDrawable implements androidx.wear.watchface.CanvasComplication {
     ctor public CanvasComplicationDrawable(androidx.wear.watchface.complications.rendering.ComplicationDrawable drawable, androidx.wear.watchface.WatchState watchState);
-    method public void drawOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, android.icu.util.Calendar calendar);
+    method public void drawOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, android.icu.util.Calendar calendar, @ColorInt int color);
     method public final androidx.wear.watchface.complications.rendering.ComplicationDrawable getDrawable();
     method public androidx.wear.complications.data.IdAndComplicationData? getIdAndData();
     method @UiThread public boolean isHighlighted();
@@ -67,12 +67,12 @@
 
   public final class ComplicationOutlineRenderer {
     ctor public ComplicationOutlineRenderer();
-    method public static void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds);
+    method public static void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, @ColorInt int color);
     field public static final androidx.wear.watchface.ComplicationOutlineRenderer.Companion Companion;
   }
 
   public static final class ComplicationOutlineRenderer.Companion {
-    method public void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds);
+    method public void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, @ColorInt int color);
   }
 
   public final class ComplicationsManager {
@@ -138,11 +138,13 @@
   }
 
   public final class RenderParameters {
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Map<androidx.wear.watchface.style.Layer,? extends androidx.wear.watchface.LayerMode> layerParameters, Integer? highlightedComplicationId);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Map<androidx.wear.watchface.style.Layer,? extends androidx.wear.watchface.LayerMode> layerParameters, Integer? highlightedComplicationId, @ColorInt int highlightTint);
     method public androidx.wear.watchface.DrawMode getDrawMode();
+    method public int getHighlightTint();
     method public Integer? getHighlightedComplicationId();
     method public java.util.Map<androidx.wear.watchface.style.Layer,androidx.wear.watchface.LayerMode> getLayerParameters();
     property public final androidx.wear.watchface.DrawMode drawMode;
+    property public final int highlightTint;
     property public final Integer? highlightedComplicationId;
     property public final java.util.Map<androidx.wear.watchface.style.Layer,androidx.wear.watchface.LayerMode> layerParameters;
     field public static final androidx.wear.watchface.RenderParameters.Companion Companion;
diff --git a/wear/wear-watchface/api/public_plus_experimental_current.txt b/wear/wear-watchface/api/public_plus_experimental_current.txt
index d3c5678..4fb343b 100644
--- a/wear/wear-watchface/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface/api/public_plus_experimental_current.txt
@@ -15,7 +15,7 @@
 
   public class CanvasComplicationDrawable implements androidx.wear.watchface.CanvasComplication {
     ctor public CanvasComplicationDrawable(androidx.wear.watchface.complications.rendering.ComplicationDrawable drawable, androidx.wear.watchface.WatchState watchState);
-    method public void drawOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, android.icu.util.Calendar calendar);
+    method public void drawOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, android.icu.util.Calendar calendar, @ColorInt int color);
     method public final androidx.wear.watchface.complications.rendering.ComplicationDrawable getDrawable();
     method public androidx.wear.complications.data.IdAndComplicationData? getIdAndData();
     method @UiThread public boolean isHighlighted();
@@ -67,12 +67,12 @@
 
   public final class ComplicationOutlineRenderer {
     ctor public ComplicationOutlineRenderer();
-    method public static void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds);
+    method public static void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, @ColorInt int color);
     field public static final androidx.wear.watchface.ComplicationOutlineRenderer.Companion Companion;
   }
 
   public static final class ComplicationOutlineRenderer.Companion {
-    method public void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds);
+    method public void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, @ColorInt int color);
   }
 
   public final class ComplicationsManager {
@@ -138,11 +138,13 @@
   }
 
   public final class RenderParameters {
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Map<androidx.wear.watchface.style.Layer,? extends androidx.wear.watchface.LayerMode> layerParameters, Integer? highlightedComplicationId);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Map<androidx.wear.watchface.style.Layer,? extends androidx.wear.watchface.LayerMode> layerParameters, Integer? highlightedComplicationId, @ColorInt int highlightTint);
     method public androidx.wear.watchface.DrawMode getDrawMode();
+    method public int getHighlightTint();
     method public Integer? getHighlightedComplicationId();
     method public java.util.Map<androidx.wear.watchface.style.Layer,androidx.wear.watchface.LayerMode> getLayerParameters();
     property public final androidx.wear.watchface.DrawMode drawMode;
+    property public final int highlightTint;
     property public final Integer? highlightedComplicationId;
     property public final java.util.Map<androidx.wear.watchface.style.Layer,androidx.wear.watchface.LayerMode> layerParameters;
     field public static final androidx.wear.watchface.RenderParameters.Companion Companion;
diff --git a/wear/wear-watchface/api/restricted_current.txt b/wear/wear-watchface/api/restricted_current.txt
index 6eddf21..3ea9707 100644
--- a/wear/wear-watchface/api/restricted_current.txt
+++ b/wear/wear-watchface/api/restricted_current.txt
@@ -15,7 +15,7 @@
 
   public class CanvasComplicationDrawable implements androidx.wear.watchface.CanvasComplication {
     ctor public CanvasComplicationDrawable(androidx.wear.watchface.complications.rendering.ComplicationDrawable drawable, androidx.wear.watchface.WatchState watchState);
-    method public void drawOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, android.icu.util.Calendar calendar);
+    method public void drawOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, android.icu.util.Calendar calendar, @ColorInt int color);
     method public final androidx.wear.watchface.complications.rendering.ComplicationDrawable getDrawable();
     method public androidx.wear.complications.data.IdAndComplicationData? getIdAndData();
     method @UiThread public boolean isHighlighted();
@@ -67,12 +67,12 @@
 
   public final class ComplicationOutlineRenderer {
     ctor public ComplicationOutlineRenderer();
-    method public static void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds);
+    method public static void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, @ColorInt int color);
     field public static final androidx.wear.watchface.ComplicationOutlineRenderer.Companion Companion;
   }
 
   public static final class ComplicationOutlineRenderer.Companion {
-    method public void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds);
+    method public void drawComplicationSelectOutline(android.graphics.Canvas canvas, android.graphics.Rect bounds, @ColorInt int color);
   }
 
   public final class ComplicationsManager {
@@ -163,13 +163,15 @@
   }
 
   public final class RenderParameters {
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Map<androidx.wear.watchface.style.Layer,? extends androidx.wear.watchface.LayerMode> layerParameters, Integer? highlightedComplicationId);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Map<androidx.wear.watchface.style.Layer,? extends androidx.wear.watchface.LayerMode> layerParameters, Integer? highlightedComplicationId, @ColorInt int highlightTint);
     ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public RenderParameters(androidx.wear.watchface.data.RenderParametersWireFormat wireFormat);
     method public androidx.wear.watchface.DrawMode getDrawMode();
+    method public int getHighlightTint();
     method public Integer? getHighlightedComplicationId();
     method public java.util.Map<androidx.wear.watchface.style.Layer,androidx.wear.watchface.LayerMode> getLayerParameters();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.wear.watchface.data.RenderParametersWireFormat toWireFormat();
     property public final androidx.wear.watchface.DrawMode drawMode;
+    property public final int highlightTint;
     property public final Integer? highlightedComplicationId;
     property public final java.util.Map<androidx.wear.watchface.style.Layer,androidx.wear.watchface.LayerMode> layerParameters;
     field public static final androidx.wear.watchface.RenderParameters.Companion Companion;
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasAnalogWatchFaceService.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasAnalogWatchFaceService.kt
index 98f48df..26d6d79 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasAnalogWatchFaceService.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasAnalogWatchFaceService.kt
@@ -31,7 +31,7 @@
     private val handler: Handler,
     var mockSystemTimeMillis: Long,
     var surfaceHolderOverride: SurfaceHolder,
-    var userUnlocked: Boolean
+    var preRInitFlow: Boolean
 ) : WatchFaceService() {
 
     private val mutableWatchState = MutableWatchState().apply {
@@ -68,5 +68,5 @@
 
     override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
 
-    override fun isUserUnlocked() = userUnlocked
+    override fun expectPreRInitFlow() = preRInitFlow
 }
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 0e28d2f..95a9ebb 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
@@ -19,6 +19,7 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.graphics.Color
 import android.support.wearable.watchface.SharedMemoryImage
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -88,7 +89,8 @@
                     RenderParameters(
                         DrawMode.INTERACTIVE,
                         RenderParameters.DRAW_ALL_LAYERS,
-                        null
+                        null,
+                        Color.RED
                     ).toWireFormat(),
                     100,
                     1234567890,
@@ -128,7 +130,8 @@
                     RenderParameters(
                         DrawMode.AMBIENT,
                         RenderParameters.DRAW_ALL_LAYERS,
-                        null
+                        null,
+                        Color.RED
                     ).toWireFormat(),
                     100,
                     123456789,
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 2c9375c..6d0daab 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
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.graphics.Bitmap
 import android.graphics.Canvas
+import android.graphics.Color
 import android.graphics.Rect
 import android.graphics.SurfaceTexture
 import android.os.Handler
@@ -248,7 +249,8 @@
                         RenderParameters(
                             DrawMode.AMBIENT,
                             RenderParameters.DRAW_ALL_LAYERS,
-                            null
+                            null,
+                            Color.RED
                         ).toWireFormat(),
                         100,
                         123456789,
@@ -281,7 +283,8 @@
                         RenderParameters(
                             DrawMode.INTERACTIVE,
                             RenderParameters.DRAW_ALL_LAYERS,
-                            null
+                            null,
+                            Color.RED
                         ).toWireFormat(),
                         100,
                         123456789,
@@ -333,7 +336,8 @@
                                 Layer.COMPLICATIONS to LayerMode.DRAW_HIGHLIGHTED,
                                 Layer.TOP_LAYER to LayerMode.DRAW
                             ),
-                            null
+                            null,
+                            Color.RED
                         ).toWireFormat(),
                         100,
                         123456789,
@@ -370,7 +374,8 @@
                                 Layer.COMPLICATIONS to LayerMode.DRAW_HIGHLIGHTED,
                                 Layer.TOP_LAYER to LayerMode.DRAW
                             ),
-                            EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
+                            EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID,
+                            Color.RED
                         ).toWireFormat(),
                         100,
                         123456789,
@@ -418,7 +423,8 @@
                         RenderParameters(
                             DrawMode.INTERACTIVE,
                             RenderParameters.DRAW_ALL_LAYERS,
-                            null
+                            null,
+                            Color.RED
                         ).toWireFormat(),
                         100,
                         123456789,
@@ -450,9 +456,9 @@
             // Simulate device shutting down.
             InteractiveInstanceManager.deleteInstance(INTERACTIVE_INSTANCE_ID)
 
-            // Simulate a direct boot scenario where a new service is created with a locked user
-            // but there's no pending PendingWallpaperInteractiveWatchFaceInstance and no
-            // wallpaper command. This should load the direct boot parameters which get saved.
+            // Simulate a R style direct boot scenario where a new service is created but there's no
+            // pending PendingWallpaperInteractiveWatchFaceInstance and no wallpaper command. This
+            // should load the direct boot parameters which get saved.
             val service2 = TestCanvasAnalogWatchFaceService(
                 ApplicationProvider.getApplicationContext<Context>(),
                 handler,
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/BroadcastReceivers.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/BroadcastReceivers.kt
new file mode 100644
index 0000000..78f928f
--- /dev/null
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/BroadcastReceivers.kt
@@ -0,0 +1,162 @@
+/*
+ * 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
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import androidx.annotation.UiThread
+import androidx.annotation.VisibleForTesting
+
+/**
+ * All watchface instances share the same [Context] which is a problem for broadcast receivers
+ * because the OS will mistakenly believe we're leaking them if there's more than one instance. So
+ * we need to use this class to share them.
+ */
+internal class BroadcastReceivers private constructor(private val context: Context) {
+
+    interface BroadcastEventObserver {
+        /** Called when we receive Intent.ACTION_TIME_TICK. */
+        @UiThread
+        fun onActionTimeTick()
+
+        /** Called when we receive Intent.ACTION_TIMEZONE_CHANGED. */
+        @UiThread
+        fun onActionTimeZoneChanged()
+
+        /** Called when we receive Intent.ACTION_TIME_CHANGED. */
+        @UiThread
+        fun onActionTimeChanged()
+
+        /** Called when we receive Intent.ACTION_BATTERY_CHANGED. */
+        @UiThread
+        fun onActionBatteryChanged(intent: Intent)
+
+        /** Called when we receive Intent.MOCK_TIME_INTENT. */
+        @UiThread
+        fun onMockTime(intent: Intent)
+    }
+
+    companion object {
+        val broadcastEventObservers = HashSet<BroadcastEventObserver>()
+
+        /* We don't leak due to balanced calls to[addBroadcastEventObserver] and
+        [removeBroadcastEventObserver] which sets this back to null.
+         */
+        @SuppressWarnings("StaticFieldLeak")
+        var broadcastReceivers: BroadcastReceivers? = null
+
+        @UiThread
+        fun addBroadcastEventObserver(context: Context, observer: BroadcastEventObserver) {
+            broadcastEventObservers.add(observer)
+            if (broadcastReceivers == null) {
+                broadcastReceivers = BroadcastReceivers(context)
+            }
+        }
+
+        @UiThread
+        fun removeBroadcastEventObserver(observer: BroadcastEventObserver) {
+            broadcastEventObservers.remove(observer)
+            if (broadcastEventObservers.isEmpty()) {
+                broadcastReceivers!!.onDestroy()
+                broadcastReceivers = null
+            }
+        }
+
+        @VisibleForTesting
+        fun sendOnActionBatteryChangedForTesting(intent: Intent) {
+            require(broadcastEventObservers.isNotEmpty())
+            for (observer in broadcastEventObservers) {
+                observer.onActionBatteryChanged(intent)
+            }
+        }
+
+        @VisibleForTesting
+        fun sendOnMockTimeForTesting(intent: Intent) {
+            require(broadcastEventObservers.isNotEmpty())
+            for (observer in broadcastEventObservers) {
+                observer.onMockTime(intent)
+            }
+        }
+    }
+
+    private val actionTimeTickReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        @SuppressWarnings("SyntheticAccessor")
+        override fun onReceive(context: Context, intent: Intent) {
+            for (observer in broadcastEventObservers) {
+                observer.onActionTimeTick()
+            }
+        }
+    }
+
+    private val actionTimeZoneReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            for (observer in broadcastEventObservers) {
+                observer.onActionTimeZoneChanged()
+            }
+        }
+    }
+
+    private val actionTimeReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context?, intent: Intent?) {
+            for (observer in broadcastEventObservers) {
+                observer.onActionTimeChanged()
+            }
+        }
+    }
+
+    private val actionBatteryLevelReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        @SuppressWarnings("SyntheticAccessor")
+        override fun onReceive(context: Context, intent: Intent) {
+            for (observer in broadcastEventObservers) {
+                observer.onActionBatteryChanged(intent)
+            }
+        }
+    }
+
+    private val mockTimeReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        @SuppressWarnings("SyntheticAccessor")
+        override fun onReceive(context: Context, intent: Intent) {
+            for (observer in broadcastEventObservers) {
+                observer.onMockTime(intent)
+            }
+        }
+    }
+
+    init {
+        context.registerReceiver(actionTimeTickReceiver, IntentFilter(Intent.ACTION_TIME_TICK))
+        context.registerReceiver(
+            actionTimeZoneReceiver,
+            IntentFilter(Intent.ACTION_TIMEZONE_CHANGED)
+        )
+        context.registerReceiver(actionTimeReceiver, IntentFilter(Intent.ACTION_TIME_CHANGED))
+        context.registerReceiver(
+            actionBatteryLevelReceiver,
+            IntentFilter(Intent.ACTION_BATTERY_CHANGED)
+        )
+        context.registerReceiver(mockTimeReceiver, IntentFilter(WatchFaceImpl.MOCK_TIME_INTENT))
+    }
+
+    fun onDestroy() {
+        context.unregisterReceiver(actionTimeTickReceiver)
+        context.unregisterReceiver(actionTimeZoneReceiver)
+        context.unregisterReceiver(actionTimeReceiver)
+        context.unregisterReceiver(actionBatteryLevelReceiver)
+        context.unregisterReceiver(mockTimeReceiver)
+    }
+}
\ No newline at end of file
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 8454576..50128ac 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
@@ -23,6 +23,7 @@
 import android.graphics.drawable.Drawable
 import android.icu.util.Calendar
 import android.support.wearable.complications.ComplicationData
+import androidx.annotation.ColorInt
 import androidx.annotation.UiThread
 import androidx.wear.complications.ComplicationBounds
 import androidx.wear.complications.ComplicationHelperActivity
@@ -158,12 +159,16 @@
                 drawable.bounds = bounds
                 drawable.currentTimeMillis = calendar.timeInMillis
                 drawable.draw(canvas)
-                // If [RenderParameters.highlightedComplicationId] is set then only highlight if
-                // the ids match.
-                if (renderParameters.highlightedComplicationId == null ||
-                    renderParameters.highlightedComplicationId == idAndData?.complicationId
-                ) {
-                    drawOutline(canvas, bounds, calendar)
+
+                // It's only sensible to render a highlight for non-background complications.
+                if (attachedComplication?.boundsType != ComplicationBoundsType.BACKGROUND) {
+                    // If [RenderParameters.highlightedComplicationId] is set then only highlight if
+                    // the ids match.
+                    if (renderParameters.highlightedComplicationId == null ||
+                        renderParameters.highlightedComplicationId == idAndData?.complicationId
+                    ) {
+                        drawOutline(canvas, bounds, calendar, renderParameters.highlightTint)
+                    }
                 }
             }
             LayerMode.HIDE -> return
@@ -174,9 +179,14 @@
     public open fun drawOutline(
         canvas: Canvas,
         bounds: Rect,
-        calendar: Calendar
+        calendar: Calendar,
+        @ColorInt color: Int
     ) {
-        ComplicationOutlineRenderer.drawComplicationSelectOutline(canvas, bounds)
+        ComplicationOutlineRenderer.drawComplicationSelectOutline(
+            canvas,
+            bounds,
+            color
+        )
     }
 
     /**
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationOutlineRenderer.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationOutlineRenderer.kt
index 4d5bb5d..9a9acd6 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationOutlineRenderer.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationOutlineRenderer.kt
@@ -17,99 +17,53 @@
 package androidx.wear.watchface
 
 import android.graphics.Canvas
-import android.graphics.Color
 import android.graphics.Paint
 import android.graphics.Rect
-import kotlin.math.cos
-import kotlin.math.sin
+import android.graphics.RectF
+import androidx.annotation.ColorInt
 
 /**
- * Helper for rendering a dashed outline around a complication. Intended for use with
+ * Helper for rendering a thick outline around a complication. Intended for use with
  * [LayerMode#DRAW_HIGHLIGHTED].
  */
 public class ComplicationOutlineRenderer {
     public companion object {
-        // Dashed lines are used for complication selection.
-        internal const val DASH_WIDTH = 10.0f
-        internal const val DASH_GAP = 2.0f
-        internal const val DASH_LENGTH = 5.0f
-
-        internal val dashPaint = Paint().apply {
-            strokeWidth = DASH_WIDTH
-            style = Paint.Style.FILL_AND_STROKE
+        internal const val EXPANSION_PX = 6
+        internal const val STROKE_WIDTH = 3.0f
+        internal val outlinePaint = Paint().apply {
+            style = Paint.Style.STROKE
+            strokeWidth = STROKE_WIDTH
             isAntiAlias = true
-            color = Color.RED
         }
 
-        /** Draws a thick dotted line around the complication with the given bounds. */
+        /** Draws a thick line around the complication with the given bounds. */
         @JvmStatic
-        public fun drawComplicationSelectOutline(canvas: Canvas, bounds: Rect) {
-            if (bounds.width() == bounds.height()) {
-                drawCircleDashBorder(canvas, bounds)
-                return
-            }
-            val radius = bounds.height() / 2.0f
-
-            // Draw left arc dash.
-            var cx = bounds.left + radius
-            var cy = bounds.centerY().toFloat()
-            var startAngle = (Math.PI / 2.0f).toFloat()
-            val dashCount = (Math.PI * radius / (DASH_WIDTH + DASH_GAP)).toInt()
-            drawArcDashBorder(canvas, cx, cy, radius, startAngle, DASH_LENGTH, dashCount)
-
-            // Draw right arc dash.
-            cx = bounds.right - radius
-            cy = bounds.centerY().toFloat()
-            startAngle = (Math.PI / 2.0f).toFloat() * 3.0f
-            drawArcDashBorder(canvas, cx, cy, radius, startAngle, DASH_LENGTH, dashCount)
-
-            // Draw straight line dash.
-            val rectangleWidth = bounds.width() - 2.0f * radius - 2.0f * DASH_GAP
-            val cnt = (rectangleWidth / (DASH_WIDTH + DASH_GAP)).toInt()
-            val baseX: Float = bounds.left + radius + DASH_GAP
-            val fixGap: Float = (rectangleWidth - cnt * DASH_WIDTH) / (cnt - 1)
-            for (i in 0 until cnt) {
-                val startX: Float = baseX + i * (fixGap + DASH_WIDTH) + DASH_WIDTH / 2
-                var startY = bounds.top.toFloat()
-                var endY: Float = bounds.top - DASH_LENGTH
-                canvas.drawLine(startX, startY, startX, endY, dashPaint)
-                startY = bounds.bottom.toFloat()
-                endY = startY + DASH_LENGTH
-                canvas.drawLine(startX, startY, startX, endY, dashPaint)
-            }
-        }
-
-        internal fun drawArcDashBorder(
+        public fun drawComplicationSelectOutline(
             canvas: Canvas,
-            cx: Float,
-            cy: Float,
-            r: Float,
-            startAngle: Float,
-            dashLength: Float,
-            dashCount: Int
+            bounds: Rect,
+            @ColorInt color: Int
         ) {
-            for (i in 0 until dashCount) {
-                val rot = (2.0 * Math.PI / (2.0 * (dashCount - 1).toDouble()) * i + startAngle)
-                val startX = (r * cos(rot)).toFloat() + cx
-                val startY = (r * sin(rot)).toFloat() + cy
-                val endX = ((r + dashLength) * cos(rot).toFloat()) + cx
-                val endY = ((r + dashLength) * sin(rot).toFloat()) + cy
-                canvas.drawLine(startX, startY, endX, endY, dashPaint)
-            }
-        }
-
-        internal fun drawCircleDashBorder(canvas: Canvas, bounds: Rect) {
-            val radius = bounds.width() / 2.0f
-            val dashCount = (2.0 * Math.PI * radius / (DASH_WIDTH + DASH_GAP)).toInt()
-            val cx = bounds.exactCenterX()
-            val cy = bounds.exactCenterY()
-            for (i in 0 until dashCount) {
-                val rot = (i * 2.0 * Math.PI / dashCount)
-                val startX = (radius * cos(rot).toFloat()) + cx
-                val startY = (radius * sin(rot).toFloat()) + cy
-                val endX = ((radius + DASH_LENGTH) * cos(rot).toFloat()) + cx
-                val endY = ((radius + DASH_LENGTH) * sin(rot).toFloat()) + cy
-                canvas.drawLine(startX, startY, endX, endY, dashPaint)
+            outlinePaint.color = color
+            val radius = bounds.height() / 2.0f
+            if (bounds.width() == bounds.height()) {
+                canvas.drawCircle(
+                    bounds.exactCenterX() + 1.0f, // Offset necessary to properly center.
+                    bounds.exactCenterY(),
+                    radius + EXPANSION_PX,
+                    outlinePaint
+                )
+            } else {
+                canvas.drawRoundRect(
+                    RectF(
+                        (bounds.left - EXPANSION_PX).toFloat(),
+                        (bounds.top - EXPANSION_PX).toFloat(),
+                        (bounds.right + EXPANSION_PX).toFloat(),
+                        (bounds.bottom + EXPANSION_PX).toFloat()
+                    ),
+                    radius,
+                    radius,
+                    outlinePaint
+                )
             }
         }
     }
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ObservableWatchData.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ObservableWatchData.kt
index 52ab4b0..91e83df 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ObservableWatchData.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ObservableWatchData.kt
@@ -21,7 +21,7 @@
 /**
  * An observable UI thread only data holder class (see [Observer]).
  *
- * @param <T> The type of data hold by this instance
+ * @param T The type of data held by this instance
  */
 public open class ObservableWatchData<T : Any> internal constructor(internal var _value: T?) {
 
@@ -100,7 +100,7 @@
 /**
  * [ObservableWatchData] which publicly exposes [setValue(T)] method.
  *
- * @param <T> The type of data hold by this instance
+ * @param T The type of data held by this instance
  */
 public class MutableObservableWatchData<T : Any>(initialValue: T?) :
     ObservableWatchData<T>(initialValue) {
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/RenderParameters.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/RenderParameters.kt
index d102e50..696fb3a 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/RenderParameters.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/RenderParameters.kt
@@ -16,6 +16,8 @@
 
 package androidx.wear.watchface
 
+import android.graphics.Color
+import androidx.annotation.ColorInt
 import androidx.annotation.RestrictTo
 import androidx.wear.watchface.data.RenderParametersWireFormat
 import androidx.wear.watchface.style.Layer
@@ -48,9 +50,13 @@
     DRAW,
 
     /**
-     * This layer should be rendered with highlighting (used by the editor). See also
+     * This layer should be rendered with highlighting (used by the editor) using
+     * [RenderParameters.highlightTint]. See also
      * [RenderParameters.highlightedComplicationId] for use in combination with
      * [Layer.COMPLICATIONS].
+     *
+     * Note a highlight for background complications won't be drawn since this would typically be
+     * off screen.
      */
     DRAW_HIGHLIGHTED,
 
@@ -77,7 +83,11 @@
      */
     @SuppressWarnings("AutoBoxing")
     @get:SuppressWarnings("AutoBoxing")
-    public val highlightedComplicationId: Int?
+    public val highlightedComplicationId: Int?,
+
+    /** Specifies the tint should be used for highlights. */
+    @ColorInt
+    public val highlightTint: Int
 ) {
     public companion object {
         /** A layerParameters map where all Layers have [LayerMode.DRAW]. */
@@ -88,7 +98,7 @@
         /** Default RenderParameters which draws everything in interactive mode. */
         @JvmField
         public val DEFAULT_INTERACTIVE: RenderParameters =
-            RenderParameters(DrawMode.INTERACTIVE, DRAW_ALL_LAYERS, null)
+            RenderParameters(DrawMode.INTERACTIVE, DRAW_ALL_LAYERS, null, Color.RED)
     }
 
     /** @hide */
@@ -99,7 +109,8 @@
             { Layer.values()[it.layer] },
             { LayerMode.values()[it.layerMode] }
         ),
-        wireFormat.highlightedComplicationId
+        wireFormat.highlightedComplicationId,
+        wireFormat.highlightTint
     )
 
     /** @hide */
@@ -112,6 +123,7 @@
                 it.value.ordinal
             )
         },
-        highlightedComplicationId
+        highlightedComplicationId,
+        highlightTint
     )
 }
\ No newline at end of file
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
index bb4754b..9c62c0d 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
@@ -17,6 +17,7 @@
 package androidx.wear.watchface
 
 import android.annotation.SuppressLint
+import android.content.res.Configuration
 import android.graphics.Bitmap
 import android.graphics.Canvas
 import android.graphics.Color
@@ -135,8 +136,8 @@
     /**
      * The bounds of the [SurfaceHolder] this Renderer renders into. Depending on the shape of the
      * device's screen not all of these pixels may be visible to the user (see
-     * [WatchState.screenShape]).  Note also that API level 27+ devices draw indicators in the top
-     * and bottom 24dp of the screen, avoid rendering anything important there.
+     * [Configuration.isScreenRound]).  Note also that API level 27+ devices draw indicators in the
+     * top and bottom 24dp of the screen, avoid rendering anything important there.
      */
     public var screenBounds: Rect = surfaceHolder.surfaceFrame
         private set
@@ -249,8 +250,9 @@
     }
 
     /**
-     * Posts a message to schedule a call to [renderInternal] to draw the next frame. Unlike
-     * [invalidate], this method is thread-safe and may be called on any thread.
+     * Posts a message to schedule a call to either [CanvasRenderer.render] or [GlesRenderer.render]
+     * to draw the next frame. Unlike [invalidate], this method is thread-safe and may be called
+     * on any thread.
      */
     public fun postInvalidate() {
         if (this::watchFaceHostApi.isInitialized) {
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 a101a9c..bd3728c 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
@@ -18,11 +18,10 @@
 
 import android.annotation.SuppressLint
 import android.app.NotificationManager
-import android.content.BroadcastReceiver
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
-import android.content.IntentFilter
+import android.graphics.Color
 import android.graphics.Point
 import android.graphics.Rect
 import android.icu.util.Calendar
@@ -39,7 +38,6 @@
 import androidx.annotation.VisibleForTesting
 import androidx.wear.complications.SystemProviders
 import androidx.wear.complications.data.ComplicationData
-import androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle
 import androidx.wear.watchface.control.IInteractiveWatchFaceSysUI
 import androidx.wear.watchface.data.RenderParametersWireFormat
 import androidx.wear.watchface.style.UserStyle
@@ -331,35 +329,6 @@
     private val pendingPostDoubleTap: CancellableUniqueTask =
         CancellableUniqueTask(watchFaceHostApi.getHandler())
 
-    private val timeZoneReceiver: BroadcastReceiver = object : BroadcastReceiver() {
-        override fun onReceive(context: Context, intent: Intent) {
-            calendar.timeZone = TimeZone.getDefault()
-            watchFaceHostApi.invalidate()
-        }
-    }
-
-    private val timeReceiver: BroadcastReceiver = object : BroadcastReceiver() {
-        override fun onReceive(context: Context?, intent: Intent?) {
-            // System time has changed hence next scheduled draw is invalid.
-            nextDrawTimeMillis = systemTimeProvider.getSystemTimeMillis()
-            watchFaceHostApi.invalidate()
-        }
-    }
-
-    internal val batteryLevelReceiver: BroadcastReceiver = object : BroadcastReceiver() {
-        @SuppressWarnings("SyntheticAccessor")
-        override fun onReceive(context: Context, intent: Intent) {
-            val isBatteryLowAndNotCharging =
-                watchState.isBatteryLowAndNotCharging as MutableObservableWatchData
-            when (intent.action) {
-                Intent.ACTION_BATTERY_LOW -> isBatteryLowAndNotCharging.value = true
-                Intent.ACTION_BATTERY_OKAY -> isBatteryLowAndNotCharging.value = false
-                Intent.ACTION_POWER_CONNECTED -> isBatteryLowAndNotCharging.value = false
-            }
-            watchFaceHostApi.invalidate()
-        }
-    }
-
     private val componentName =
         ComponentName(
             watchFaceHostApi.getContext().packageName,
@@ -376,14 +345,42 @@
         legacyWatchFaceStyle.tapEventsAccepted
     )
 
-    /**
-     * We listen for MOCK_TIME_INTENTs which we interpret as a request to modify time. E.g. speeding
-     * up or slowing down time, and providing support for making time loop between two instants.
-     * This is intended to help implement animations which may occur infrequently (e.g. hourly).
-     */
-    internal val mockTimeReceiver: BroadcastReceiver = object : BroadcastReceiver() {
-        @SuppressWarnings("SyntheticAccessor")
-        override fun onReceive(context: Context, intent: Intent) {
+    private val broadcastEventObserver = object : BroadcastReceivers.BroadcastEventObserver {
+        override fun onActionTimeTick() {
+            if (!watchState.isAmbient.value) {
+                renderer.invalidate()
+            }
+        }
+
+        override fun onActionTimeZoneChanged() {
+            calendar.timeZone = TimeZone.getDefault()
+            renderer.invalidate()
+        }
+
+        override fun onActionTimeChanged() {
+            // System time has changed hence next scheduled draw is invalid.
+            nextDrawTimeMillis = systemTimeProvider.getSystemTimeMillis()
+            renderer.invalidate()
+        }
+
+        override fun onActionBatteryChanged(intent: Intent) {
+            val newValue = when (intent.action) {
+                Intent.ACTION_BATTERY_LOW -> true
+                Intent.ACTION_BATTERY_OKAY -> false
+                Intent.ACTION_POWER_CONNECTED -> false
+                else -> return // No change required.
+            }
+            val isBatteryLowAndNotCharging =
+                watchState.isBatteryLowAndNotCharging as MutableObservableWatchData
+            if (!isBatteryLowAndNotCharging.hasValue() ||
+                newValue != isBatteryLowAndNotCharging.value
+            ) {
+                isBatteryLowAndNotCharging.value = newValue
+                renderer.invalidate()
+            }
+        }
+
+        override fun onMockTime(intent: Intent) {
             mockTime.speed = intent.getFloatExtra(
                 EXTRA_MOCK_TIME_SPEED_MULTIPLIER,
                 MOCK_TIME_DEFAULT_SPEED_MULTIPLIER
@@ -561,21 +558,9 @@
             return
         }
         registeredReceivers = true
-        watchFaceHostApi.getContext().registerReceiver(
-            timeZoneReceiver,
-            IntentFilter(Intent.ACTION_TIMEZONE_CHANGED)
-        )
-        watchFaceHostApi.getContext().registerReceiver(
-            timeReceiver,
-            IntentFilter(Intent.ACTION_TIME_CHANGED)
-        )
-        watchFaceHostApi.getContext().registerReceiver(
-            batteryLevelReceiver,
-            IntentFilter(Intent.ACTION_BATTERY_CHANGED)
-        )
-        watchFaceHostApi.getContext().registerReceiver(
-            mockTimeReceiver,
-            IntentFilter(MOCK_TIME_INTENT)
+        BroadcastReceivers.addBroadcastEventObserver(
+            watchFaceHostApi.getContext(),
+            broadcastEventObserver
         )
     }
 
@@ -584,10 +569,7 @@
             return
         }
         registeredReceivers = false
-        watchFaceHostApi.getContext().unregisterReceiver(timeZoneReceiver)
-        watchFaceHostApi.getContext().unregisterReceiver(timeReceiver)
-        watchFaceHostApi.getContext().unregisterReceiver(batteryLevelReceiver)
-        watchFaceHostApi.getContext().unregisterReceiver(mockTimeReceiver)
+        BroadcastReceivers.removeBroadcastEventObserver(broadcastEventObserver)
     }
 
     private fun scheduleDraw() {
@@ -637,7 +619,12 @@
             newDrawMode = DrawMode.MUTE
         }
         renderer.renderParameters =
-            RenderParameters(newDrawMode, RenderParameters.DRAW_ALL_LAYERS, null)
+            RenderParameters(
+                newDrawMode,
+                RenderParameters.DRAW_ALL_LAYERS,
+                null,
+                Color.BLACK // Required by the constructor but unused.
+            )
     }
 
     /** @hide */
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 5a99ffc..da8e3e7 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
@@ -17,23 +17,21 @@
 package androidx.wear.watchface
 
 import android.annotation.SuppressLint
-import android.content.BroadcastReceiver
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
-import android.content.IntentFilter
 import android.graphics.Bitmap
 import android.graphics.Canvas
 import android.graphics.Rect
 import android.icu.util.Calendar
 import android.icu.util.TimeZone
+import android.os.Build
 import android.os.Bundle
 import android.os.Handler
 import android.os.Looper
 import android.os.PowerManager
 import android.os.RemoteException
 import android.os.Trace
-import android.os.UserManager
 import android.service.wallpaper.WallpaperService
 import android.support.wearable.watchface.Constants
 import android.support.wearable.watchface.IWatchFaceService
@@ -237,8 +235,9 @@
     // This is open for use by tests.
     internal open fun allowWatchFaceToAnimate() = true
 
+    // Whether or not the pre R style init flow (SET_BINDER wallpaper command) is expected.
     // This is open for use by tests.
-    internal open fun isUserUnlocked() = getSystemService(UserManager::class.java).isUserUnlocked
+    internal open fun expectPreRInitFlow() = Build.VERSION.SDK_INT < Build.VERSION_CODES.R
 
     /**
      * This is open for use by tests, it allows them to inject a custom [SurfaceHolder].
@@ -263,14 +262,6 @@
             isVisible.value = this@EngineWrapper.isVisible
         }
 
-        private var timeTickRegistered = false
-        private val timeTickReceiver: BroadcastReceiver = object : BroadcastReceiver() {
-            @SuppressWarnings("SyntheticAccessor")
-            override fun onReceive(context: Context, intent: Intent) {
-                watchFaceImpl.renderer.invalidate()
-            }
-        }
-
         /**
          * Whether or not we allow watchfaces to animate. In some tests or for headless
          * rendering (for remote config) we don't want this.
@@ -303,16 +294,6 @@
 
         private val invalidateRunnable = Runnable(this::invalidate)
 
-        private val ambientTimeTickFilter = IntentFilter().apply {
-            addAction(Intent.ACTION_DATE_CHANGED)
-            addAction(Intent.ACTION_TIME_CHANGED)
-            addAction(Intent.ACTION_TIMEZONE_CHANGED)
-        }
-
-        private val interactiveTimeTickFilter = IntentFilter(ambientTimeTickFilter).apply {
-            addAction(Intent.ACTION_TIME_TICK)
-        }
-
         // TODO(alexclarke): Figure out if we can remove this.
         private var pendingBackgroundAction: Bundle? = null
         private var pendingProperties: Bundle? = null
@@ -345,7 +326,7 @@
                 InteractiveInstanceManager.takePendingWallpaperInteractiveWatchFaceInstance()
 
             // In a direct boot scenario attempt to load the previously serialized parameters.
-            if (pendingWallpaperInstance == null && !isUserUnlocked()) {
+            if (pendingWallpaperInstance == null && !expectPreRInitFlow()) {
                 val params = readDirectBootPrefs(_context, DIRECT_BOOT_PREFS)
                 if (params != null) {
                     createInteractiveInstance(params).createWCSApi()
@@ -399,7 +380,6 @@
                 systemState.inAmbientMode != mutableWatchState.isAmbient.value
             ) {
                 mutableWatchState.isAmbient.value = systemState.inAmbientMode
-                updateTimeTickReceiver()
             }
 
             if (firstSetSystemState ||
@@ -458,7 +438,10 @@
                             it.value.defaultProviderPolicy.providersAsList(),
                             it.value.defaultProviderPolicy.systemProviderFallback,
                             it.value.defaultProviderType.asWireComplicationType(),
-                            it.value.enabled
+                            it.value.enabled,
+                            it.value.renderer.idAndData?.complicationData?.type
+                                ?.asWireComplicationType()
+                                ?: ComplicationType.NO_DATA.asWireComplicationType()
                         )
                     )
                 }
@@ -660,11 +643,6 @@
                 choreographer.removeFrameCallback(frameCallback)
             }
 
-            if (timeTickRegistered) {
-                timeTickRegistered = false
-                unregisterReceiver(timeTickReceiver)
-            }
-
             if (this::watchFaceImpl.isInitialized) {
                 watchFaceImpl.onDestroy()
             }
@@ -924,41 +902,6 @@
             pendingSetWatchFaceStyle = false
         }
 
-        /**
-         * Registers [timeTickReceiver] if it should be registered and isn't currently, or
-         * unregisters it if it shouldn't be registered but currently is. It also applies the right
-         * intent filter depending on whether we are in ambient mode or not.
-         */
-        internal fun updateTimeTickReceiver() {
-            // Separate calls are issued to deliver the state of isAmbient and isVisible, so during
-            // init we might not yet know the state of both.
-            if (!mutableWatchState.isAmbient.hasValue() ||
-                !mutableWatchState.isVisible.hasValue()
-            ) {
-                return
-            }
-
-            if (timeTickRegistered) {
-                unregisterReceiver(timeTickReceiver)
-                timeTickRegistered = false
-            }
-
-            // We only register if we are visible, otherwise it doesn't make sense to waste cycles.
-            if (mutableWatchState.isVisible.value) {
-                if (mutableWatchState.isAmbient.value) {
-                    registerReceiver(timeTickReceiver, ambientTimeTickFilter)
-                } else {
-                    registerReceiver(timeTickReceiver, interactiveTimeTickFilter)
-                }
-                timeTickRegistered = true
-
-                // In case we missed a tick while transitioning from ambient to interactive, we
-                // want to make sure the watch face doesn't show stale time when in interactive
-                // mode.
-                watchFaceImpl.renderer.invalidate()
-            }
-        }
-
         override fun onVisibilityChanged(visible: Boolean) {
             super.onVisibilityChanged(visible)
 
@@ -979,7 +922,6 @@
             }
 
             mutableWatchState.isVisible.value = visible
-            updateTimeTickReceiver()
             pendingVisibilityChanged = null
         }
 
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 e9666da..28a81c3 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
@@ -115,4 +115,10 @@
         uiThreadHandler.runOnHandler {
             engine.watchFaceImpl.userStyleRepository.schema.toWireFormat()
         }
+
+    override fun bringAttentionToComplication(id: Int) {
+        uiThreadHandler.runOnHandler {
+            engine.watchFaceImpl.complicationsManager.bringAttentionToComplication(id)
+        }
+    }
 }
\ No newline at end of file
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
index cd68cc9..c732a7c 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
@@ -31,9 +31,9 @@
 import androidx.wear.watchface.runOnHandler
 
 /**
- *  A service for creating and controlling WatchFaceInstances.
+ * A service for creating and controlling watch face instances.
  *
- *  @hide
+ * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
 @RequiresApi(27)
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 b8b0dec..2065aee 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
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.content.Intent
 import android.graphics.Canvas
+import android.graphics.Color
 import android.graphics.Rect
 import android.icu.util.Calendar
 import android.os.Bundle
@@ -196,7 +197,8 @@
                     Layer.COMPLICATIONS to LayerMode.DRAW_HIGHLIGHTED,
                     Layer.TOP_LAYER to LayerMode.DRAW
                 ),
-                null
+                null,
+                Color.RED
             ).toWireFormat()
         )
         canvas.drawBitmap(bitmap, drawRect, drawRect, null)
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
index 1165fbe9..7b3158c 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
@@ -106,6 +106,10 @@
     override fun getHandler() = handler
 
     override fun getMutableWatchState() = watchState
+
+    fun setIsVisible(isVisible: Boolean) {
+        watchState.isVisible.value = isVisible
+    }
 }
 
 /**
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 c8cd451..034a2c6 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
@@ -329,6 +329,7 @@
         sendImmutableProperties(engineWrapper, hasLowBitAmbient, hasBurnInProtection)
 
         watchFaceImpl = engineWrapper.watchFaceImpl
+        testWatchFaceService.setIsVisible(true)
     }
 
     private fun initWallpaperInteractiveWatchFaceInstance(
@@ -378,6 +379,7 @@
         // The [SurfaceHolder] must be sent before binding.
         engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
         watchFaceImpl = engineWrapper.watchFaceImpl
+        testWatchFaceService.setIsVisible(true)
     }
 
     private fun sendBinder(engine: WatchFaceService.EngineWrapper, apiVersion: Int) {
@@ -518,8 +520,7 @@
         watchState.isAmbient.value = false
         testWatchFaceService.mockSystemTimeMillis = 1000L
 
-        watchFaceImpl.mockTimeReceiver.onReceive(
-            context,
+        BroadcastReceivers.sendOnMockTimeForTesting(
             Intent(WatchFaceImpl.MOCK_TIME_INTENT).apply {
                 putExtra(WatchFaceImpl.EXTRA_MOCK_TIME_SPEED_MULTIPLIER, 2.0f)
                 putExtra(WatchFaceImpl.EXTRA_MOCK_TIME_WRAPPING_MIN_TIME, -1L)
@@ -546,8 +547,7 @@
         watchState.isAmbient.value = false
         testWatchFaceService.mockSystemTimeMillis = 1000L
 
-        watchFaceImpl.mockTimeReceiver.onReceive(
-            context,
+        BroadcastReceivers.sendOnMockTimeForTesting(
             Intent(WatchFaceImpl.MOCK_TIME_INTENT).apply {
                 putExtra(WatchFaceImpl.EXTRA_MOCK_TIME_SPEED_MULTIPLIER, 2.0f)
                 putExtra(WatchFaceImpl.EXTRA_MOCK_TIME_WRAPPING_MIN_TIME, 1000L)
@@ -875,19 +875,13 @@
         )
 
         // The delay should change when battery is low.
-        watchFaceImpl.batteryLevelReceiver.onReceive(
-            context,
-            Intent(Intent.ACTION_BATTERY_LOW)
-        )
+        BroadcastReceivers.sendOnActionBatteryChangedForTesting(Intent(Intent.ACTION_BATTERY_LOW))
         assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0)).isEqualTo(
             WatchFaceImpl.MAX_LOW_POWER_INTERACTIVE_UPDATE_RATE_MS
         )
 
         // And go back to normal when battery is OK.
-        watchFaceImpl.batteryLevelReceiver.onReceive(
-            context,
-            Intent(Intent.ACTION_BATTERY_OKAY)
-        )
+        BroadcastReceivers.sendOnActionBatteryChangedForTesting(Intent(Intent.ACTION_BATTERY_OKAY))
         assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0)).isEqualTo(
             INTERACTIVE_UPDATE_RATE_MS
         )
diff --git a/wear/wear/api/api_lint.ignore b/wear/wear/api/api_lint.ignore
index aedd625..e513a66 100644
--- a/wear/wear/api/api_lint.ignore
+++ b/wear/wear/api/api_lint.ignore
@@ -25,24 +25,6 @@
     Public class androidx.wear.widget.SwipeDismissController stripped of unavailable superclass androidx.wear.widget.DismissController
 
 
-MissingGetterMatchingBuilder: androidx.wear.ongoingactivity.OngoingActivity.Builder#setAnimatedIcon(android.graphics.drawable.Icon):
-    androidx.wear.ongoingactivity.OngoingActivity does not declare a `getAnimatedIcon()` method matching method androidx.wear.ongoingactivity.OngoingActivity.Builder.setAnimatedIcon(android.graphics.drawable.Icon)
-MissingGetterMatchingBuilder: androidx.wear.ongoingactivity.OngoingActivity.Builder#setAnimatedIcon(int):
-    androidx.wear.ongoingactivity.OngoingActivity does not declare a `getAnimatedIcon()` method matching method androidx.wear.ongoingactivity.OngoingActivity.Builder.setAnimatedIcon(int)
-MissingGetterMatchingBuilder: androidx.wear.ongoingactivity.OngoingActivity.Builder#setStaticIcon(android.graphics.drawable.Icon):
-    androidx.wear.ongoingactivity.OngoingActivity does not declare a `getStaticIcon()` method matching method androidx.wear.ongoingactivity.OngoingActivity.Builder.setStaticIcon(android.graphics.drawable.Icon)
-MissingGetterMatchingBuilder: androidx.wear.ongoingactivity.OngoingActivity.Builder#setStaticIcon(int):
-    androidx.wear.ongoingactivity.OngoingActivity does not declare a `getStaticIcon()` method matching method androidx.wear.ongoingactivity.OngoingActivity.Builder.setStaticIcon(int)
-MissingGetterMatchingBuilder: androidx.wear.ongoingactivity.OngoingActivity.Builder#setStatus(androidx.wear.ongoingactivity.OngoingActivityStatus):
-    androidx.wear.ongoingactivity.OngoingActivity does not declare a `getStatus()` method matching method androidx.wear.ongoingactivity.OngoingActivity.Builder.setStatus(androidx.wear.ongoingactivity.OngoingActivityStatus)
-MissingGetterMatchingBuilder: androidx.wear.ongoingactivity.OngoingActivity.Builder#setTouchIntent(android.app.PendingIntent):
-    androidx.wear.ongoingactivity.OngoingActivity does not declare a `getTouchIntent()` method matching method androidx.wear.ongoingactivity.OngoingActivity.Builder.setTouchIntent(android.app.PendingIntent)
-MissingGetterMatchingBuilder: androidx.wear.ongoingactivity.OngoingActivity.Builder#setLocusId(androidx.core.content.LocusIdCompat):
-    androidx.wear.ongoingactivity.OngoingActivity does not declare a `getLocusId()` method matching method androidx.wear.ongoingactivity.OngoingActivity.Builder.setLocusId(androidx.core.content.LocusIdCompat)
-MissingGetterMatchingBuilder: androidx.wear.ongoingactivity.OngoingActivity.Builder#setOngoingActivityId(int):
-    androidx.wear.ongoingactivity.OngoingActivity does not declare a `getOngoingActivityId()` method matching method androidx.wear.ongoingactivity.OngoingActivity.Builder.setOngoingActivityId(int)
-
-
 MissingNullability: androidx.wear.activity.ConfirmationActivity#onCreate(android.os.Bundle) parameter #0:
     Missing nullability on parameter `savedInstanceState` in method `onCreate`
 MissingNullability: androidx.wear.ambient.AmbientMode#attachAmbientSupport(T):
diff --git a/wear/wear/api/current.txt b/wear/wear/api/current.txt
index c6f4bf9..363b0a2 100644
--- a/wear/wear/api/current.txt
+++ b/wear/wear/api/current.txt
@@ -73,6 +73,7 @@
   public final class AmbientModeSupport.AmbientController {
     method public boolean isAmbient();
     method public void setAmbientOffloadEnabled(boolean);
+    method public void setAutoResumeEnabled(boolean);
   }
 
 }
@@ -92,6 +93,7 @@
     method public androidx.wear.ongoingactivity.OngoingActivity build();
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setAnimatedIcon(android.graphics.drawable.Icon);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setAnimatedIcon(@DrawableRes int);
+    method public androidx.wear.ongoingactivity.OngoingActivity.Builder setCategory(String);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setLocusId(androidx.core.content.LocusIdCompat);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setOngoingActivityId(int);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setStaticIcon(android.graphics.drawable.Icon);
@@ -103,24 +105,23 @@
   public class OngoingActivityData implements androidx.versionedparcelable.VersionedParcelable {
     method public static androidx.wear.ongoingactivity.OngoingActivityData? create(android.app.Notification);
     method public android.graphics.drawable.Icon? getAnimatedIcon();
+    method public String? getCategory();
     method public androidx.core.content.LocusIdCompat? getLocusId();
     method public int getOngoingActivityId();
     method public android.graphics.drawable.Icon getStaticIcon();
     method public androidx.wear.ongoingactivity.OngoingActivityStatus? getStatus();
+    method public long getTimestamp();
     method public android.app.PendingIntent getTouchIntent();
     method public static boolean hasOngoingActivity(android.app.Notification);
   }
 
-  public abstract class OngoingActivityStatus implements androidx.versionedparcelable.VersionedParcelable {
-    ctor public OngoingActivityStatus();
-    method public abstract long getNextChangeTimeMillis(long);
-    method public abstract CharSequence getText(android.content.Context, long);
+  public class OngoingActivityStatus implements androidx.versionedparcelable.VersionedParcelable {
+    method public long getNextChangeTimeMillis(long);
+    method public CharSequence getText(android.content.Context, long);
   }
 
   public class TextOngoingActivityStatus extends androidx.wear.ongoingactivity.OngoingActivityStatus {
     ctor public TextOngoingActivityStatus(String);
-    method public long getNextChangeTimeMillis(long);
-    method public CharSequence getText(android.content.Context, long);
   }
 
   public class TimerOngoingActivityStatus extends androidx.wear.ongoingactivity.OngoingActivityStatus {
@@ -128,9 +129,7 @@
     ctor public TimerOngoingActivityStatus(long, boolean, long);
     ctor public TimerOngoingActivityStatus(long, boolean);
     ctor public TimerOngoingActivityStatus(long);
-    method public long getNextChangeTimeMillis(long);
     method public long getPausedAtMillis();
-    method public CharSequence getText(android.content.Context, long);
     method public long getTimeZeroMillis();
     method public long getTotalDurationMillis();
     method public boolean hasTotalDuration();
diff --git a/wear/wear/api/public_plus_experimental_current.txt b/wear/wear/api/public_plus_experimental_current.txt
index ebd25b4..22878f3 100644
--- a/wear/wear/api/public_plus_experimental_current.txt
+++ b/wear/wear/api/public_plus_experimental_current.txt
@@ -73,6 +73,7 @@
   public final class AmbientModeSupport.AmbientController {
     method public boolean isAmbient();
     method public void setAmbientOffloadEnabled(boolean);
+    method public void setAutoResumeEnabled(boolean);
   }
 
 }
@@ -92,6 +93,7 @@
     method public androidx.wear.ongoingactivity.OngoingActivity build();
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setAnimatedIcon(android.graphics.drawable.Icon);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setAnimatedIcon(@DrawableRes int);
+    method public androidx.wear.ongoingactivity.OngoingActivity.Builder setCategory(String);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setLocusId(androidx.core.content.LocusIdCompat);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setOngoingActivityId(int);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setStaticIcon(android.graphics.drawable.Icon);
@@ -103,24 +105,23 @@
   @androidx.versionedparcelable.VersionedParcelize public class OngoingActivityData implements androidx.versionedparcelable.VersionedParcelable {
     method public static androidx.wear.ongoingactivity.OngoingActivityData? create(android.app.Notification);
     method public android.graphics.drawable.Icon? getAnimatedIcon();
+    method public String? getCategory();
     method public androidx.core.content.LocusIdCompat? getLocusId();
     method public int getOngoingActivityId();
     method public android.graphics.drawable.Icon getStaticIcon();
     method public androidx.wear.ongoingactivity.OngoingActivityStatus? getStatus();
+    method public long getTimestamp();
     method public android.app.PendingIntent getTouchIntent();
     method public static boolean hasOngoingActivity(android.app.Notification);
   }
 
-  @androidx.versionedparcelable.VersionedParcelize public abstract class OngoingActivityStatus implements androidx.versionedparcelable.VersionedParcelable {
-    ctor public OngoingActivityStatus();
-    method public abstract long getNextChangeTimeMillis(long);
-    method public abstract CharSequence getText(android.content.Context, long);
+  @androidx.versionedparcelable.VersionedParcelize public class OngoingActivityStatus implements androidx.versionedparcelable.VersionedParcelable {
+    method public long getNextChangeTimeMillis(long);
+    method public CharSequence getText(android.content.Context, long);
   }
 
   @androidx.versionedparcelable.VersionedParcelize public class TextOngoingActivityStatus extends androidx.wear.ongoingactivity.OngoingActivityStatus {
     ctor public TextOngoingActivityStatus(String);
-    method public long getNextChangeTimeMillis(long);
-    method public CharSequence getText(android.content.Context, long);
   }
 
   @androidx.versionedparcelable.VersionedParcelize public class TimerOngoingActivityStatus extends androidx.wear.ongoingactivity.OngoingActivityStatus {
@@ -128,9 +129,7 @@
     ctor public TimerOngoingActivityStatus(long, boolean, long);
     ctor public TimerOngoingActivityStatus(long, boolean);
     ctor public TimerOngoingActivityStatus(long);
-    method public long getNextChangeTimeMillis(long);
     method public long getPausedAtMillis();
-    method public CharSequence getText(android.content.Context, long);
     method public long getTimeZeroMillis();
     method public long getTotalDurationMillis();
     method public boolean hasTotalDuration();
diff --git a/wear/wear/api/restricted_current.txt b/wear/wear/api/restricted_current.txt
index 5640c27..9be0044 100644
--- a/wear/wear/api/restricted_current.txt
+++ b/wear/wear/api/restricted_current.txt
@@ -73,6 +73,7 @@
   public final class AmbientModeSupport.AmbientController {
     method public boolean isAmbient();
     method public void setAmbientOffloadEnabled(boolean);
+    method public void setAutoResumeEnabled(boolean);
   }
 
 }
@@ -92,6 +93,7 @@
     method public androidx.wear.ongoingactivity.OngoingActivity build();
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setAnimatedIcon(android.graphics.drawable.Icon);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setAnimatedIcon(@DrawableRes int);
+    method public androidx.wear.ongoingactivity.OngoingActivity.Builder setCategory(String);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setLocusId(androidx.core.content.LocusIdCompat);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setOngoingActivityId(int);
     method public androidx.wear.ongoingactivity.OngoingActivity.Builder setStaticIcon(android.graphics.drawable.Icon);
@@ -103,24 +105,23 @@
   @androidx.versionedparcelable.VersionedParcelize public class OngoingActivityData implements androidx.versionedparcelable.VersionedParcelable {
     method public static androidx.wear.ongoingactivity.OngoingActivityData? create(android.app.Notification);
     method public android.graphics.drawable.Icon? getAnimatedIcon();
+    method public String? getCategory();
     method public androidx.core.content.LocusIdCompat? getLocusId();
     method public int getOngoingActivityId();
     method public android.graphics.drawable.Icon getStaticIcon();
     method public androidx.wear.ongoingactivity.OngoingActivityStatus? getStatus();
+    method public long getTimestamp();
     method public android.app.PendingIntent getTouchIntent();
     method public static boolean hasOngoingActivity(android.app.Notification);
   }
 
-  @androidx.versionedparcelable.VersionedParcelize public abstract class OngoingActivityStatus implements androidx.versionedparcelable.VersionedParcelable {
-    ctor public OngoingActivityStatus();
-    method public abstract long getNextChangeTimeMillis(long);
-    method public abstract CharSequence getText(android.content.Context, long);
+  @androidx.versionedparcelable.VersionedParcelize public class OngoingActivityStatus implements androidx.versionedparcelable.VersionedParcelable {
+    method public long getNextChangeTimeMillis(long);
+    method public CharSequence getText(android.content.Context, long);
   }
 
   @androidx.versionedparcelable.VersionedParcelize public class TextOngoingActivityStatus extends androidx.wear.ongoingactivity.OngoingActivityStatus {
     ctor public TextOngoingActivityStatus(String);
-    method public long getNextChangeTimeMillis(long);
-    method public CharSequence getText(android.content.Context, long);
   }
 
   @androidx.versionedparcelable.VersionedParcelize public class TimerOngoingActivityStatus extends androidx.wear.ongoingactivity.OngoingActivityStatus {
@@ -128,9 +129,7 @@
     ctor public TimerOngoingActivityStatus(long, boolean, long);
     ctor public TimerOngoingActivityStatus(long, boolean);
     ctor public TimerOngoingActivityStatus(long);
-    method public long getNextChangeTimeMillis(long);
     method public long getPausedAtMillis();
-    method public CharSequence getText(android.content.Context, long);
     method public long getTimeZeroMillis();
     method public long getTotalDurationMillis();
     method public boolean hasTotalDuration();
diff --git a/wear/wear/build.gradle b/wear/wear/build.gradle
index eae997b..3c16a59 100644
--- a/wear/wear/build.gradle
+++ b/wear/wear/build.gradle
@@ -34,6 +34,8 @@
 
     implementation "androidx.core:core-ktx:1.5.0-alpha04"
 
+    annotationProcessor(project(":versionedparcelable:versionedparcelable-compiler"))
+
     compileOnly fileTree(dir: '../wear_stubs', include: ['com.google.android.wearable-stubs.jar'])
 }
 
@@ -58,4 +60,5 @@
     mavenVersion = LibraryVersions.WEAR
     inceptionYear = "2016"
     description = "Android Wear Support UI"
+    failOnDeprecationWarnings = false
 }
diff --git a/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportResumeTest.java b/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportResumeTest.java
index 2acbbcd..fe6c193 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportResumeTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportResumeTest.java
@@ -19,9 +19,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
-import androidx.test.rule.ActivityTestRule;
 import androidx.wear.widget.util.WakeLockRule;
 
 import com.google.android.wearable.compat.WearableActivityController;
@@ -37,12 +37,27 @@
     public final WakeLockRule mWakeLock = new WakeLockRule();
 
     @Rule
-    public final ActivityTestRule<AmbientModeSupportResumeTestActivity> mActivityRule =
-            new ActivityTestRule<>(AmbientModeSupportResumeTestActivity.class);
+    public final ActivityScenarioRule<AmbientModeSupportResumeTestActivity> mActivityRule =
+            new ActivityScenarioRule<>(AmbientModeSupportResumeTestActivity.class);
 
     @Test
-    public void testActivityDefaults() throws Throwable {
+    public void testActivityDefaults()  {
         assertTrue(WearableActivityController.getLastInstance().isAutoResumeEnabled());
         assertFalse(WearableActivityController.getLastInstance().isAmbientEnabled());
     }
+
+    @Test
+    public void testActivityAutoResume() {
+        assertTrue(WearableActivityController.getLastInstance().isAutoResumeEnabled());
+
+        // Test disable/enable auto resume with ambient mode disabled
+        assertFalse(WearableActivityController.getLastInstance().isAmbientEnabled());
+        mActivityRule.getScenario().onActivity(activity-> {
+            activity.getAmbientController().setAutoResumeEnabled(false);
+            assertFalse(WearableActivityController.getLastInstance().isAutoResumeEnabled());
+
+            activity.getAmbientController().setAutoResumeEnabled(true);
+            assertTrue(WearableActivityController.getLastInstance().isAutoResumeEnabled());
+        });
+    }
 }
diff --git a/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportResumeTestActivity.java b/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportResumeTestActivity.java
index 9571ae7..b7b49c9 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportResumeTestActivity.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportResumeTestActivity.java
@@ -27,4 +27,8 @@
         super.onCreate(savedInstanceState);
         mAmbientController = AmbientModeSupport.attach(this);
     }
+
+    public AmbientModeSupport.AmbientController getAmbientController() {
+        return mAmbientController;
+    }
 }
diff --git a/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportTest.java b/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportTest.java
index 271412e..4b15b7a 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/ambient/AmbientModeSupportTest.java
@@ -19,9 +19,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
 import androidx.wear.widget.util.WakeLockRule;
 
 import com.google.android.wearable.compat.WearableActivityController;
@@ -37,51 +37,51 @@
     public final WakeLockRule mWakeLock = new WakeLockRule();
 
     @Rule
-    public final ActivityTestRule<AmbientModeSupportTestActivity> mActivityRule =
-            new ActivityTestRule<>(AmbientModeSupportTestActivity.class);
+    public final ActivityScenarioRule<AmbientModeSupportTestActivity> mActivityRule =
+            new ActivityScenarioRule<>(AmbientModeSupportTestActivity.class);
 
     @Test
-    public void testEnterAmbientCallback() throws Throwable {
-        AmbientModeSupportTestActivity activity = mActivityRule.getActivity();
-
-        WearableActivityController.getLastInstance().enterAmbient();
-        assertTrue(activity.mEnterAmbientCalled);
-        assertFalse(activity.mUpdateAmbientCalled);
-        assertFalse(activity.mExitAmbientCalled);
-        assertFalse(activity.mAmbientOffloadInvalidatedCalled);
+    public void testEnterAmbientCallback() {
+        mActivityRule.getScenario().onActivity(activity-> {
+            WearableActivityController.getLastInstance().enterAmbient();
+            assertTrue(activity.mEnterAmbientCalled);
+            assertFalse(activity.mUpdateAmbientCalled);
+            assertFalse(activity.mExitAmbientCalled);
+            assertFalse(activity.mAmbientOffloadInvalidatedCalled);
+        });
     }
 
     @Test
-    public void testUpdateAmbientCallback() throws Throwable {
-        AmbientModeSupportTestActivity activity = mActivityRule.getActivity();
-
-        WearableActivityController.getLastInstance().updateAmbient();
-        assertFalse(activity.mEnterAmbientCalled);
-        assertTrue(activity.mUpdateAmbientCalled);
-        assertFalse(activity.mExitAmbientCalled);
-        assertFalse(activity.mAmbientOffloadInvalidatedCalled);
+    public void testUpdateAmbientCallback() {
+        mActivityRule.getScenario().onActivity(activity-> {
+            WearableActivityController.getLastInstance().updateAmbient();
+            assertFalse(activity.mEnterAmbientCalled);
+            assertTrue(activity.mUpdateAmbientCalled);
+            assertFalse(activity.mExitAmbientCalled);
+            assertFalse(activity.mAmbientOffloadInvalidatedCalled);
+        });
     }
 
     @Test
-    public void testExitAmbientCallback() throws Throwable {
-        AmbientModeSupportTestActivity activity = mActivityRule.getActivity();
-
-        WearableActivityController.getLastInstance().exitAmbient();
-        assertFalse(activity.mEnterAmbientCalled);
-        assertFalse(activity.mUpdateAmbientCalled);
-        assertTrue(activity.mExitAmbientCalled);
-        assertFalse(activity.mAmbientOffloadInvalidatedCalled);
+    public void testExitAmbientCallback() {
+        mActivityRule.getScenario().onActivity(activity-> {
+            WearableActivityController.getLastInstance().exitAmbient();
+            assertFalse(activity.mEnterAmbientCalled);
+            assertFalse(activity.mUpdateAmbientCalled);
+            assertTrue(activity.mExitAmbientCalled);
+            assertFalse(activity.mAmbientOffloadInvalidatedCalled);
+        });
     }
 
     @Test
-    public void testAmbientOffloadInvalidatedCallback() throws Throwable {
-        AmbientModeSupportTestActivity activity = mActivityRule.getActivity();
-
-        WearableActivityController.getLastInstance().invalidateAmbientOffload();
-        assertFalse(activity.mEnterAmbientCalled);
-        assertFalse(activity.mUpdateAmbientCalled);
-        assertFalse(activity.mExitAmbientCalled);
-        assertTrue(activity.mAmbientOffloadInvalidatedCalled);
+    public void testAmbientOffloadInvalidatedCallback() {
+        mActivityRule.getScenario().onActivity(activity-> {
+            WearableActivityController.getLastInstance().invalidateAmbientOffload();
+            assertFalse(activity.mEnterAmbientCalled);
+            assertFalse(activity.mUpdateAmbientCalled);
+            assertFalse(activity.mExitAmbientCalled);
+            assertTrue(activity.mAmbientOffloadInvalidatedCalled);
+        });
     }
 
     @Test
@@ -91,23 +91,38 @@
 
     @Test
     public void testCallsControllerIsAmbient() {
-        AmbientModeSupportTestActivity activity = mActivityRule.getActivity();
+        mActivityRule.getScenario().onActivity(activity-> {
+            WearableActivityController.getLastInstance().setAmbient(true);
+            assertTrue(activity.getAmbientController().isAmbient());
 
-        WearableActivityController.getLastInstance().setAmbient(true);
-        assertTrue(activity.getAmbientController().isAmbient());
-
-        WearableActivityController.getLastInstance().setAmbient(false);
-        assertFalse(activity.getAmbientController().isAmbient());
+            WearableActivityController.getLastInstance().setAmbient(false);
+            assertFalse(activity.getAmbientController().isAmbient());
+        });
     }
 
     @Test
     public void testEnableAmbientOffload() {
-        AmbientModeSupportTestActivity activity = mActivityRule.getActivity();
+        mActivityRule.getScenario().onActivity(activity-> {
+            activity.getAmbientController().setAmbientOffloadEnabled(true);
+            assertTrue(WearableActivityController.getLastInstance().isAmbientOffloadEnabled());
 
-        activity.getAmbientController().setAmbientOffloadEnabled(true);
-        assertTrue(WearableActivityController.getLastInstance().isAmbientOffloadEnabled());
+            activity.getAmbientController().setAmbientOffloadEnabled(false);
+            assertFalse(WearableActivityController.getLastInstance().isAmbientOffloadEnabled());
+        });
+    }
 
-        activity.getAmbientController().setAmbientOffloadEnabled(false);
-        assertFalse(WearableActivityController.getLastInstance().isAmbientOffloadEnabled());
+    @Test
+    public void testActivityEnableAutoResume() throws Throwable {
+        assertTrue(WearableActivityController.getLastInstance().isAutoResumeEnabled());
+
+        // Test disable/enable auto resume with ambient mode enabled
+        assertTrue(WearableActivityController.getLastInstance().isAmbientEnabled());
+        mActivityRule.getScenario().onActivity(activity-> {
+            activity.getAmbientController().setAutoResumeEnabled(false);
+            assertFalse(WearableActivityController.getLastInstance().isAutoResumeEnabled());
+
+            activity.getAmbientController().setAutoResumeEnabled(true);
+            assertTrue(WearableActivityController.getLastInstance().isAutoResumeEnabled());
+        });
     }
 }
diff --git a/wear/wear/src/androidTest/java/androidx/wear/widget/WearableRecyclerViewTest.java b/wear/wear/src/androidTest/java/androidx/wear/widget/WearableRecyclerViewTest.java
index 3158fbd..03e34be 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/widget/WearableRecyclerViewTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/widget/WearableRecyclerViewTest.java
@@ -28,7 +28,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import android.app.Activity;
 import android.content.res.Configuration;
 import android.view.View;
 
@@ -39,10 +38,10 @@
 import androidx.test.espresso.action.GeneralSwipeAction;
 import androidx.test.espresso.action.Press;
 import androidx.test.espresso.action.Swipe;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
 import androidx.wear.test.R;
 import androidx.wear.widget.util.WakeLockRule;
 
@@ -65,8 +64,8 @@
     public final WakeLockRule wakeLock = new WakeLockRule();
 
     @Rule
-    public final ActivityTestRule<WearableRecyclerViewTestActivity> mActivityRule =
-            new ActivityTestRule<>(WearableRecyclerViewTestActivity.class, true, true);
+    public final ActivityScenarioRule<WearableRecyclerViewTestActivity> mActivityRule =
+            new ActivityScenarioRule<>(WearableRecyclerViewTestActivity.class);
 
     @Before
     public void setUp() {
@@ -75,100 +74,118 @@
 
     @Test
     public void testCaseInitState() {
-        WearableRecyclerView wrv = new WearableRecyclerView(mActivityRule.getActivity());
-        wrv.setLayoutManager(new WearableLinearLayoutManager(wrv.getContext()));
+        mActivityRule.getScenario().onActivity(activity -> {
+            WearableRecyclerView wrv = new WearableRecyclerView(activity);
+            wrv.setLayoutManager(new WearableLinearLayoutManager(wrv.getContext()));
 
-        assertFalse(wrv.isEdgeItemsCenteringEnabled());
-        assertFalse(wrv.isCircularScrollingGestureEnabled());
-        assertEquals(1.0f, wrv.getBezelFraction(), 0.01f);
-        assertEquals(180.0f, wrv.getScrollDegreesPerScreen(), 0.01f);
+            assertFalse(wrv.isEdgeItemsCenteringEnabled());
+            assertFalse(wrv.isCircularScrollingGestureEnabled());
+            assertEquals(1.0f, wrv.getBezelFraction(), 0.01f);
+            assertEquals(180.0f, wrv.getScrollDegreesPerScreen(), 0.01f);
+        });
     }
 
     @Test
     public void testEdgeItemsCenteringOnAndOff() throws Throwable {
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                WearableRecyclerView wrv =
-                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
-                wrv.setEdgeItemsCenteringEnabled(true);
-            }
-        });
 
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                WearableRecyclerView wrv =
-                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
-                View child = wrv.getChildAt(0);
-                assertNotNull("child", child);
-                Activity activity = mActivityRule.getActivity();
-                Configuration configuration = activity.getResources().getConfiguration();
-                if (configuration.isScreenRound()) {
-                    assertEquals((wrv.getHeight() - child.getHeight()) / 2, child.getTop());
-                } else {
-                    assertEquals(0, child.getTop());
+        mActivityRule.getScenario().onActivity(activity -> {
+            activity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    WearableRecyclerView wrv =
+                            (WearableRecyclerView) activity.findViewById(R.id.wrv);
+                    wrv.setEdgeItemsCenteringEnabled(true);
                 }
-            }
+            });
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        mActivityRule.getScenario().onActivity(activity -> {
+            activity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    WearableRecyclerView wrv =
+                            (WearableRecyclerView) activity.findViewById(
+                                    R.id.wrv);
+                    View child = wrv.getChildAt(0);
+                    assertNotNull("child", child);
+                    Configuration configuration = activity.getResources().getConfiguration();
+                    if (configuration.isScreenRound()) {
+                        assertEquals((wrv.getHeight() - child.getHeight()) / 2, child.getTop());
+                    } else {
+                        assertEquals(0, child.getTop());
+                    }
+                }
+            });
         });
 
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                WearableRecyclerView wrv =
-                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
-                wrv.setEdgeItemsCenteringEnabled(false);
-            }
+        mActivityRule.getScenario().onActivity(activity -> {
+            activity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    WearableRecyclerView wrv =
+                            (WearableRecyclerView) activity.findViewById(
+                                    R.id.wrv);
+                    wrv.setEdgeItemsCenteringEnabled(false);
+                }
+            });
         });
 
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                WearableRecyclerView wrv =
-                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
-                View child = wrv.getChildAt(0);
-                assertNotNull("child", child);
-                assertEquals(0, child.getTop());
+        mActivityRule.getScenario().onActivity(activity -> {
+            activity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    WearableRecyclerView wrv =
+                            (WearableRecyclerView) activity.findViewById(
+                                    R.id.wrv);
+                    View child = wrv.getChildAt(0);
+                    assertNotNull("child", child);
+                    assertEquals(0, child.getTop());
 
-            }
+                }
+            });
         });
     }
 
     @Test
     public void testEdgeItemsCenteringBeforeChildrenDrawn() throws Throwable {
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                Activity activity = mActivityRule.getActivity();
-                WearableRecyclerView wrv = (WearableRecyclerView) activity.findViewById(R.id.wrv);
-                RecyclerView.Adapter<WearableRecyclerView.ViewHolder> adapter = wrv.getAdapter();
-                wrv.setAdapter(null);
-                wrv.setEdgeItemsCenteringEnabled(true);
-                wrv.setAdapter(adapter);
-            }
+        mActivityRule.getScenario().onActivity(activity -> {
+            activity.runOnUiThread(new Runnable() {
+                @Override
+                @SuppressWarnings("unchecked")
+                public void run() {
+                    WearableRecyclerView wrv = (WearableRecyclerView) activity.findViewById(
+                            R.id.wrv);
+                    RecyclerView.Adapter<WearableRecyclerView.ViewHolder> adapter =
+                            wrv.getAdapter();
+                    wrv.setAdapter(null);
+                    wrv.setEdgeItemsCenteringEnabled(true);
+                    wrv.setAdapter(adapter);
+                }
+            });
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                WearableRecyclerView wrv =
-                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
-                // Verify the first child
-                View child = wrv.getChildAt(0);
-                assertNotNull("child", child);
-                Activity activity = mActivityRule.getActivity();
-                Configuration configuration = activity.getResources().getConfiguration();
-                if (configuration.isScreenRound()) {
-                    assertEquals((wrv.getHeight() - child.getHeight()) / 2, child.getTop());
-                } else {
-                    assertEquals(0, child.getTop());
+        mActivityRule.getScenario().onActivity(activity -> {
+            activity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    WearableRecyclerView wrv =
+                            (WearableRecyclerView) activity.findViewById(
+                                    R.id.wrv);
+                    // Verify the first child
+                    View child = wrv.getChildAt(0);
+                    assertNotNull("child", child);
+                    Configuration configuration = activity.getResources().getConfiguration();
+                    if (configuration.isScreenRound()) {
+                        assertEquals((wrv.getHeight() - child.getHeight()) / 2, child.getTop());
+                    } else {
+                        assertEquals(0, child.getTop());
+                    }
                 }
-            }
+            });
         });
     }
 
@@ -176,20 +193,21 @@
     public void testCircularScrollingGesture() throws Throwable {
         onView(withId(R.id.wrv)).perform(swipeDownFromTopRight());
         assertNotScrolledY(R.id.wrv);
-        final WearableRecyclerView wrv =
-                (WearableRecyclerView) mActivityRule.getActivity().findViewById(
-                        R.id.wrv);
-        assertFalse(wrv.isCircularScrollingGestureEnabled());
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                WearableRecyclerView wrv = (WearableRecyclerView)
-                        mActivityRule.getActivity().findViewById(R.id.wrv);
-                wrv.setCircularScrollingGestureEnabled(true);
-            }
+        mActivityRule.getScenario().onActivity(activity -> {
+            final WearableRecyclerView wrv =
+                    (WearableRecyclerView) activity.findViewById(
+                            R.id.wrv);
+            assertFalse(wrv.isCircularScrollingGestureEnabled());
+            activity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    WearableRecyclerView wrv = (WearableRecyclerView)
+                            activity.findViewById(R.id.wrv);
+                    wrv.setCircularScrollingGestureEnabled(true);
+                }
+            });
+            assertTrue(wrv.isCircularScrollingGestureEnabled());
         });
-        assertTrue(wrv.isCircularScrollingGestureEnabled());
-
         // Explicitly set the swipe to SLOW here to avoid problems with test failures on phone AVDs
         // with "Gesture navigation" enabled. This is not a particularly satisfactory fix to this
         // problem and ideally we should look to move these tests to use a watch AVD which should
@@ -200,36 +218,39 @@
 
     @Test
     public void testCurvedOffsettingHelper() throws Throwable {
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                WearableRecyclerView wrv =
-                        (WearableRecyclerView) mActivityRule.getActivity().findViewById(R.id.wrv);
-                wrv.setLayoutManager(new WearableLinearLayoutManager(wrv.getContext()));
-            }
+        mActivityRule.getScenario().onActivity(activity -> {
+            activity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    WearableRecyclerView wrv =
+                            (WearableRecyclerView) activity.findViewById(
+                                    R.id.wrv);
+                    wrv.setLayoutManager(new WearableLinearLayoutManager(wrv.getContext()));
+                }
+            });
         });
-
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         onView(withId(R.id.wrv)).perform(swipeDownFromTopRight());
 
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                Activity activity = mActivityRule.getActivity();
-                WearableRecyclerView wrv = (WearableRecyclerView) activity.findViewById(R.id.wrv);
-                if (activity.getResources().getConfiguration().isScreenRound()) {
-                    View child = wrv.getChildAt(0);
-                    assertTrue(child.getLeft() > 0);
-                } else {
-                    for (int i = 0; i < wrv.getChildCount(); i++) {
-                        assertEquals(0, wrv.getChildAt(i).getLeft());
+        mActivityRule.getScenario().onActivity(activity -> {
+            activity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    WearableRecyclerView wrv = (WearableRecyclerView) activity.findViewById(
+                            R.id.wrv);
+                    if (activity.getResources().getConfiguration().isScreenRound()) {
+                        View child = wrv.getChildAt(0);
+                        assertTrue(child.getLeft() > 0);
+                    } else {
+                        for (int i = 0; i < wrv.getChildCount(); i++) {
+                            assertEquals(0, wrv.getChildAt(i).getLeft());
+                        }
                     }
                 }
-            }
+            });
         });
     }
-
     private static ViewAction swipeDownFromTopRightSlowly() {
         return new GeneralSwipeAction(
                 Swipe.SLOW, GeneralLocation.TOP_RIGHT,
diff --git a/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java b/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
index 9d48837..36f4a4a 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
@@ -701,6 +701,7 @@
      * Returns the first child of {@code root} to be an instance of class {@code T}, or {@code null}
      * if none were found.
      */
+    @SuppressWarnings("unchecked")
     @Nullable
     private <T> T getChildByType(View root, Class<T> classOfChildToFind) {
         for (View child : TreeIterables.breadthFirstViewTraversal(root)) {
diff --git a/wear/wear/src/main/java/androidx/wear/ambient/AmbientDelegate.java b/wear/wear/src/main/java/androidx/wear/ambient/AmbientDelegate.java
index fd6146c..b78af94 100644
--- a/wear/wear/src/main/java/androidx/wear/ambient/AmbientDelegate.java
+++ b/wear/wear/src/main/java/androidx/wear/ambient/AmbientDelegate.java
@@ -51,7 +51,7 @@
          * method. If they do not, an exception will be thrown.</em>
          *
          * @param ambientDetails bundle containing information about the display being used.
-         *                      It includes information about low-bit color and burn-in protection.
+         *                       It includes information about low-bit color and burn-in protection.
          */
         void onEnterAmbient(Bundle ambientDetails);
 
@@ -81,15 +81,15 @@
     }
 
     AmbientDelegate(@Nullable Activity activity,
-                           @NonNull WearableControllerProvider wearableControllerProvider,
-                           @NonNull AmbientCallback callback) {
+            @NonNull WearableControllerProvider wearableControllerProvider,
+            @NonNull AmbientCallback callback) {
         mActivity = new WeakReference<>(activity);
         mCallback = callback;
         mWearableControllerProvider = wearableControllerProvider;
     }
 
     /**
-     * Receives and handles the onCreate call from the associated {@link AmbientMode}
+     * Receives and handles the onCreate call from the associated {@link AmbientModeSupport}
      */
     void onCreate() {
         Activity activity = mActivity.get();
@@ -103,7 +103,7 @@
     }
 
     /**
-     * Receives and handles the onResume call from the associated {@link AmbientMode}
+     * Receives and handles the onResume call from the associated {@link AmbientModeSupport}
      */
     void onResume() {
         if (mWearableController != null) {
@@ -112,7 +112,7 @@
     }
 
     /**
-     * Receives and handles the onPause call from the associated {@link AmbientMode}
+     * Receives and handles the onPause call from the associated {@link AmbientModeSupport}
      */
     void onPause() {
         if (mWearableController != null) {
@@ -121,7 +121,7 @@
     }
 
     /**
-     * Receives and handles the onStop call from the associated {@link AmbientMode}
+     * Receives and handles the onStop call from the associated {@link AmbientModeSupport}
      */
     void onStop() {
         if (mWearableController != null) {
@@ -130,7 +130,7 @@
     }
 
     /**
-     * Receives and handles the onDestroy call from the associated {@link AmbientMode}
+     * Receives and handles the onDestroy call from the associated {@link AmbientModeSupport}
      */
     void onDestroy() {
         if (mWearableController != null) {
@@ -156,6 +156,18 @@
     }
 
     /**
+     * Sets whether this activity's task should be moved to the front when the system exits
+     * ambient mode. If true, the activity's task may be moved to the front if it was the last
+     * activity to be running when ambient started, depending on how much time the system spent
+     * in ambient mode.
+     */
+    public void setAutoResumeEnabled(boolean enabled) {
+        if (mWearableController != null) {
+            mWearableController.setAutoResumeEnabled(enabled);
+        }
+    }
+
+    /**
      * @return {@code true} if the activity is currently in ambient.
      */
     boolean isAmbient() {
diff --git a/wear/wear/src/main/java/androidx/wear/ambient/AmbientModeSupport.java b/wear/wear/src/main/java/androidx/wear/ambient/AmbientModeSupport.java
index b0eebcf..0b4b539 100644
--- a/wear/wear/src/main/java/androidx/wear/ambient/AmbientModeSupport.java
+++ b/wear/wear/src/main/java/androidx/wear/ambient/AmbientModeSupport.java
@@ -38,21 +38,48 @@
  * The application that uses this should add the {@link android.Manifest.permission#WAKE_LOCK}
  * permission to its manifest.
  * <p>
+ * The following describes the general use of this class:
+ * <p>
+ * Create a subclass of one of the {@link FragmentActivity} classes and implement the
+ * {@link AmbientCallbackProvider} interface. Override the
+ * {@link AmbientCallbackProvider#getAmbientCallback()} method to provide the callbacks required
+ * for reacting to the ambient events from the Android system. If a valid
+ * {@link AmbientCallback} is not provided (either no implementation of the
+ * {@link AmbientCallbackProvider} interface, or returning null from
+ * {@link AmbientCallbackProvider#getAmbientCallback()}), then ambient mode will NOT be enabled.
+ * <p>
  * The primary entry  point for this code is the {@link #attach(FragmentActivity)} method.
  * It should be called with an {@link FragmentActivity} as an argument and that
  * {@link FragmentActivity} will then be able to receive ambient lifecycle events through
  * an {@link AmbientCallback}. The {@link FragmentActivity} will also receive a
  * {@link AmbientController} object from the attachment which can be used to query the current
- * status of the ambient mode. An example of how to attach {@link AmbientModeSupport} to your
- * {@link FragmentActivity} and use the {@link AmbientController} can be found below:
+ * status of the ambient mode.
  * <p>
- * <pre class="prettyprint">{@code
- *     AmbientMode.AmbientController controller = AmbientMode.attachAmbientSupport(this);
- *     boolean isAmbient =  controller.isAmbient();
+ * An example of how to implement the {@link AmbientCallbackProvider} interface, attach
+ * {@link AmbientModeSupport} to your {@link FragmentActivity} and use the
+ * {@link AmbientController} can be found below:
+ *
+ * <pre class="prettyprint">
+ * public class MyActivity extends FragmentActivity
+ *     implements AmbientModeSupport.AmbientCallbackProvider {
+ *     {@literal @}Override
+ *     public void onCreate(Bundle savedInstanceState) {
+ *         super.onCreate(savedInstanceState)
+ *         ...
+ *         AmbientModeSupport.AmbientController controller = AmbientModeSupport.attach(this);
+ *         boolean isAmbient = controller.isAmbient();
+ *     }
+ *     {@literal @}Override
+ *     AmbientModeSupport.AmbientCallback getAmbientCallback() {
+ *         return new AmbientModeSupport.AmbientCallback() {
+ *             public void onEnterAmbient(Bundle ambientDetails) {...}
+ *             public void onExitAmbient(Bundle ambientDetails) {...}
+ *         }
+ *     }
  * }</pre>
  */
 public final class AmbientModeSupport extends Fragment {
-    private static final String TAG = "AmbientMode";
+    private static final String TAG = "AmbientModeSupport";
 
     /**
      * Property in bundle passed to {@code AmbientCallback#onEnterAmbient(Bundle)} to indicate
@@ -82,11 +109,11 @@
 
     /**
      * Interface for any {@link Activity} that wishes to implement Ambient Mode. Use the
-     * {@link #getAmbientCallback()} method to return and {@link AmbientCallback} which can be used
+     * {@link #getAmbientCallback()} method to return an {@link AmbientCallback} which can be used
      * to bind the {@link AmbientModeSupport} to the instantiation of this interface.
      * <p>
      * <pre class="prettyprint">{@code
-     * return new AmbientMode.AmbientCallback() {
+     * return new AmbientModeSupport.AmbientCallback() {
      *     public void onEnterAmbient(Bundle ambientDetails) {...}
      *     public void onExitAmbient(Bundle ambientDetails) {...}
      *  }
@@ -101,7 +128,8 @@
     }
 
     /**
-     * Callback to receive ambient mode state changes. It must be used by all users of AmbientMode.
+     * Callback to receive ambient mode state changes. It must be used by all users of
+     * AmbientModeSupport.
      */
     public abstract static class AmbientCallback {
         /**
@@ -302,5 +330,17 @@
                 mDelegate.setAmbientOffloadEnabled(enabled);
             }
         }
+
+        /**
+         * Sets whether this activity's task should be moved to the front when the system exits
+         * ambient mode. If true, the activity's task may be moved to the front if it was the
+         * last activity to be running when ambient started, depending on how much time the
+         * system spent in ambient mode.
+         */
+        public void setAutoResumeEnabled(boolean enabled) {
+            if (mDelegate != null) {
+                mDelegate.setAutoResumeEnabled(enabled);
+            }
+        }
     }
 }
diff --git a/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivity.java b/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivity.java
index 6ac18bd..f2af9a3 100644
--- a/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivity.java
+++ b/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivity.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.os.Build;
+import android.os.SystemClock;
 import android.service.notification.StatusBarNotification;
 
 import androidx.annotation.DrawableRes;
@@ -83,6 +84,7 @@
         private PendingIntent mTouchIntent;
         private LocusIdCompat mLocusId;
         private int mOngoingActivityId = OngoingActivityData.DEFAULT_ID;
+        private String mCategory;
 
         /**
          * Construct a new empty {@link Builder}, associated with the given notification.
@@ -106,6 +108,8 @@
          * {@link OngoingActivity}. For example, in the WatchFace.
          * Should be white with a transparent background, preferably an AnimatedVectorDrawable.
          */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setAnimatedIcon(@NonNull Icon animatedIcon) {
             mAnimatedIcon = animatedIcon;
@@ -117,6 +121,8 @@
          * {@link OngoingActivity}. For example, in the WatchFace.
          * Should be white with a transparent background, preferably an AnimatedVectorDrawable.
          */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setAnimatedIcon(@DrawableRes int animatedIcon) {
             mAnimatedIcon = Icon.createWithResource(mContext, animatedIcon);
@@ -128,6 +134,8 @@
          * {@link OngoingActivity}, for example in the WatchFace in ambient mode.
          * Should be white with a transparent background, preferably an VectorDrawable.
          */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setStaticIcon(@NonNull Icon staticIcon) {
             mStaticIcon = staticIcon;
@@ -139,6 +147,8 @@
          * {@link OngoingActivity}, for example in the WatchFace in ambient mode.
          * Should be white with a transparent background, preferably an VectorDrawable.
          */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setStaticIcon(@DrawableRes int staticIcon) {
             mStaticIcon = Icon.createWithResource(mContext, staticIcon);
@@ -149,6 +159,8 @@
          * Set the initial status of this ongoing activity, the status may be displayed on the UI to
          * show progress of the Ongoing Activity.
          */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setStatus(@NonNull OngoingActivityStatus status) {
             mStatus = status;
@@ -159,6 +171,8 @@
          * Set the intent to be used to go back to the activity when the user interacts with the
          * Ongoing Activity in other surfaces (for example, taps the Icon on the WatchFace)
          */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setTouchIntent(@NonNull PendingIntent touchIntent) {
             mTouchIntent = touchIntent;
@@ -169,6 +183,8 @@
          * Set the corresponding LocusId of this {@link OngoingActivity}, this will be used by the
          * launcher to identify the corresponding launcher item and display it accordingly.
          */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setLocusId(@NonNull LocusIdCompat locusId) {
             mLocusId = locusId;
@@ -179,6 +195,8 @@
          * Give an id to this {@link OngoingActivity}, as a way to reference it in
          * {@link OngoingActivity#fromExistingOngoingActivity(Context, int)}
          */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setOngoingActivityId(int ongoingActivityId) {
             mOngoingActivityId = ongoingActivityId;
@@ -186,6 +204,18 @@
         }
 
         /**
+         * Set the category of this {@link OngoingActivity}, this may be used by the system to
+         * prioritize it.
+         */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
+        @NonNull
+        public Builder setCategory(@NonNull String category) {
+            mCategory = category;
+            return this;
+        }
+
+        /**
          * Combine all options provided and the information in the notification if needed,
          * return a new {@link OngoingActivity} object.
          *
@@ -220,14 +250,18 @@
                 locusId = Api29Impl.getLocusId(notification);
             }
 
+            String category = mCategory == null ? notification.category : mCategory;
+
             return new OngoingActivity(mNotificationId, mNotificationBuilder,
                     new OngoingActivityData(
                         mAnimatedIcon,
                         staticIcon,
                         status,
                         touchIntent,
-                        locusId,
-                        mOngoingActivityId
+                        locusId == null ? null : locusId.getId(),
+                        mOngoingActivityId,
+                        category,
+                        SystemClock.elapsedRealtime()
                     ));
         }
     }
@@ -278,8 +312,8 @@
         StatusBarNotification[] notifications =
                 context.getSystemService(NotificationManager.class).getActiveNotifications();
         for (StatusBarNotification statusBarNotification : notifications) {
-            OngoingActivityData data = OngoingActivityData.create(
-                    statusBarNotification.getNotification());
+            OngoingActivityData data =
+                    OngoingActivityData.create(statusBarNotification.getNotification());
             if (data != null && filter.test(data)) {
                 return new OngoingActivity(statusBarNotification.getId(),
                         new NotificationCompat.Builder(context,
diff --git a/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivityData.java b/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivityData.java
index 17130b4..5b07295 100644
--- a/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivityData.java
+++ b/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivityData.java
@@ -18,6 +18,7 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.graphics.drawable.Icon;
+import android.os.SystemClock;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -35,34 +36,47 @@
 public class OngoingActivityData implements VersionedParcelable {
     @Nullable
     @ParcelField(value = 1, defaultValue = "null")
-    private final Icon mAnimatedIcon;
+    Icon mAnimatedIcon;
 
     @NonNull
     @ParcelField(value = 2)
-    private final Icon mStaticIcon;
+    Icon mStaticIcon;
 
     @Nullable
     @ParcelField(value = 3, defaultValue = "null")
-    private OngoingActivityStatus mStatus;
+    OngoingActivityStatus mStatus;
 
     @NonNull
     @ParcelField(value = 4)
-    private final PendingIntent mTouchIntent;
+    PendingIntent mTouchIntent;
 
     @Nullable
     @ParcelField(value = 5, defaultValue = "null")
-    private final LocusIdCompat mLocusId;
+    String mLocusId;
 
     @ParcelField(value = 6, defaultValue = "-1")
-    private final int mOngoingActivityId;
+    int mOngoingActivityId;
+
+    @Nullable
+    @ParcelField(value = 7, defaultValue = "null")
+    String mCategory;
+
+    @ParcelField(value = 8)
+    long mTimestamp;
+
+    // Required by VersionedParcelable
+    OngoingActivityData() {
+    }
 
     OngoingActivityData(
             @Nullable Icon animatedIcon,
             @NonNull Icon staticIcon,
             @Nullable OngoingActivityStatus status,
             @NonNull PendingIntent touchIntent,
-            @Nullable LocusIdCompat locusId,
-            int ongoingActivityId
+            @Nullable String locusId,
+            int ongoingActivityId,
+            @Nullable String category,
+            long timestamp
     ) {
         mAnimatedIcon = animatedIcon;
         mStaticIcon = staticIcon;
@@ -70,6 +84,8 @@
         mTouchIntent = touchIntent;
         mLocusId = locusId;
         mOngoingActivityId = ongoingActivityId;
+        mCategory = category;
+        mTimestamp = timestamp;
     }
 
     @NonNull
@@ -122,7 +138,8 @@
 
     /**
      * Get the static icon that can be used on some surfaces to represent this
-     * {@link OngoingActivity}. For example in the WatchFace in ambient mode.
+     * {@link OngoingActivity}. For example in the WatchFace in ambient mode. If not set, returns
+     *  the small icon of the corresponding Notification.
      */
     @NonNull
     public Icon getStaticIcon() {
@@ -131,7 +148,8 @@
 
     /**
      * Get the status of this ongoing activity, the status may be displayed on the UI to
-     * show progress of the Ongoing Activity.
+     * show progress of the Ongoing Activity. If not set, returns the content text of the
+     * corresponding Notification.
      */
     @Nullable
     public OngoingActivityStatus getStatus() {
@@ -140,7 +158,8 @@
 
     /**
      * Get the intent to be used to go back to the activity when the user interacts with the
-     * Ongoing Activity in other surfaces (for example, taps the Icon on the WatchFace)
+     * Ongoing Activity in other surfaces (for example, taps the Icon on the WatchFace). If not
+     * set, returns the touch intent of the corresponding Notification.
      */
     @NonNull
     public PendingIntent getTouchIntent() {
@@ -149,11 +168,12 @@
 
     /**
      * Get the LocusId of this {@link OngoingActivity}, this can be used by the launcher to
-     * identify the corresponding launcher item and display it accordingly.
+     * identify the corresponding launcher item and display it accordingly. If not set, returns
+     * the one in the corresponding Notification.
      */
     @Nullable
     public LocusIdCompat getLocusId() {
-        return mLocusId;
+        return new LocusIdCompat(mLocusId);
     }
 
     /**
@@ -164,6 +184,22 @@
         return mOngoingActivityId;
     }
 
+    /**
+     * Get the Category of this {@link OngoingActivity} if set, otherwise the category of the
+     * corresponding notification.
+     */
+    @Nullable
+    public String getCategory() {
+        return mCategory;
+    }
+
+    /**
+     * Get the time (in {@link SystemClock#elapsedRealtime()} time) the OngoingActivity was built.
+     */
+    public long getTimestamp() {
+        return mTimestamp;
+    }
+
     // Status is mutable, by the library.
     void setStatus(@NonNull OngoingActivityStatus status) {
         mStatus = status;
diff --git a/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivityStatus.java b/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivityStatus.java
index fc37c9d..2dc53a7 100644
--- a/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivityStatus.java
+++ b/wear/wear/src/main/java/androidx/wear/ongoingactivity/OngoingActivityStatus.java
@@ -21,6 +21,8 @@
 import androidx.versionedparcelable.VersionedParcelable;
 import androidx.versionedparcelable.VersionedParcelize;
 
+import kotlin.NotImplementedError;
+
 /**
  * Base class to serialize / deserialize {@link OngoingActivityStatus} into / from a Notification
  *
@@ -29,7 +31,12 @@
  * {@link android.os.SystemClock#elapsedRealtime()}
  */
 @VersionedParcelize
-public abstract class OngoingActivityStatus implements VersionedParcelable {
+public class OngoingActivityStatus implements VersionedParcelable {
+
+    // Required by VersionedParcelable
+    OngoingActivityStatus() {
+    }
+
     /**
      * Returns a textual representation of the ongoing activity status at the given time
      * represented as milliseconds timestamp
@@ -43,7 +50,9 @@
      *                      returned by {@link android.os.SystemClock#elapsedRealtime()}.
      */
     @NonNull
-    public abstract CharSequence getText(@NonNull Context context, long timeNowMillis);
+    public CharSequence getText(@NonNull Context context, long timeNowMillis) {
+        throw new NotImplementedError();
+    }
 
     /**
      * Returns the timestamp of the next time when the display will be different from the current
@@ -56,7 +65,9 @@
      * @return the first point in time after {@code fromTimeMillis} when the displayed value of
      * this status will change. returns Long.MAX_VALUE if the display will never change.
      */
-    public abstract long getNextChangeTimeMillis(long fromTimeMillis);
+    public long getNextChangeTimeMillis(long fromTimeMillis) {
+        throw new NotImplementedError();
+    }
 
     // Invalid value to use for paused_at and duration, as suggested by api guidelines 5.15
     static final long LONG_DEFAULT = -1L;
diff --git a/wear/wear/src/main/java/androidx/wear/ongoingactivity/TextOngoingActivityStatus.java b/wear/wear/src/main/java/androidx/wear/ongoingactivity/TextOngoingActivityStatus.java
index bceaded..d31e814 100644
--- a/wear/wear/src/main/java/androidx/wear/ongoingactivity/TextOngoingActivityStatus.java
+++ b/wear/wear/src/main/java/androidx/wear/ongoingactivity/TextOngoingActivityStatus.java
@@ -31,7 +31,11 @@
 public class TextOngoingActivityStatus extends OngoingActivityStatus {
     @NonNull
     @ParcelField(value = 1, defaultValue = "")
-    private String mStr = "";
+    String mStr = "";
+
+    // Required by VersionedParcelable
+    TextOngoingActivityStatus() {
+    }
 
     public TextOngoingActivityStatus(@NonNull String str) {
         this.mStr = str;
diff --git a/wear/wear/src/main/java/androidx/wear/ongoingactivity/TimerOngoingActivityStatus.java b/wear/wear/src/main/java/androidx/wear/ongoingactivity/TimerOngoingActivityStatus.java
index 524423e..79d76df 100644
--- a/wear/wear/src/main/java/androidx/wear/ongoingactivity/TimerOngoingActivityStatus.java
+++ b/wear/wear/src/main/java/androidx/wear/ongoingactivity/TimerOngoingActivityStatus.java
@@ -20,6 +20,7 @@
 import android.text.format.DateUtils;
 
 import androidx.annotation.NonNull;
+import androidx.versionedparcelable.NonParcelField;
 import androidx.versionedparcelable.ParcelField;
 import androidx.versionedparcelable.VersionedParcelize;
 
@@ -31,20 +32,26 @@
 @VersionedParcelize
 public class TimerOngoingActivityStatus extends OngoingActivityStatus {
     @ParcelField(value = 1, defaultValue = "0")
-    private long mTimeZeroMillis;
+    long mTimeZeroMillis;
 
     @ParcelField(value = 2, defaultValue = "false")
-    private boolean mCountDown = false;
+    boolean mCountDown = false;
 
     @ParcelField(value = 3, defaultValue = "-1")
-    private long mPausedAtMillis = LONG_DEFAULT;
+    long mPausedAtMillis = LONG_DEFAULT;
 
     @ParcelField(value = 4, defaultValue = "-1")
-    private long mTotalDurationMillis = LONG_DEFAULT;
+    long mTotalDurationMillis = LONG_DEFAULT;
 
+    @NonParcelField
     private final StringBuilder mStringBuilder = new StringBuilder(8);
+
     private static final String NEGATIVE_DURATION_PREFIX = "-";
 
+    // Required by VersionedParcelable
+    TimerOngoingActivityStatus() {
+    }
+
     /**
      * Create a Status representing a timer or stopwatch.
      *
diff --git a/webkit/README.md b/webkit/README.md
index 2c7ae98..dedcbe1 100644
--- a/webkit/README.md
+++ b/webkit/README.md
@@ -1,7 +1,7 @@
 # androidx.webkit
 
 **See this page rendered in [Gitiles
-markdown](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/webkit/README.md).**
+markdown](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/webkit/README.md).**
 
 The androidx.webkit library is a static library you can add to your Android
 application in order to use android.webkit APIs that are not available for older
@@ -11,7 +11,7 @@
 
 * [Library owners](OWNERS)
 * [Release notes](https://developer.android.com/jetpack/androidx/releases/webkit)
-* [Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/webkit/)
+* [Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/webkit/)
 * [Reference docs and guide to import the library](https://developer.android.com/reference/androidx/webkit/package-summary)
 * [Existing open bugs](https://issuetracker.google.com/issues?q=componentid:460423%20status:open)
 * [File a new bug](https://issuetracker.google.com/issues/new?component=460423)
diff --git a/webkit/integration-tests/testapp/README.md b/webkit/integration-tests/testapp/README.md
index 4c7d56c..335af43 100644
--- a/webkit/integration-tests/testapp/README.md
+++ b/webkit/integration-tests/testapp/README.md
@@ -1,7 +1,7 @@
 # WebView Demo App
 
 **See this page rendered in [Gitiles
-markdown](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/webkit/integration-tests/testapp/README.md).**
+markdown](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/webkit/integration-tests/testapp/README.md).**
 
 The WebView/Webkit demo app serves as both a practical demonstration how to use
 the latest AndroidX Webkit APIs and as a means to exercise those APIs for manual
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatForceDarkTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatForceDarkTest.java
index 3e06afc..7b7806e 100644
--- a/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatForceDarkTest.java
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatForceDarkTest.java
@@ -29,7 +29,6 @@
 import androidx.core.graphics.ColorUtils;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.After;
 import org.junit.Before;
@@ -61,9 +60,10 @@
     // LayoutParams are null until WebView has a parent Activity.
     // Test testForceDark_rendersDark requires LayoutParams to define
     // width and height of WebView to capture its bitmap representation.
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<WebViewTestActivity> mActivityRule =
-            new ActivityTestRule<>(WebViewTestActivity.class);
+    public final androidx.test.rule.ActivityTestRule<WebViewTestActivity> mActivityRule =
+            new androidx.test.rule.ActivityTestRule<>(WebViewTestActivity.class);
     private WebViewOnUiThread mWebViewOnUiThread;
 
     @Before
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewDocumentStartJavaScriptTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewDocumentStartJavaScriptTest.java
index d9ef604..2b1421f 100644
--- a/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewDocumentStartJavaScriptTest.java
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewDocumentStartJavaScriptTest.java
@@ -46,7 +46,7 @@
     private static final String JS_OBJECT_NAME = "myObject";
     private static final String BASIC_USAGE = "<!DOCTYPE html><html><body></body></html>";
     private static final String BASIC_SCRIPT = "myObject.postMessage('hello');";
-    private static final Set<String> MATCH_EXAMPLE_COM = new HashSet(Arrays.asList(BASE_URI));
+    private static final Set<String> MATCH_EXAMPLE_COM = new HashSet<>(Arrays.asList(BASE_URI));
 
     private WebViewOnUiThread mWebViewOnUiThread;
     private TestWebMessageListener mListener = new TestWebMessageListener();
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewWebMessageListenerTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewWebMessageListenerTest.java
index cedfdbb..ad67acc 100644
--- a/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewWebMessageListenerTest.java
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewWebMessageListenerTest.java
@@ -58,7 +58,7 @@
             + "        myObject.postMessage('hello');"
             + "    </script>"
             + "</body></html>";
-    private static final Set<String> MATCH_EXAMPLE_COM = new HashSet(Arrays.asList(BASE_URI));
+    private static final Set<String> MATCH_EXAMPLE_COM = new HashSet<>(Arrays.asList(BASE_URI));
 
     private WebViewOnUiThread mWebViewOnUiThread;
     private TestWebMessageListener mListener = new TestWebMessageListener();
diff --git a/window/window-extensions/api/current.txt b/window/window-extensions/api/current.txt
index 01608ca..78bdc7f 100644
--- a/window/window-extensions/api/current.txt
+++ b/window/window-extensions/api/current.txt
@@ -11,10 +11,18 @@
     field public static final int POSTURE_UNKNOWN = 0; // 0x0
   }
 
-  public class ExtensionDisplayFeature {
-    ctor public ExtensionDisplayFeature(android.graphics.Rect, int);
+  public interface ExtensionDisplayFeature {
     method public android.graphics.Rect getBounds();
+  }
+
+  public class ExtensionFoldingFeature implements androidx.window.extensions.ExtensionDisplayFeature {
+    ctor public ExtensionFoldingFeature(android.graphics.Rect, int, int);
+    method public android.graphics.Rect getBounds();
+    method public int getState();
     method public int getType();
+    field public static final int STATE_FLAT = 1; // 0x1
+    field public static final int STATE_FLIPPED = 3; // 0x3
+    field public static final int STATE_HALF_OPENED = 2; // 0x2
     field public static final int TYPE_FOLD = 1; // 0x1
     field public static final int TYPE_HINGE = 2; // 0x2
   }
diff --git a/window/window-extensions/api/public_plus_experimental_current.txt b/window/window-extensions/api/public_plus_experimental_current.txt
index 01608ca..78bdc7f 100644
--- a/window/window-extensions/api/public_plus_experimental_current.txt
+++ b/window/window-extensions/api/public_plus_experimental_current.txt
@@ -11,10 +11,18 @@
     field public static final int POSTURE_UNKNOWN = 0; // 0x0
   }
 
-  public class ExtensionDisplayFeature {
-    ctor public ExtensionDisplayFeature(android.graphics.Rect, int);
+  public interface ExtensionDisplayFeature {
     method public android.graphics.Rect getBounds();
+  }
+
+  public class ExtensionFoldingFeature implements androidx.window.extensions.ExtensionDisplayFeature {
+    ctor public ExtensionFoldingFeature(android.graphics.Rect, int, int);
+    method public android.graphics.Rect getBounds();
+    method public int getState();
     method public int getType();
+    field public static final int STATE_FLAT = 1; // 0x1
+    field public static final int STATE_FLIPPED = 3; // 0x3
+    field public static final int STATE_HALF_OPENED = 2; // 0x2
     field public static final int TYPE_FOLD = 1; // 0x1
     field public static final int TYPE_HINGE = 2; // 0x2
   }
diff --git a/window/window-extensions/api/restricted_current.txt b/window/window-extensions/api/restricted_current.txt
index 01608ca..78bdc7f 100644
--- a/window/window-extensions/api/restricted_current.txt
+++ b/window/window-extensions/api/restricted_current.txt
@@ -11,10 +11,18 @@
     field public static final int POSTURE_UNKNOWN = 0; // 0x0
   }
 
-  public class ExtensionDisplayFeature {
-    ctor public ExtensionDisplayFeature(android.graphics.Rect, int);
+  public interface ExtensionDisplayFeature {
     method public android.graphics.Rect getBounds();
+  }
+
+  public class ExtensionFoldingFeature implements androidx.window.extensions.ExtensionDisplayFeature {
+    ctor public ExtensionFoldingFeature(android.graphics.Rect, int, int);
+    method public android.graphics.Rect getBounds();
+    method public int getState();
     method public int getType();
+    field public static final int STATE_FLAT = 1; // 0x1
+    field public static final int STATE_FLIPPED = 3; // 0x3
+    field public static final int STATE_HALF_OPENED = 2; // 0x2
     field public static final int TYPE_FOLD = 1; // 0x1
     field public static final int TYPE_HINGE = 2; // 0x2
   }
diff --git a/window/window-extensions/build.gradle b/window/window-extensions/build.gradle
index 82c95ec..6e84dbc 100644
--- a/window/window-extensions/build.gradle
+++ b/window/window-extensions/build.gradle
@@ -19,6 +19,13 @@
 import androidx.build.Publish
 import androidx.build.RunApiTasks
 
+import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_EXT_JUNIT
+import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_RULES
+import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_RUNNER
+import static androidx.build.dependencies.DependenciesKt.DEXMAKER_MOCKITO
+import static androidx.build.dependencies.DependenciesKt.MOCKITO_CORE
+import static androidx.build.dependencies.DependenciesKt.TRUTH
+
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -32,6 +39,12 @@
 
 dependencies {
     implementation("androidx.annotation:annotation:1.1.0")
+
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(ANDROIDX_TEST_RULES)
+    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
 }
 
 androidx {
diff --git a/car/app/app/src/androidTest/AndroidManifest.xml b/window/window-extensions/src/androidTest/AndroidManifest.xml
similarity index 68%
copy from car/app/app/src/androidTest/AndroidManifest.xml
copy to window/window-extensions/src/androidTest/AndroidManifest.xml
index 3bc2684..d85d231 100644
--- a/car/app/app/src/androidTest/AndroidManifest.xml
+++ b/window/window-extensions/src/androidTest/AndroidManifest.xml
@@ -15,5 +15,11 @@
   limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.car.app">
+    package="androidx.window.extensions.test">
+
+    <application>
+        <activity android:name="androidx.window.extensions.TestActivity" />
+        <activity android:name="androidx.window.extensions.TestConfigChangeHandlingActivity"
+            android:configChanges="orientation|screenLayout|screenSize"/>
+    </application>
 </manifest>
diff --git a/window/window-extensions/src/androidTest/java/androidx/window/extensions/ExtensionDeviceStateTest.java b/window/window-extensions/src/androidTest/java/androidx/window/extensions/ExtensionDeviceStateTest.java
new file mode 100644
index 0000000..3299a08
--- /dev/null
+++ b/window/window-extensions/src/androidTest/java/androidx/window/extensions/ExtensionDeviceStateTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link ExtensionDeviceState} class. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class ExtensionDeviceStateTest {
+
+    @Test
+    public void testEquals_samePosture() {
+        ExtensionDeviceState original = new ExtensionDeviceState(0);
+        ExtensionDeviceState copy = new ExtensionDeviceState(0);
+
+        assertEquals(original, copy);
+    }
+
+    @Test
+    public void testEquals_differentPosture() {
+        ExtensionDeviceState original = new ExtensionDeviceState(0);
+        ExtensionDeviceState different = new ExtensionDeviceState(1);
+
+        assertNotEquals(original, different);
+    }
+
+    @Test
+    public void testHashCode_matchesIfEqual() {
+        int posture = 111;
+        ExtensionDeviceState original = new ExtensionDeviceState(posture);
+        ExtensionDeviceState matching = new ExtensionDeviceState(posture);
+
+        assertEquals(original, matching);
+        assertEquals(original.hashCode(), matching.hashCode());
+    }
+}
diff --git a/window/window-extensions/src/androidTest/java/androidx/window/extensions/ExtensionDisplayFeatureTest.java b/window/window-extensions/src/androidTest/java/androidx/window/extensions/ExtensionDisplayFeatureTest.java
new file mode 100644
index 0000000..fdd42fa
--- /dev/null
+++ b/window/window-extensions/src/androidTest/java/androidx/window/extensions/ExtensionDisplayFeatureTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.extensions;
+
+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 ExtensionFoldingFeature} class. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class ExtensionDisplayFeatureTest {
+
+    @Test
+    public void testEquals_sameAttributes() {
+        Rect bounds = new Rect(1, 0, 1, 10);
+        int type = ExtensionFoldingFeature.TYPE_FOLD;
+        int state = ExtensionFoldingFeature.STATE_FLAT;
+
+        ExtensionFoldingFeature original = new ExtensionFoldingFeature(bounds, type, state);
+        ExtensionFoldingFeature copy = new ExtensionFoldingFeature(bounds, type, state);
+
+        assertEquals(original, copy);
+    }
+
+    @Test
+    public void testEquals_differentRect() {
+        Rect originalRect = new Rect(1, 0, 1, 10);
+        Rect otherRect = new Rect(2, 0, 2, 10);
+        int type = ExtensionFoldingFeature.TYPE_FOLD;
+        int state = ExtensionFoldingFeature.STATE_FLAT;
+
+        ExtensionFoldingFeature original = new ExtensionFoldingFeature(originalRect, type,
+                state);
+        ExtensionFoldingFeature other = new ExtensionFoldingFeature(otherRect, type, state);
+
+        assertNotEquals(original, other);
+    }
+
+    @Test
+    public void testEquals_differentType() {
+        Rect rect = new Rect(1, 0, 1, 10);
+        int originalType = ExtensionFoldingFeature.TYPE_FOLD;
+        int otherType = ExtensionFoldingFeature.TYPE_HINGE;
+        int state = ExtensionFoldingFeature.STATE_FLAT;
+
+        ExtensionFoldingFeature original = new ExtensionFoldingFeature(rect, originalType,
+                state);
+        ExtensionFoldingFeature other = new ExtensionFoldingFeature(rect, otherType, state);
+
+        assertNotEquals(original, other);
+    }
+
+    @Test
+    public void testEquals_differentState() {
+        Rect rect = new Rect(1, 0, 1, 10);
+        int type = ExtensionFoldingFeature.TYPE_FOLD;
+        int originalState = ExtensionFoldingFeature.STATE_FLAT;
+        int otherState = ExtensionFoldingFeature.STATE_FLIPPED;
+
+        ExtensionFoldingFeature original = new ExtensionFoldingFeature(rect, type,
+                originalState);
+        ExtensionFoldingFeature other = new ExtensionFoldingFeature(rect, type, otherState);
+
+        assertNotEquals(original, other);
+    }
+
+    @Test
+    public void testHashCode_matchesIfEqual() {
+        Rect originalRect = new Rect(1, 0, 1, 10);
+        Rect matchingRect = new Rect(1, 0, 1, 10);
+        int type = ExtensionFoldingFeature.TYPE_FOLD;
+        int state = ExtensionFoldingFeature.STATE_FLAT;
+
+        ExtensionFoldingFeature original = new ExtensionFoldingFeature(originalRect, type,
+                state);
+        ExtensionFoldingFeature matching = new ExtensionFoldingFeature(matchingRect, type,
+                state);
+
+        assertEquals(original, matching);
+        assertEquals(original.hashCode(), matching.hashCode());
+    }
+}
diff --git a/window/window-extensions/src/androidTest/java/androidx/window/extensions/ExtensionWindowLayoutInfoTest.java b/window/window-extensions/src/androidTest/java/androidx/window/extensions/ExtensionWindowLayoutInfoTest.java
new file mode 100644
index 0000000..bb87c3b
--- /dev/null
+++ b/window/window-extensions/src/androidTest/java/androidx/window/extensions/ExtensionWindowLayoutInfoTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.extensions;
+
+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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Tests for {@link ExtensionWindowLayoutInfo} class. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class ExtensionWindowLayoutInfoTest {
+
+    @Test
+    public void testEquals_sameFeatures() {
+        List<ExtensionDisplayFeature> features = new ArrayList<>();
+
+        ExtensionWindowLayoutInfo original = new ExtensionWindowLayoutInfo(features);
+        ExtensionWindowLayoutInfo copy = new ExtensionWindowLayoutInfo(features);
+
+        assertEquals(original, copy);
+    }
+
+    @Test
+    public void testEquals_differentFeatures() {
+        List<ExtensionDisplayFeature> originalFeatures = new ArrayList<>();
+        List<ExtensionDisplayFeature> differentFeatures = new ArrayList<>();
+        Rect rect = new Rect(1, 0, 1, 10);
+        differentFeatures.add(new ExtensionFoldingFeature(
+                rect, ExtensionFoldingFeature.TYPE_HINGE,
+                ExtensionFoldingFeature.STATE_FLAT));
+
+        ExtensionWindowLayoutInfo original = new ExtensionWindowLayoutInfo(originalFeatures);
+        ExtensionWindowLayoutInfo different = new ExtensionWindowLayoutInfo(differentFeatures);
+
+        assertNotEquals(original, different);
+    }
+
+    @Test
+    public void testHashCode_matchesIfEqual() {
+        List<ExtensionDisplayFeature> firstFeatures = new ArrayList<>();
+        List<ExtensionDisplayFeature> secondFeatures = new ArrayList<>();
+        ExtensionWindowLayoutInfo first = new ExtensionWindowLayoutInfo(firstFeatures);
+        ExtensionWindowLayoutInfo second = new ExtensionWindowLayoutInfo(secondFeatures);
+
+        assertEquals(first, second);
+        assertEquals(first.hashCode(), second.hashCode());
+    }
+
+    @Test
+    public void testHashCode_matchesIfEqualFeatures() {
+        ExtensionDisplayFeature originalFeature = new ExtensionFoldingFeature(
+                new Rect(0, 0, 100, 0),
+                ExtensionFoldingFeature.TYPE_HINGE,
+                ExtensionFoldingFeature.STATE_FLAT
+        );
+        ExtensionDisplayFeature matchingFeature = new ExtensionFoldingFeature(
+                new Rect(0, 0, 100, 0),
+                ExtensionFoldingFeature.TYPE_HINGE,
+                ExtensionFoldingFeature.STATE_FLAT
+        );
+        List<ExtensionDisplayFeature> firstFeatures = Collections.singletonList(originalFeature);
+        List<ExtensionDisplayFeature> secondFeatures = Collections.singletonList(matchingFeature);
+        ExtensionWindowLayoutInfo first = new ExtensionWindowLayoutInfo(firstFeatures);
+        ExtensionWindowLayoutInfo second = new ExtensionWindowLayoutInfo(secondFeatures);
+
+        assertEquals(first, second);
+        assertEquals(first.hashCode(), second.hashCode());
+    }
+}
diff --git a/window/window-extensions/src/androidTest/java/androidx/window/extensions/TestActivity.java b/window/window-extensions/src/androidTest/java/androidx/window/extensions/TestActivity.java
new file mode 100644
index 0000000..1c36baf
--- /dev/null
+++ b/window/window-extensions/src/androidTest/java/androidx/window/extensions/TestActivity.java
@@ -0,0 +1,55 @@
+/*
+ * 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.extensions;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.CountDownLatch;
+
+public class TestActivity extends Activity implements View.OnLayoutChangeListener {
+
+    private int mRootViewId;
+    private CountDownLatch mLayoutLatch = new CountDownLatch(1);
+    private static CountDownLatch sResumeLatch = new CountDownLatch(1);
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final View contentView = new View(this);
+        mRootViewId = View.generateViewId();
+        contentView.setId(mRootViewId);
+        setContentView(contentView);
+
+        getWindow().getDecorView().addOnLayoutChangeListener(this);
+    }
+
+    @Override
+    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+            int oldTop, int oldRight, int oldBottom) {
+        mLayoutLatch.countDown();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        sResumeLatch.countDown();
+    }
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt b/window/window-extensions/src/androidTest/java/androidx/window/extensions/TestConfigChangeHandlingActivity.java
similarity index 77%
copy from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
copy to window/window-extensions/src/androidTest/java/androidx/window/extensions/TestConfigChangeHandlingActivity.java
index f9cb2fe..f0a2871 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/ExperimentalFocus.kt
+++ b/window/window-extensions/src/androidTest/java/androidx/window/extensions/TestConfigChangeHandlingActivity.java
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.focus
+package androidx.window.extensions;
 
-@RequiresOptIn("The Focus API is experimental and is likely to change in the future.")
-annotation class ExperimentalFocus
\ No newline at end of file
+/** Activity that handles orientation configuration change. */
+public final class TestConfigChangeHandlingActivity extends TestActivity {
+}
diff --git a/window/window-extensions/src/androidTest/java/androidx/window/extensions/WindowTestBase.java b/window/window-extensions/src/androidTest/java/androidx/window/extensions/WindowTestBase.java
new file mode 100644
index 0000000..b68fc23f
--- /dev/null
+++ b/window/window-extensions/src/androidTest/java/androidx/window/extensions/WindowTestBase.java
@@ -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.window.extensions;
+
+import android.app.Activity;
+import android.os.IBinder;
+
+import androidx.test.core.app.ActivityScenario;
+
+import org.junit.Before;
+
+/**
+ * Base class for all tests in the module.
+ */
+class WindowTestBase {
+    ActivityScenario<TestActivity> mActivityTestRule;
+
+    @Before
+    public void setUp() {
+        mActivityTestRule = ActivityScenario.launch(TestActivity.class);
+    }
+
+    static IBinder getActivityWindowToken(Activity activity) {
+        return activity.getWindow().getAttributes().token;
+    }
+}
diff --git a/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionDeviceState.java b/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionDeviceState.java
index be7ce85..f2f1481 100644
--- a/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionDeviceState.java
+++ b/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionDeviceState.java
@@ -43,8 +43,6 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
-            POSTURE_UNKNOWN,
-            POSTURE_CLOSED,
             POSTURE_HALF_OPENED,
             POSTURE_OPENED,
             POSTURE_FLIPPED
diff --git a/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionDisplayFeature.java b/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionDisplayFeature.java
index 8bbd56d..59794d9 100644
--- a/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionDisplayFeature.java
+++ b/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionDisplayFeature.java
@@ -18,110 +18,19 @@
 
 import android.graphics.Rect;
 
-import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 
 /**
  * Description of a physical feature on the display.
  */
-public class ExtensionDisplayFeature {
+public interface ExtensionDisplayFeature {
+
     /**
-     * The bounding rectangle of the feature within the application window in the window
-     * coordinate space.
+     * The bounding rectangle of the feature within the application window
+     * in the window coordinate space.
      *
-     * <p>The bounds for features of type {@link #TYPE_FOLD fold} must be zero-high (for
-     * horizontal folds) or zero-wide (for vertical folds) and span the entire window.
-     *
-     * <p>The bounds for features of type {@link #TYPE_HINGE hinge} must span the entire window
-     * but, unlike folds, can have a non-zero area which represents the region that is occluded by
-     * the hinge and not visible to the user.
+     * @return bounds of display feature.
      */
     @NonNull
-    private final Rect mBounds;
-
-    /**
-     * The physical type of the feature.
-     */
-    @Type
-    private final int mType;
-
-    /**
-     * A fold in the flexible screen without a physical gap.
-     */
-    public static final int TYPE_FOLD = 1;
-
-    /**
-     * A physical separation with a hinge that allows two display panels to fold.
-     */
-    public static final int TYPE_HINGE = 2;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            TYPE_FOLD,
-            TYPE_HINGE,
-    })
-    @interface Type{}
-
-    public ExtensionDisplayFeature(@NonNull Rect bounds, @Type int type) {
-        mBounds = new Rect(bounds);
-        mType = type;
-    }
-
-    /** Gets the bounding rect of the display feature in window coordinate space. */
-    @NonNull
-    public Rect getBounds() {
-        return new Rect(mBounds);
-    }
-
-    /** Gets the type of the display feature. */
-    @Type
-    public int getType() {
-        return mType;
-    }
-
-    @NonNull
-    private static String typeToString(int type) {
-        switch (type) {
-            case TYPE_FOLD:
-                return "FOLD";
-            case TYPE_HINGE:
-                return "HINGE";
-            default:
-                return "Unknown feature type (" + type + ")";
-        }
-    }
-
-    @NonNull
-    @Override
-    public String toString() {
-        return "ExtensionDisplayFeature { bounds=" + mBounds + ", type=" + typeToString(getType())
-                + " }";
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof ExtensionDisplayFeature)) {
-            return false;
-        }
-        final ExtensionDisplayFeature
-                other = (ExtensionDisplayFeature) obj;
-        if (mType != other.mType) {
-            return false;
-        }
-        return mBounds.equals(other.mBounds);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = mType;
-        result = 31 * result + mBounds.centerX() + mBounds.centerY();
-        return result;
-    }
+    Rect getBounds();
 }
diff --git a/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionFoldingFeature.java b/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionFoldingFeature.java
new file mode 100644
index 0000000..e39e5ca
--- /dev/null
+++ b/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionFoldingFeature.java
@@ -0,0 +1,213 @@
+/*
+ * 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.extensions;
+
+import android.graphics.Rect;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A feature that describes a fold in a flexible display
+ * or a hinge between two physical display panels.
+ */
+public class ExtensionFoldingFeature implements ExtensionDisplayFeature {
+
+    /**
+     * A fold in the flexible screen without a physical gap.
+     */
+    public static final int TYPE_FOLD = 1;
+
+    /**
+     * A physical separation with a hinge that allows two display panels to fold.
+     */
+    public static final int TYPE_HINGE = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            TYPE_FOLD,
+            TYPE_HINGE,
+    })
+    @interface Type{}
+
+    /**
+     * The foldable device's hinge is completely open, the screen space that is presented to the
+     * user is flat. See the
+     * <a href="https://developer.android.com/guide/topics/ui/foldables#postures">Posture</a>
+     * section in the official documentation for visual samples and references.
+     */
+    public static final int STATE_FLAT = 1;
+
+    /**
+     * The foldable device's hinge is in an intermediate position between opened and closed state,
+     * there is a non-flat angle between parts of the flexible screen or between physical screen
+     * panels. See the
+     * <a href="https://developer.android.com/guide/topics/ui/foldables#postures">Posture</a>
+     * section in the official documentation for visual samples and references.
+     */
+    public static final int STATE_HALF_OPENED = 2;
+
+    /**
+     * The foldable device's hinge is flipped with the flexible screen parts or physical screens
+     * facing opposite directions. See the
+     * <a href="https://developer.android.com/guide/topics/ui/foldables#postures">Posture</a>
+     * section in the official documentation for visual samples and references.
+     */
+    public static final int STATE_FLIPPED = 3;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            STATE_HALF_OPENED,
+            STATE_FLAT,
+            STATE_FLIPPED
+    })
+    @interface State {}
+
+    /**
+     * The bounding rectangle of the feature within the application window in the window
+     * coordinate space.
+     */
+    @NonNull
+    private final Rect mBounds;
+
+    /**
+     * The physical type of the feature.
+     */
+    @Type
+    private final int mType;
+
+    /**
+     * The state of the feature.
+     */
+    @State
+    private final int mState;
+
+    public ExtensionFoldingFeature(@NonNull Rect bounds, @Type int type, @State int state) {
+        validateFeatureBounds(bounds, type);
+        mBounds = new Rect(bounds);
+        mType = type;
+        mState = state;
+    }
+
+    /** Gets the bounding rect of the display feature in window coordinate space. */
+    @NonNull
+    @Override
+    public Rect getBounds() {
+        return new Rect(mBounds);
+    }
+
+    /** Gets the type of the display feature. */
+    @Type
+    public int getType() {
+        return mType;
+    }
+
+    /** Gets the state of the display feature. */
+    @State
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Verifies the bounds of the folding feature.
+     */
+    private static void validateFeatureBounds(@NonNull Rect bounds, int type) {
+        if (bounds.width() == 0 && bounds.height() == 0) {
+            throw new IllegalArgumentException("Bounds must be non zero");
+        }
+        if (type == TYPE_FOLD) {
+            if (bounds.width() != 0 && bounds.height() != 0) {
+                throw new IllegalArgumentException("Bounding rectangle must be either zero-wide "
+                        + "or zero-high for features of type " + typeToString(type));
+            }
+
+            if ((bounds.width() != 0 && bounds.left != 0)
+                    || (bounds.height() != 0 && bounds.top != 0)) {
+                throw new IllegalArgumentException("Bounding rectangle must span the entire "
+                        + "window space for features of type " + typeToString(type));
+            }
+        } else if (type == TYPE_HINGE) {
+            if (bounds.left != 0 && bounds.top != 0) {
+                throw new IllegalArgumentException("Bounding rectangle must span the entire "
+                        + "window space for features of type " + typeToString(type));
+            }
+        }
+    }
+
+    @NonNull
+    private static String typeToString(int type) {
+        switch (type) {
+            case TYPE_FOLD:
+                return "FOLD";
+            case TYPE_HINGE:
+                return "HINGE";
+            default:
+                return "Unknown feature type (" + type + ")";
+        }
+    }
+
+    @NonNull
+    private static String stateToString(int state) {
+        switch (state) {
+            case STATE_FLAT:
+                return "FLAT";
+            case STATE_FLIPPED:
+                return "FLIPPED";
+            case STATE_HALF_OPENED:
+                return "HALF_OPENED";
+            default:
+                return "Unknown feature state (" + state + ")";
+        }
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "ExtensionDisplayFoldFeature { " + mBounds
+                + ", type=" + typeToString(getType()) + ", state=" + stateToString(mState) + " }";
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof ExtensionFoldingFeature)) {
+            return false;
+        }
+        final ExtensionFoldingFeature other = (ExtensionFoldingFeature) obj;
+        if (mType != other.mType) {
+            return false;
+        }
+        if (mState != other.mState) {
+            return false;
+        }
+        return mBounds.equals(other.mBounds);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mBounds.hashCode();
+        result = 31 * result + mType;
+        result = 31 * result + mState;
+        return result;
+    }
+}
diff --git a/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionWindowLayoutInfo.java b/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionWindowLayoutInfo.java
index 69e7238..0afefc7 100644
--- a/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionWindowLayoutInfo.java
+++ b/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionWindowLayoutInfo.java
@@ -16,8 +16,6 @@
 
 package androidx.window.extensions;
 
-import android.content.Context;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
@@ -33,7 +31,6 @@
      * List of display features within the window.
      * <p>NOTE: All display features returned with this container must be cropped to the application
      * window and reported within the coordinate space of the window that was provided by the app.
-     * @see ExtensionInterface#getWindowLayoutInfo(Context)
      */
     @NonNull
     private List<ExtensionDisplayFeature> mDisplayFeatures;
diff --git a/window/window-samples/src/main/java/androidx/window/sample/DisplayFeaturesActivity.kt b/window/window-samples/src/main/java/androidx/window/sample/DisplayFeaturesActivity.kt
index 77bc4de..5710f79 100644
--- a/window/window-samples/src/main/java/androidx/window/sample/DisplayFeaturesActivity.kt
+++ b/window/window-samples/src/main/java/androidx/window/sample/DisplayFeaturesActivity.kt
@@ -22,8 +22,7 @@
 import android.widget.FrameLayout
 import android.widget.TextView
 import androidx.core.util.Consumer
-import androidx.window.DeviceState
-import androidx.window.DisplayFeature
+import androidx.window.FoldingFeature
 import androidx.window.WindowLayoutInfo
 import androidx.window.WindowManager
 import java.text.SimpleDateFormat
@@ -53,20 +52,12 @@
 
     override fun onStart() {
         super.onStart()
-        windowManager.registerDeviceStateChangeCallback(
-            mainThreadExecutor,
-            stateContainer.stateConsumer
-        )
-        windowManager.registerLayoutChangeCallback(
-            mainThreadExecutor,
-            stateContainer.layoutConsumer
-        )
+        windowManager.registerLayoutChangeCallback(mainThreadExecutor, stateContainer)
     }
 
     override fun onStop() {
         super.onStop()
-        windowManager.unregisterDeviceStateChangeCallback(stateContainer.stateConsumer)
-        windowManager.unregisterLayoutChangeCallback(stateContainer.layoutConsumer)
+        windowManager.unregisterLayoutChangeCallback(stateContainer)
     }
 
     /** Updates the device state and display feature positions. */
@@ -80,13 +71,6 @@
 
         // Update the UI with the current state
         val stateStringBuilder = StringBuilder()
-        // Update the current state string
-        stateContainer.lastState?.let { deviceState ->
-            stateStringBuilder.append(getString(R.string.deviceState))
-                .append(": ")
-                .append(deviceState)
-                .append("\n")
-        }
 
         stateContainer.lastLayoutInfo?.let { windowLayoutInfo ->
             stateStringBuilder.append(getString(R.string.windowLayout))
@@ -107,9 +91,10 @@
                 }
 
                 val featureView = View(this)
-                val color = when (displayFeature.type) {
-                    DisplayFeature.TYPE_FOLD -> getColor(R.color.colorFeatureFold)
-                    DisplayFeature.TYPE_HINGE -> getColor(R.color.colorFeatureHinge)
+                val foldFeature = displayFeature as? FoldingFeature
+                val color = when (foldFeature?.type) {
+                    FoldingFeature.TYPE_FOLD -> getColor(R.color.colorFeatureFold)
+                    FoldingFeature.TYPE_HINGE -> getColor(R.color.colorFeatureHinge)
                     else -> getColor(R.color.colorFeatureUnknown)
                 }
                 featureView.foreground = ColorDrawable(color)
@@ -139,25 +124,10 @@
         return currentDate.toString()
     }
 
-    inner class StateContainer {
-        var lastState: DeviceState? = null
+    inner class StateContainer : Consumer<WindowLayoutInfo> {
         var lastLayoutInfo: WindowLayoutInfo? = null
 
-        val stateConsumer: Consumer<DeviceState>
-        val layoutConsumer: Consumer<WindowLayoutInfo>
-
-        init {
-            stateConsumer = Consumer { state: DeviceState -> update(state) }
-            layoutConsumer = Consumer { layout: WindowLayoutInfo -> update(layout) }
-        }
-
-        fun update(newDeviceState: DeviceState) {
-            updateStateLog(newDeviceState)
-            lastState = newDeviceState
-            updateCurrentState()
-        }
-
-        fun update(newLayoutInfo: WindowLayoutInfo) {
+        override fun accept(newLayoutInfo: WindowLayoutInfo) {
             updateStateLog(newLayoutInfo)
             lastLayoutInfo = newLayoutInfo
             updateCurrentState()
diff --git a/window/window-samples/src/main/java/androidx/window/sample/PresentationActivity.kt b/window/window-samples/src/main/java/androidx/window/sample/PresentationActivity.kt
index 5e43a732..2cc38db 100644
--- a/window/window-samples/src/main/java/androidx/window/sample/PresentationActivity.kt
+++ b/window/window-samples/src/main/java/androidx/window/sample/PresentationActivity.kt
@@ -28,7 +28,8 @@
 import android.widget.TextView
 import android.widget.Toast
 import androidx.core.util.Consumer
-import androidx.window.DeviceState
+import androidx.window.FoldingFeature
+import androidx.window.WindowLayoutInfo
 import androidx.window.WindowManager
 
 /**
@@ -39,7 +40,7 @@
     private val TAG = "FoldablePresentation"
 
     private lateinit var windowManager: WindowManager
-    private val deviceStateChangeCallback = DeviceStateChangeCallback()
+    private val deviceStateChangeCallback = WindowLayoutInfoChangeCallback()
     private var presentation: DemoPresentation? = null
 
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -48,7 +49,7 @@
 
         windowManager = getTestBackend()?.let { backend -> WindowManager(this, backend) }
             ?: WindowManager(this)
-        windowManager.registerDeviceStateChangeCallback(
+        windowManager.registerLayoutChangeCallback(
             mainThreadExecutor,
             deviceStateChangeCallback
         )
@@ -56,7 +57,7 @@
 
     override fun onDestroy() {
         super.onDestroy()
-        windowManager.unregisterDeviceStateChangeCallback(deviceStateChangeCallback)
+        windowManager.unregisterLayoutChangeCallback(deviceStateChangeCallback)
     }
 
     internal fun startPresentation(context: Context) {
@@ -137,26 +138,36 @@
     }
 
     /**
-     * Updates the display of the current device state.
+     * Updates the display of the current fold feature state.
      */
-    internal fun updateCurrentState(deviceState: DeviceState) {
+    internal fun updateCurrentState(info: WindowLayoutInfo) {
         val stateStringBuilder = StringBuilder()
+
         stateStringBuilder.append(getString(R.string.deviceState))
             .append(": ")
-            .append(deviceState)
-            .append("\n")
+
+        info.displayFeatures
+            .mapNotNull { it as? FoldingFeature }
+            .forEach { feature ->
+                stateStringBuilder.append(feature.stateString())
+                    .append("\n")
+            }
 
         findViewById<TextView>(R.id.currentState).text = stateStringBuilder.toString()
     }
 
-    inner class DeviceStateChangeCallback : Consumer<DeviceState> {
-        override fun accept(newDeviceState: DeviceState) {
-            updateCurrentState(newDeviceState)
-            if (newDeviceState.posture == DeviceState.POSTURE_CLOSED) {
-                startPresentation(this@PresentationActivity)
-            } else {
-                stopPresentation(null)
-            }
+    private fun FoldingFeature.stateString(): String {
+        return when (state) {
+            FoldingFeature.STATE_FLAT -> "FLAT"
+            FoldingFeature.STATE_FLIPPED -> "FLIPPED"
+            FoldingFeature.STATE_HALF_OPENED -> "HALF_OPENED"
+            else -> "Unknown feature state ($state)"
+        }
+    }
+
+    inner class WindowLayoutInfoChangeCallback : Consumer<WindowLayoutInfo> {
+        override fun accept(info: WindowLayoutInfo) {
+            updateCurrentState(info)
         }
     }
 }
diff --git a/window/window-samples/src/main/java/androidx/window/sample/SplitLayout.kt b/window/window-samples/src/main/java/androidx/window/sample/SplitLayout.kt
index 954c798..cc0b341 100644
--- a/window/window-samples/src/main/java/androidx/window/sample/SplitLayout.kt
+++ b/window/window-samples/src/main/java/androidx/window/sample/SplitLayout.kt
@@ -23,8 +23,10 @@
 import android.view.View.MeasureSpec.AT_MOST
 import android.view.View.MeasureSpec.EXACTLY
 import android.widget.FrameLayout
-import androidx.window.DisplayFeature.TYPE_FOLD
-import androidx.window.DisplayFeature.TYPE_HINGE
+import androidx.window.DisplayFeature
+import androidx.window.FoldingFeature
+import androidx.window.FoldingFeature.TYPE_FOLD
+import androidx.window.FoldingFeature.TYPE_HINGE
 import androidx.window.WindowLayoutInfo
 
 /**
@@ -41,9 +43,11 @@
     private var lastHeightMeasureSpec: Int = 0
 
     constructor(context: Context) : super(context)
+
     constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
         setAttributes(attrs)
     }
+
     constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
         context,
         attrs,
@@ -125,46 +129,43 @@
         val paddedWidth = width - paddingLeft - paddingRight
         val paddedHeight = height - paddingTop - paddingBottom
 
-        for (feature in windowLayoutInfo?.displayFeatures!!) {
-            // Only a hinge or a fold can split the area in two
-            if (feature.type != TYPE_FOLD && feature.type != TYPE_HINGE) {
-                continue
-            }
+        windowLayoutInfo?.displayFeatures
+            ?.firstOrNull { feature -> isValidFoldFeature(feature) }
+            ?.let { feature ->
+                getFeaturePositionInViewRect(feature, this)?.let {
+                    if (feature.bounds.left == 0) { // Horizontal layout
+                        val topRect = Rect(
+                            paddingLeft, paddingTop,
+                            paddingLeft + paddedWidth, it.top
+                        )
+                        val bottomRect = Rect(
+                            paddingLeft, it.bottom,
+                            paddingLeft + paddedWidth, paddingTop + paddedHeight
+                        )
 
-            val splitRect = getFeaturePositionInViewRect(feature, this) ?: continue
+                        if (measureAndCheckMinSize(topRect, startView) &&
+                            measureAndCheckMinSize(bottomRect, endView)
+                        ) {
+                            return arrayOf(topRect, bottomRect)
+                        }
+                    } else if (feature.bounds.top == 0) { // Vertical layout
+                        val leftRect = Rect(
+                            paddingLeft, paddingTop,
+                            it.left, paddingTop + paddedHeight
+                        )
+                        val rightRect = Rect(
+                            it.right, paddingTop,
+                            paddingLeft + paddedWidth, paddingTop + paddedHeight
+                        )
 
-            if (feature.bounds.left == 0) { // Horizontal layout
-                val topRect = Rect(
-                    paddingLeft, paddingTop,
-                    paddingLeft + paddedWidth, splitRect.top
-                )
-                val bottomRect = Rect(
-                    paddingLeft, splitRect.bottom,
-                    paddingLeft + paddedWidth, paddingTop + paddedHeight
-                )
-
-                if (measureAndCheckMinSize(topRect, startView) &&
-                    measureAndCheckMinSize(bottomRect, endView)
-                ) {
-                    return arrayOf(topRect, bottomRect)
-                }
-            } else if (feature.bounds.top == 0) { // Vertical layout
-                val leftRect = Rect(
-                    paddingLeft, paddingTop,
-                    splitRect.left, paddingTop + paddedHeight
-                )
-                val rightRect = Rect(
-                    splitRect.right, paddingTop,
-                    paddingLeft + paddedWidth, paddingTop + paddedHeight
-                )
-
-                if (measureAndCheckMinSize(leftRect, startView) &&
-                    measureAndCheckMinSize(rightRect, endView)
-                ) {
-                    return arrayOf(leftRect, rightRect)
+                        if (measureAndCheckMinSize(leftRect, startView) &&
+                            measureAndCheckMinSize(rightRect, endView)
+                        ) {
+                            return arrayOf(leftRect, rightRect)
+                        }
+                    }
                 }
             }
-        }
 
         // We have tried to fit the children and measured them previously. Since they didn't fit,
         // we need to measure again to update the stored values.
@@ -191,4 +192,10 @@
         return childView.measuredWidthAndState and MEASURED_STATE_TOO_SMALL == 0 &&
             childView.measuredHeightAndState and MEASURED_STATE_TOO_SMALL == 0
     }
+
+    private fun isValidFoldFeature(displayFeature: DisplayFeature): Boolean {
+        val feature = displayFeature as? FoldingFeature ?: return false
+        return (feature.type == TYPE_FOLD || feature.type == TYPE_HINGE) &&
+            getFeaturePositionInViewRect(feature, this) != null
+    }
 }
\ No newline at end of file
diff --git a/window/window-samples/src/main/java/androidx/window/sample/backend/MidScreenFoldBackend.kt b/window/window-samples/src/main/java/androidx/window/sample/backend/MidScreenFoldBackend.kt
index 5ee6df2..27754f4 100644
--- a/window/window-samples/src/main/java/androidx/window/sample/backend/MidScreenFoldBackend.kt
+++ b/window/window-samples/src/main/java/androidx/window/sample/backend/MidScreenFoldBackend.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATION") // TODO(b/173739071) Remove DeviceState
+
 package androidx.window.sample.backend
 
 import android.app.Activity
@@ -22,7 +24,7 @@
 import androidx.core.util.Consumer
 import androidx.window.DeviceState
 import androidx.window.DisplayFeature
-import androidx.window.DisplayFeature.TYPE_FOLD
+import androidx.window.FoldingFeature
 import androidx.window.WindowBackend
 import androidx.window.WindowLayoutInfo
 import java.util.concurrent.Executor
@@ -53,18 +55,16 @@
         SHORT_DIMENSION
     }
 
-    private fun getDeviceState(): DeviceState {
-        return DeviceState.Builder().setPosture(DeviceState.POSTURE_OPENED).build()
-    }
-
     private fun getWindowLayoutInfo(activity: Activity): WindowLayoutInfo {
         val windowSize = activity.calculateWindowSizeExt()
         val featureRect = foldRect(windowSize)
 
-        val displayFeature = DisplayFeature.Builder()
-            .setBounds(featureRect)
-            .setType(TYPE_FOLD)
-            .build()
+        val displayFeature =
+            FoldingFeature(
+                featureRect,
+                FoldingFeature.TYPE_FOLD,
+                FoldingFeature.STATE_FLAT
+            )
         val featureList = ArrayList<DisplayFeature>()
         featureList.add(displayFeature)
         return WindowLayoutInfo.Builder().setDisplayFeatures(featureList).build()
@@ -96,9 +96,7 @@
     override fun registerDeviceStateChangeCallback(
         executor: Executor,
         callback: Consumer<DeviceState>
-    ) {
-        executor.execute { callback.accept(getDeviceState()) }
-    }
+    ) {}
 
     override fun unregisterDeviceStateChangeCallback(callback: Consumer<DeviceState>) {
     }
diff --git a/window/window/api/current.txt b/window/window/api/current.txt
index 3abcf8b..3055076 100644
--- a/window/window/api/current.txt
+++ b/window/window/api/current.txt
@@ -1,35 +1,37 @@
 // Signature format: 4.0
 package androidx.window {
 
-  public final class DeviceState {
-    method public int getPosture();
-    field public static final int POSTURE_CLOSED = 1; // 0x1
-    field public static final int POSTURE_FLIPPED = 4; // 0x4
-    field public static final int POSTURE_HALF_OPENED = 2; // 0x2
-    field public static final int POSTURE_OPENED = 3; // 0x3
-    field public static final int POSTURE_UNKNOWN = 0; // 0x0
+  @Deprecated public final class DeviceState {
+    method @Deprecated public int getPosture();
+    field @Deprecated public static final int POSTURE_CLOSED = 1; // 0x1
+    field @Deprecated public static final int POSTURE_FLIPPED = 4; // 0x4
+    field @Deprecated public static final int POSTURE_HALF_OPENED = 2; // 0x2
+    field @Deprecated public static final int POSTURE_OPENED = 3; // 0x3
+    field @Deprecated public static final int POSTURE_UNKNOWN = 0; // 0x0
   }
 
-  public static final class DeviceState.Builder {
-    ctor public DeviceState.Builder();
-    method public androidx.window.DeviceState build();
-    method public androidx.window.DeviceState.Builder setPosture(int);
+  @Deprecated public static final class DeviceState.Builder {
+    ctor @Deprecated public DeviceState.Builder();
+    method @Deprecated public androidx.window.DeviceState build();
+    method @Deprecated public androidx.window.DeviceState.Builder setPosture(int);
   }
 
-  public final class DisplayFeature {
+  public interface DisplayFeature {
     method public android.graphics.Rect getBounds();
+  }
+
+  public class FoldingFeature implements androidx.window.DisplayFeature {
+    ctor public FoldingFeature(android.graphics.Rect, int, int);
+    method public android.graphics.Rect getBounds();
+    method public int getState();
     method public int getType();
+    field public static final int STATE_FLAT = 1; // 0x1
+    field public static final int STATE_FLIPPED = 3; // 0x3
+    field public static final int STATE_HALF_OPENED = 2; // 0x2
     field public static final int TYPE_FOLD = 1; // 0x1
     field public static final int TYPE_HINGE = 2; // 0x2
   }
 
-  public static final class DisplayFeature.Builder {
-    ctor public DisplayFeature.Builder();
-    method public androidx.window.DisplayFeature build();
-    method public androidx.window.DisplayFeature.Builder setBounds(android.graphics.Rect);
-    method public androidx.window.DisplayFeature.Builder setType(int);
-  }
-
   public interface WindowBackend {
     method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void registerLayoutChangeCallback(android.app.Activity, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
@@ -52,9 +54,9 @@
     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 @Deprecated 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 @Deprecated public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
   }
 
diff --git a/window/window/api/public_plus_experimental_current.txt b/window/window/api/public_plus_experimental_current.txt
index 3abcf8b..3055076 100644
--- a/window/window/api/public_plus_experimental_current.txt
+++ b/window/window/api/public_plus_experimental_current.txt
@@ -1,35 +1,37 @@
 // Signature format: 4.0
 package androidx.window {
 
-  public final class DeviceState {
-    method public int getPosture();
-    field public static final int POSTURE_CLOSED = 1; // 0x1
-    field public static final int POSTURE_FLIPPED = 4; // 0x4
-    field public static final int POSTURE_HALF_OPENED = 2; // 0x2
-    field public static final int POSTURE_OPENED = 3; // 0x3
-    field public static final int POSTURE_UNKNOWN = 0; // 0x0
+  @Deprecated public final class DeviceState {
+    method @Deprecated public int getPosture();
+    field @Deprecated public static final int POSTURE_CLOSED = 1; // 0x1
+    field @Deprecated public static final int POSTURE_FLIPPED = 4; // 0x4
+    field @Deprecated public static final int POSTURE_HALF_OPENED = 2; // 0x2
+    field @Deprecated public static final int POSTURE_OPENED = 3; // 0x3
+    field @Deprecated public static final int POSTURE_UNKNOWN = 0; // 0x0
   }
 
-  public static final class DeviceState.Builder {
-    ctor public DeviceState.Builder();
-    method public androidx.window.DeviceState build();
-    method public androidx.window.DeviceState.Builder setPosture(int);
+  @Deprecated public static final class DeviceState.Builder {
+    ctor @Deprecated public DeviceState.Builder();
+    method @Deprecated public androidx.window.DeviceState build();
+    method @Deprecated public androidx.window.DeviceState.Builder setPosture(int);
   }
 
-  public final class DisplayFeature {
+  public interface DisplayFeature {
     method public android.graphics.Rect getBounds();
+  }
+
+  public class FoldingFeature implements androidx.window.DisplayFeature {
+    ctor public FoldingFeature(android.graphics.Rect, int, int);
+    method public android.graphics.Rect getBounds();
+    method public int getState();
     method public int getType();
+    field public static final int STATE_FLAT = 1; // 0x1
+    field public static final int STATE_FLIPPED = 3; // 0x3
+    field public static final int STATE_HALF_OPENED = 2; // 0x2
     field public static final int TYPE_FOLD = 1; // 0x1
     field public static final int TYPE_HINGE = 2; // 0x2
   }
 
-  public static final class DisplayFeature.Builder {
-    ctor public DisplayFeature.Builder();
-    method public androidx.window.DisplayFeature build();
-    method public androidx.window.DisplayFeature.Builder setBounds(android.graphics.Rect);
-    method public androidx.window.DisplayFeature.Builder setType(int);
-  }
-
   public interface WindowBackend {
     method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void registerLayoutChangeCallback(android.app.Activity, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
@@ -52,9 +54,9 @@
     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 @Deprecated 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 @Deprecated public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
   }
 
diff --git a/window/window/api/restricted_current.txt b/window/window/api/restricted_current.txt
index 3abcf8b..3055076 100644
--- a/window/window/api/restricted_current.txt
+++ b/window/window/api/restricted_current.txt
@@ -1,35 +1,37 @@
 // Signature format: 4.0
 package androidx.window {
 
-  public final class DeviceState {
-    method public int getPosture();
-    field public static final int POSTURE_CLOSED = 1; // 0x1
-    field public static final int POSTURE_FLIPPED = 4; // 0x4
-    field public static final int POSTURE_HALF_OPENED = 2; // 0x2
-    field public static final int POSTURE_OPENED = 3; // 0x3
-    field public static final int POSTURE_UNKNOWN = 0; // 0x0
+  @Deprecated public final class DeviceState {
+    method @Deprecated public int getPosture();
+    field @Deprecated public static final int POSTURE_CLOSED = 1; // 0x1
+    field @Deprecated public static final int POSTURE_FLIPPED = 4; // 0x4
+    field @Deprecated public static final int POSTURE_HALF_OPENED = 2; // 0x2
+    field @Deprecated public static final int POSTURE_OPENED = 3; // 0x3
+    field @Deprecated public static final int POSTURE_UNKNOWN = 0; // 0x0
   }
 
-  public static final class DeviceState.Builder {
-    ctor public DeviceState.Builder();
-    method public androidx.window.DeviceState build();
-    method public androidx.window.DeviceState.Builder setPosture(int);
+  @Deprecated public static final class DeviceState.Builder {
+    ctor @Deprecated public DeviceState.Builder();
+    method @Deprecated public androidx.window.DeviceState build();
+    method @Deprecated public androidx.window.DeviceState.Builder setPosture(int);
   }
 
-  public final class DisplayFeature {
+  public interface DisplayFeature {
     method public android.graphics.Rect getBounds();
+  }
+
+  public class FoldingFeature implements androidx.window.DisplayFeature {
+    ctor public FoldingFeature(android.graphics.Rect, int, int);
+    method public android.graphics.Rect getBounds();
+    method public int getState();
     method public int getType();
+    field public static final int STATE_FLAT = 1; // 0x1
+    field public static final int STATE_FLIPPED = 3; // 0x3
+    field public static final int STATE_HALF_OPENED = 2; // 0x2
     field public static final int TYPE_FOLD = 1; // 0x1
     field public static final int TYPE_HINGE = 2; // 0x2
   }
 
-  public static final class DisplayFeature.Builder {
-    ctor public DisplayFeature.Builder();
-    method public androidx.window.DisplayFeature build();
-    method public androidx.window.DisplayFeature.Builder setBounds(android.graphics.Rect);
-    method public androidx.window.DisplayFeature.Builder setType(int);
-  }
-
   public interface WindowBackend {
     method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void registerLayoutChangeCallback(android.app.Activity, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
@@ -52,9 +54,9 @@
     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 @Deprecated 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 @Deprecated public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
   }
 
diff --git a/window/window/build.gradle b/window/window/build.gradle
index 30a2fa6..4b98a67 100644
--- a/window/window/build.gradle
+++ b/window/window/build.gradle
@@ -18,12 +18,16 @@
 import androidx.build.LibraryVersions
 import androidx.build.Publish
 
+import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_CORE
 import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_EXT_JUNIT
 import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_RULES
 import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_RUNNER
 import static androidx.build.dependencies.DependenciesKt.DEXMAKER_MOCKITO
+import static androidx.build.dependencies.DependenciesKt.JUNIT
 import static androidx.build.dependencies.DependenciesKt.MOCKITO_CORE
+import static androidx.build.dependencies.DependenciesKt.ROBOLECTRIC
 import static androidx.build.dependencies.DependenciesKt.TRUTH
+import static androidx.build.dependencies.DependenciesKt.getKOTLIN_STDLIB
 
 plugins {
     id("AndroidXPlugin")
@@ -47,6 +51,16 @@
     compileOnly(project(":window:window-extensions"))
     compileOnly(project(":window:window-sidecar"))
 
+    testImplementation(KOTLIN_STDLIB)
+    testImplementation(ANDROIDX_TEST_CORE)
+    testImplementation(ANDROIDX_TEST_RUNNER)
+    testImplementation(JUNIT)
+    testImplementation(TRUTH)
+    testImplementation(ROBOLECTRIC)
+    testImplementation(MOCKITO_CORE)
+    testImplementation(compileOnly(project(":window:window-extensions")))
+    testImplementation(compileOnly(project(":window:window-sidecar")))
+
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
diff --git a/window/window/src/androidTest/java/androidx/window/DisplayFeatureTest.java b/window/window/src/androidTest/java/androidx/window/DisplayFeatureTest.java
deleted file mode 100644
index 93033ae..0000000
--- a/window/window/src/androidTest/java/androidx/window/DisplayFeatureTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window;
-
-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 DisplayFeature} class. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class DisplayFeatureTest {
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testBuilder_empty() {
-        new DisplayFeature.Builder().build();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testBuilder_foldWithNonZeroArea() {
-        DisplayFeature feature = new DisplayFeature.Builder()
-                .setBounds(new Rect(10, 0, 20, 30))
-                .setType(DisplayFeature.TYPE_FOLD).build();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testBuilder_horizontalHingeWithNonZeroOrigin() {
-        DisplayFeature horizontalHinge = new DisplayFeature.Builder()
-                .setBounds(new Rect(1, 10, 20, 10))
-                .setType(DisplayFeature.TYPE_HINGE).build();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testBuilder_verticalHingeWithNonZeroOrigin() {
-        DisplayFeature verticalHinge = new DisplayFeature.Builder()
-                .setBounds(new Rect(10, 1, 10, 20))
-                .setType(DisplayFeature.TYPE_HINGE).build();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testBuilder_horizontalFoldWithNonZeroOrigin() {
-        DisplayFeature horizontalFold = new DisplayFeature.Builder()
-                .setBounds(new Rect(1, 10, 20, 10))
-                .setType(DisplayFeature.TYPE_FOLD).build();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testBuilder_verticalFoldWithNonZeroOrigin() {
-        DisplayFeature verticalFold = new DisplayFeature.Builder()
-                .setBounds(new Rect(10, 1, 10, 20))
-                .setType(DisplayFeature.TYPE_FOLD).build();
-    }
-
-    @Test
-    public void testBuilder_setBoundsAndType() {
-        DisplayFeature.Builder builder = new DisplayFeature.Builder();
-        Rect bounds = new Rect(0, 10, 30, 10);
-        builder.setBounds(bounds);
-        builder.setType(DisplayFeature.TYPE_HINGE);
-        DisplayFeature feature = builder.build();
-
-        assertEquals(bounds, feature.getBounds());
-        assertEquals(DisplayFeature.TYPE_HINGE, feature.getType());
-    }
-
-    @Test
-    public void testEquals_sameAttributes() {
-        Rect bounds = new Rect(1, 0, 1, 10);
-        int type = DisplayFeature.TYPE_FOLD;
-
-        DisplayFeature original = new DisplayFeature(bounds, type);
-        DisplayFeature copy = new DisplayFeature(bounds, type);
-
-        assertEquals(original, copy);
-    }
-
-    @Test
-    public void testEquals_differentRect() {
-        Rect originalRect = new Rect(1, 0, 1, 10);
-        Rect otherRect = new Rect(2, 0, 2, 10);
-        int type = DisplayFeature.TYPE_FOLD;
-
-        DisplayFeature original = new DisplayFeature(originalRect, type);
-        DisplayFeature other = new DisplayFeature(otherRect, type);
-
-        assertNotEquals(original, other);
-    }
-
-    @Test
-    public void testEquals_differentType() {
-        Rect rect = new Rect(1, 0, 1, 10);
-        int originalType = DisplayFeature.TYPE_FOLD;
-        int otherType = DisplayFeature.TYPE_HINGE;
-
-        DisplayFeature original = new DisplayFeature(rect, originalType);
-        DisplayFeature other = new DisplayFeature(rect, otherType);
-
-        assertNotEquals(original, other);
-    }
-
-    @Test
-    public void testHashCode_matchesIfEqual() {
-        Rect originalRect = new Rect(1, 0, 1, 10);
-        Rect matchingRect = new Rect(1, 0, 1, 10);
-        int type = DisplayFeature.TYPE_FOLD;
-
-        DisplayFeature original = new DisplayFeature(originalRect, type);
-        DisplayFeature matching = new DisplayFeature(matchingRect, type);
-
-        assertEquals(original, matching);
-        assertEquals(original.hashCode(), matching.hashCode());
-    }
-}
diff --git a/window/window/src/androidTest/java/androidx/window/ExtensionAdapterTest.java b/window/window/src/androidTest/java/androidx/window/ExtensionAdapterTest.java
index c6e6348..9932500 100644
--- a/window/window/src/androidTest/java/androidx/window/ExtensionAdapterTest.java
+++ b/window/window/src/androidTest/java/androidx/window/ExtensionAdapterTest.java
@@ -25,6 +25,7 @@
 
 import androidx.window.extensions.ExtensionDeviceState;
 import androidx.window.extensions.ExtensionDisplayFeature;
+import androidx.window.extensions.ExtensionFoldingFeature;
 import androidx.window.extensions.ExtensionWindowLayoutInfo;
 
 import org.junit.After;
@@ -64,8 +65,8 @@
     public void testTranslate_validFeature() {
         Activity mockActivity = mock(Activity.class);
         Rect bounds = new Rect(WINDOW_BOUNDS.left, 0, WINDOW_BOUNDS.right, 0);
-        ExtensionDisplayFeature foldFeature = new ExtensionDisplayFeature(bounds,
-                ExtensionDisplayFeature.TYPE_FOLD);
+        ExtensionDisplayFeature foldFeature = new ExtensionFoldingFeature(bounds,
+                ExtensionFoldingFeature.TYPE_FOLD, ExtensionFoldingFeature.STATE_FLAT);
 
         List<ExtensionDisplayFeature> extensionDisplayFeatures = new ArrayList<>();
         extensionDisplayFeatures.add(foldFeature);
@@ -73,7 +74,8 @@
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
 
         List<DisplayFeature> expectedFeatures = new ArrayList<>();
-        expectedFeatures.add(new DisplayFeature(foldFeature.getBounds(), DisplayFeature.TYPE_FOLD));
+        expectedFeatures.add(new FoldingFeature(foldFeature.getBounds(), FoldingFeature.TYPE_FOLD,
+                FoldingFeature.STATE_FLAT));
         WindowLayoutInfo expected = new WindowLayoutInfo(expectedFeatures);
 
         ExtensionAdapter adapter = new ExtensionAdapter();
@@ -85,59 +87,17 @@
 
     @Test
     @Override
-    public void testTranslateWindowLayoutInfo_filterRemovesEmptyBoundsFeature() {
-        List<ExtensionDisplayFeature> extensionDisplayFeatures = new ArrayList<>();
-        extensionDisplayFeatures.add(
-                new ExtensionDisplayFeature(new Rect(), ExtensionDisplayFeature.TYPE_FOLD));
-
-        ExtensionAdapter adapter = new ExtensionAdapter();
-        ExtensionWindowLayoutInfo windowLayoutInfo =
-                new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
-        Activity mockActivity = mock(Activity.class);
-
-        WindowLayoutInfo actual = adapter.translate(mockActivity, windowLayoutInfo);
-
-        assertTrue("Remove empty bounds feature", actual.getDisplayFeatures().isEmpty());
-    }
-
-
-    @Test
-    @Override
-    public void testTranslateWindowLayoutInfo_filterRemovesNonEmptyAreaFoldFeature() {
-        List<ExtensionDisplayFeature> extensionDisplayFeatures = new ArrayList<>();
-        Rect fullWidthBounds = new Rect(0, 1, WINDOW_BOUNDS.width(), 2);
-        Rect fullHeightBounds = new Rect(1, 0, 2, WINDOW_BOUNDS.height());
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullWidthBounds,
-                ExtensionDisplayFeature.TYPE_FOLD));
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullHeightBounds,
-                ExtensionDisplayFeature.TYPE_FOLD));
-
-        ExtensionAdapter extensionCallbackAdapter = new ExtensionAdapter();
-        ExtensionWindowLayoutInfo windowLayoutInfo =
-                new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
-        Activity mockActivity = mock(Activity.class);
-
-        WindowLayoutInfo actual = extensionCallbackAdapter.translate(mockActivity,
-                windowLayoutInfo);
-
-        assertTrue("Remove non empty area fold feature", actual.getDisplayFeatures().isEmpty());
-    }
-
-    @Test
-    @Override
     public void testTranslateWindowLayoutInfo_filterRemovesHingeFeatureNotSpanningFullDimension() {
         List<ExtensionDisplayFeature> extensionDisplayFeatures = new ArrayList<>();
         Rect fullWidthBounds = new Rect(WINDOW_BOUNDS.left, WINDOW_BOUNDS.top,
                 WINDOW_BOUNDS.right / 2, 2);
         Rect fullHeightBounds = new Rect(WINDOW_BOUNDS.left, WINDOW_BOUNDS.top, 2,
                 WINDOW_BOUNDS.bottom / 2);
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullWidthBounds,
-                ExtensionDisplayFeature.TYPE_HINGE));
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullHeightBounds,
-                ExtensionDisplayFeature.TYPE_HINGE));
+        extensionDisplayFeatures.add(new ExtensionFoldingFeature(fullWidthBounds,
+                ExtensionFoldingFeature.TYPE_HINGE, ExtensionFoldingFeature.STATE_FLAT));
+        extensionDisplayFeatures.add(new ExtensionFoldingFeature(fullHeightBounds,
+                ExtensionFoldingFeature.TYPE_HINGE, ExtensionFoldingFeature.STATE_FLAT));
 
-        ExtensionInterfaceCompat.ExtensionCallbackInterface mockCallback = mock(
-                ExtensionInterfaceCompat.ExtensionCallbackInterface.class);
         ExtensionAdapter extensionCallbackAdapter = new ExtensionAdapter();
         ExtensionWindowLayoutInfo windowLayoutInfo =
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
@@ -159,10 +119,10 @@
                 WINDOW_BOUNDS.right / 2, WINDOW_BOUNDS.top);
         Rect fullHeightBounds = new Rect(WINDOW_BOUNDS.left, WINDOW_BOUNDS.top, WINDOW_BOUNDS.left,
                 WINDOW_BOUNDS.bottom / 2);
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullWidthBounds,
-                ExtensionDisplayFeature.TYPE_HINGE));
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullHeightBounds,
-                ExtensionDisplayFeature.TYPE_HINGE));
+        extensionDisplayFeatures.add(new ExtensionFoldingFeature(fullWidthBounds,
+                ExtensionFoldingFeature.TYPE_HINGE, ExtensionFoldingFeature.STATE_FLAT));
+        extensionDisplayFeatures.add(new ExtensionFoldingFeature(fullHeightBounds,
+                ExtensionFoldingFeature.TYPE_HINGE, ExtensionFoldingFeature.STATE_FLAT));
 
         ExtensionAdapter adapter = new ExtensionAdapter();
         ExtensionWindowLayoutInfo windowLayoutInfo =
@@ -183,20 +143,14 @@
         List<DeviceState> values = new ArrayList<>();
 
         values.add(extensionCallbackAdapter.translate(new ExtensionDeviceState(
-                ExtensionDeviceState.POSTURE_UNKNOWN)));
-        values.add(extensionCallbackAdapter.translate(new ExtensionDeviceState(
-                ExtensionDeviceState.POSTURE_CLOSED)));
-        values.add(extensionCallbackAdapter.translate(new ExtensionDeviceState(
                 ExtensionDeviceState.POSTURE_HALF_OPENED)));
         values.add(extensionCallbackAdapter.translate(new ExtensionDeviceState(
                 ExtensionDeviceState.POSTURE_OPENED)));
         values.add(extensionCallbackAdapter.translate(new ExtensionDeviceState(
                 ExtensionDeviceState.POSTURE_FLIPPED)));
 
-        assertEquals(DeviceState.POSTURE_UNKNOWN, values.get(0).getPosture());
-        assertEquals(DeviceState.POSTURE_CLOSED, values.get(1).getPosture());
-        assertEquals(DeviceState.POSTURE_HALF_OPENED, values.get(2).getPosture());
-        assertEquals(DeviceState.POSTURE_OPENED, values.get(3).getPosture());
-        assertEquals(DeviceState.POSTURE_FLIPPED, values.get(4).getPosture());
+        assertEquals(DeviceState.POSTURE_HALF_OPENED, values.get(0).getPosture());
+        assertEquals(DeviceState.POSTURE_OPENED, values.get(1).getPosture());
+        assertEquals(DeviceState.POSTURE_FLIPPED, values.get(2).getPosture());
     }
 }
diff --git a/window/window/src/androidTest/java/androidx/window/ExtensionCompatTest.java b/window/window/src/androidTest/java/androidx/window/ExtensionCompatTest.java
index f2d1f92..c0d318e 100644
--- a/window/window/src/androidTest/java/androidx/window/ExtensionCompatTest.java
+++ b/window/window/src/androidTest/java/androidx/window/ExtensionCompatTest.java
@@ -17,11 +17,12 @@
 package androidx.window;
 
 import static androidx.window.ExtensionInterfaceCompat.ExtensionCallbackInterface;
-import static androidx.window.TestBoundUtil.invalidFoldBounds;
-import static androidx.window.TestBoundUtil.invalidHingeBounds;
-import static androidx.window.TestBoundUtil.validFoldBound;
+import static androidx.window.TestBoundsUtil.invalidFoldBounds;
+import static androidx.window.TestBoundsUtil.invalidHingeBounds;
+import static androidx.window.TestBoundsUtil.validFoldBound;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
@@ -37,6 +38,7 @@
 import androidx.test.filters.LargeTest;
 import androidx.window.extensions.ExtensionDeviceState;
 import androidx.window.extensions.ExtensionDisplayFeature;
+import androidx.window.extensions.ExtensionFoldingFeature;
 import androidx.window.extensions.ExtensionInterface;
 import androidx.window.extensions.ExtensionWindowLayoutInfo;
 
@@ -62,19 +64,17 @@
     private static final Rect WINDOW_BOUNDS = new Rect(0, 0, 50, 100);
 
     ExtensionCompat mExtensionCompat;
-    private ExtensionInterface mMockExtensionInterface;
     private Activity mActivity;
 
     @Before
     public void setUp() {
-        mMockExtensionInterface = mock(ExtensionInterface.class);
-        mExtensionCompat = new ExtensionCompat(mMockExtensionInterface, new ExtensionAdapter());
+        mExtensionCompat = new ExtensionCompat(mock(ExtensionInterface.class),
+                new ExtensionAdapter());
         mActivity = mock(Activity.class);
 
         TestWindowBoundsHelper mWindowBoundsHelper = new TestWindowBoundsHelper();
         mWindowBoundsHelper.setCurrentBounds(WINDOW_BOUNDS);
         WindowBoundsHelper.setForTesting(mWindowBoundsHelper);
-
     }
 
     @After
@@ -141,7 +141,8 @@
         mExtensionCompat.onWindowLayoutChangeListenerAdded(mActivity);
         Rect bounds = new Rect(WINDOW_BOUNDS.left, WINDOW_BOUNDS.top, WINDOW_BOUNDS.width(), 1);
         ExtensionDisplayFeature extensionDisplayFeature =
-                new ExtensionDisplayFeature(bounds, ExtensionDisplayFeature.TYPE_HINGE);
+                new ExtensionFoldingFeature(bounds, ExtensionFoldingFeature.TYPE_HINGE,
+                        ExtensionFoldingFeature.STATE_FLIPPED);
         List<ExtensionDisplayFeature> displayFeatures = new ArrayList<>();
         displayFeatures.add(extensionDisplayFeature);
         ExtensionWindowLayoutInfo extensionWindowLayoutInfo =
@@ -156,7 +157,10 @@
         WindowLayoutInfo capturedLayout = windowLayoutInfoCaptor.getValue();
         assertEquals(1, capturedLayout.getDisplayFeatures().size());
         DisplayFeature capturedDisplayFeature = capturedLayout.getDisplayFeatures().get(0);
-        assertEquals(DisplayFeature.TYPE_HINGE, capturedDisplayFeature.getType());
+
+        FoldingFeature foldingFeature = (FoldingFeature) capturedDisplayFeature;
+        assertNotNull(foldingFeature);
+        assertEquals(FoldingFeature.TYPE_HINGE, foldingFeature.getType());
         assertEquals(bounds, capturedDisplayFeature.getBounds());
     }
 
@@ -263,13 +267,15 @@
             List<ExtensionDisplayFeature> malformedFeatures = new ArrayList<>();
 
             for (Rect malformedBound : invalidFoldBounds(WINDOW_BOUNDS)) {
-                malformedFeatures.add(new ExtensionDisplayFeature(malformedBound,
-                        ExtensionDisplayFeature.TYPE_FOLD));
+                malformedFeatures.add(new ExtensionFoldingFeature(malformedBound,
+                        ExtensionFoldingFeature.TYPE_FOLD,
+                        ExtensionFoldingFeature.STATE_FLAT));
             }
 
             for (Rect malformedBound : invalidHingeBounds(WINDOW_BOUNDS)) {
-                malformedFeatures.add(new ExtensionDisplayFeature(malformedBound,
-                        ExtensionDisplayFeature.TYPE_HINGE));
+                malformedFeatures.add(new ExtensionFoldingFeature(malformedBound,
+                        ExtensionFoldingFeature.TYPE_HINGE,
+                        ExtensionFoldingFeature.STATE_FLAT));
             }
 
             return new ExtensionWindowLayoutInfo(malformedFeatures);
@@ -278,8 +284,8 @@
         private ExtensionWindowLayoutInfo validWindowLayoutInfo() {
             List<ExtensionDisplayFeature> validFeatures = new ArrayList<>();
 
-            validFeatures.add(new ExtensionDisplayFeature(validFoldBound(WINDOW_BOUNDS),
-                    ExtensionDisplayFeature.TYPE_FOLD));
+            validFeatures.add(new ExtensionFoldingFeature(validFoldBound(WINDOW_BOUNDS),
+                    ExtensionFoldingFeature.TYPE_FOLD, ExtensionFoldingFeature.STATE_FLAT));
 
             return new ExtensionWindowLayoutInfo(validFeatures);
         }
diff --git a/window/window/src/androidTest/java/androidx/window/ExtensionTest.java b/window/window/src/androidTest/java/androidx/window/ExtensionTest.java
index 3e89816..a34be4a 100644
--- a/window/window/src/androidTest/java/androidx/window/ExtensionTest.java
+++ b/window/window/src/androidTest/java/androidx/window/ExtensionTest.java
@@ -20,8 +20,6 @@
 import static androidx.window.ExtensionWindowBackend.initAndVerifyExtension;
 import static androidx.window.Version.VERSION_0_1;
 import static androidx.window.Version.VERSION_1_0;
-import static androidx.window.extensions.ExtensionDisplayFeature.TYPE_FOLD;
-import static androidx.window.extensions.ExtensionDisplayFeature.TYPE_HINGE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -35,6 +33,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -42,7 +41,7 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
 import androidx.window.extensions.ExtensionDeviceState;
-import androidx.window.extensions.ExtensionDisplayFeature;
+import androidx.window.extensions.ExtensionFoldingFeature;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -79,8 +78,6 @@
     public void testDeviceStateCallback() {
         assumeExtensionV10_V01();
         final Set<Integer> validValues = new HashSet<>();
-        validValues.add(ExtensionDeviceState.POSTURE_UNKNOWN);
-        validValues.add(ExtensionDeviceState.POSTURE_CLOSED);
         validValues.add(ExtensionDeviceState.POSTURE_FLIPPED);
         validValues.add(ExtensionDeviceState.POSTURE_HALF_OPENED);
         validValues.add(ExtensionDeviceState.POSTURE_OPENED);
@@ -89,8 +86,7 @@
         extension.setExtensionCallback(callbackInterface);
         extension.onDeviceStateListenersChanged(false);
 
-        verify(callbackInterface).onDeviceStateChanged(argThat(
-                deviceState -> validValues.contains(deviceState.getPosture())));
+        verify(callbackInterface, atLeastOnce()).onDeviceStateChanged(any());
     }
 
     @Test
@@ -106,15 +102,16 @@
     public void testDisplayFeatureDataClass() {
         assumeExtensionV10_V01();
 
-        Rect rect = new Rect(1, 2, 3, 4);
+        Rect rect = new Rect(0, 100, 100, 100);
         int type = 1;
-        ExtensionDisplayFeature displayFeature = new ExtensionDisplayFeature(rect, type);
+        int state = 1;
+        ExtensionFoldingFeature displayFeature =
+                new ExtensionFoldingFeature(rect, type, state);
         assertEquals(rect, displayFeature.getBounds());
-        assertEquals(type, displayFeature.getType());
     }
 
     @Test
-    public void testWindowLayoutInfoCallback() {
+    public void testWindowLayoutCallback() {
         assumeExtensionV10_V01();
         ExtensionInterfaceCompat extension = initAndVerifyExtension(mContext);
         ExtensionCallbackInterface callbackInterface = mock(ExtensionCallbackInterface.class);
@@ -125,7 +122,7 @@
 
         assertTrue("Layout must happen after launch", activity.waitForLayout());
 
-        verify(callbackInterface).onWindowLayoutChanged(any(), argThat(
+        verify(callbackInterface, atLeastOnce()).onWindowLayoutChanged(any(), argThat(
                 new WindowLayoutInfoValidator(activity)));
     }
 
@@ -155,14 +152,30 @@
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
 
         activity.waitForLayout();
+        if (activity.getResources().getConfiguration().orientation
+                != Configuration.ORIENTATION_PORTRAIT) {
+            // Orientation change did not occur on this device config. Skipping the test.
+            return;
+        }
 
         activity.resetLayoutCounter();
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
 
-        assertTrue("Layout must happen after orientation change", activity.waitForLayout());
+        boolean layoutHappened = activity.waitForLayout();
+        if (activity.getResources().getConfiguration().orientation
+                != Configuration.ORIENTATION_LANDSCAPE) {
+            // Orientation change did not occur on this device config. Skipping the test.
+            return;
+        }
+        assertTrue("Layout must happen after orientation change", layoutHappened);
 
-        verify(callbackInterface, atLeastOnce())
-                .onWindowLayoutChanged(any(), argThat(new DistinctWindowLayoutInfoMatcher()));
+        if (!isSidecar()) {
+            verify(callbackInterface, atLeastOnce())
+                    .onWindowLayoutChanged(any(), argThat(new DistinctWindowLayoutInfoMatcher()));
+        } else {
+            verify(callbackInterface, atLeastOnce())
+                    .onWindowLayoutChanged(any(), any());
+        }
     }
 
     @Test
@@ -181,6 +194,11 @@
         activity = mActivityTestRule.getActivity();
 
         activity.waitForLayout();
+        if (activity.getResources().getConfiguration().orientation
+                != Configuration.ORIENTATION_PORTRAIT) {
+            // Orientation change did not occur on this device config. Skipping the test.
+            return;
+        }
 
         TestActivity.resetResumeCounter();
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
@@ -189,9 +207,19 @@
         activity = mActivityTestRule.getActivity();
 
         activity.waitForLayout();
+        if (activity.getResources().getConfiguration().orientation
+                != Configuration.ORIENTATION_LANDSCAPE) {
+            // Orientation change did not occur on this device config. Skipping the test.
+            return;
+        }
 
-        verify(callbackInterface, atLeastOnce())
-                .onWindowLayoutChanged(any(), argThat(new DistinctWindowLayoutInfoMatcher()));
+        if (!isSidecar()) {
+            verify(callbackInterface, atLeastOnce())
+                    .onWindowLayoutChanged(any(), argThat(new DistinctWindowLayoutInfoMatcher()));
+        } else {
+            verify(callbackInterface, atLeastOnce())
+                    .onWindowLayoutChanged(any(), any());
+        }
     }
 
     @Test
@@ -212,6 +240,10 @@
                 || VERSION_0_1.equals(SidecarCompat.getSidecarVersion()));
     }
 
+    private boolean isSidecar() {
+        return SidecarCompat.getSidecarVersion() != null;
+    }
+
     /**
      * An argument matcher that ensures the arguments used to call are distinct.  The only exception
      * is to allow the first value to trigger twice in case the initial value is pushed and then
@@ -253,26 +285,38 @@
                 return true;
             }
 
-            for (DisplayFeature displayFeature :
-                    windowLayoutInfo.getDisplayFeatures()) {
-                int featureType = displayFeature.getType();
-                if (featureType != TYPE_FOLD && featureType != TYPE_HINGE) {
-                    return false;
-                }
-
-                Rect featureRect = displayFeature.getBounds();
-
-                if (featureRect.isEmpty() || featureRect.left < 0 || featureRect.top < 0) {
-                    return false;
-                }
-                if (featureRect.right < 1 || featureRect.right > mActivity.getWidth()) {
-                    return false;
-                }
-                if (featureRect.bottom < 1 || featureRect.bottom > mActivity.getHeight()) {
+            for (DisplayFeature displayFeature : windowLayoutInfo.getDisplayFeatures()) {
+                if (!isValid(mActivity, displayFeature)) {
                     return false;
                 }
             }
             return true;
         }
     }
+
+    private static boolean isValid(TestActivity activity, DisplayFeature displayFeature) {
+        if (!(displayFeature instanceof FoldingFeature)) {
+            return false;
+        }
+        FoldingFeature feature = (FoldingFeature) displayFeature;
+        int featureType = feature.getType();
+        if (featureType != FoldingFeature.TYPE_FOLD && featureType != FoldingFeature.TYPE_HINGE) {
+            return false;
+        }
+
+        Rect featureRect = feature.getBounds();
+        WindowMetrics windowMetrics = new WindowManager(activity).getCurrentWindowMetrics();
+
+        if ((featureRect.height() == 0 && featureRect.width() == 0) || featureRect.left < 0
+                || featureRect.top < 0) {
+            return false;
+        }
+        if (featureRect.right < 1 || featureRect.right > windowMetrics.getBounds().width()) {
+            return false;
+        }
+        if (featureRect.bottom < 1 || featureRect.bottom > windowMetrics.getBounds().height()) {
+            return false;
+        }
+        return true;
+    }
 }
diff --git a/window/window/src/androidTest/java/androidx/window/ExtensionTranslatingCallbackTest.java b/window/window/src/androidTest/java/androidx/window/ExtensionTranslatingCallbackTest.java
index 09a9305..fee07ed 100644
--- a/window/window/src/androidTest/java/androidx/window/ExtensionTranslatingCallbackTest.java
+++ b/window/window/src/androidTest/java/androidx/window/ExtensionTranslatingCallbackTest.java
@@ -30,6 +30,7 @@
 
 import androidx.window.extensions.ExtensionDeviceState;
 import androidx.window.extensions.ExtensionDisplayFeature;
+import androidx.window.extensions.ExtensionFoldingFeature;
 import androidx.window.extensions.ExtensionWindowLayoutInfo;
 
 import org.junit.After;
@@ -62,8 +63,8 @@
     public void testOnWindowLayoutChange_validFeature() {
         Activity mockActivity = mock(Activity.class);
         Rect bounds = new Rect(WINDOW_BOUNDS.left, 0, WINDOW_BOUNDS.right, 0);
-        ExtensionDisplayFeature foldFeature = new ExtensionDisplayFeature(bounds,
-                ExtensionDisplayFeature.TYPE_FOLD);
+        ExtensionDisplayFeature foldFeature = new ExtensionFoldingFeature(bounds,
+                ExtensionFoldingFeature.TYPE_FOLD, ExtensionFoldingFeature.STATE_FLAT);
 
         List<ExtensionDisplayFeature> extensionDisplayFeatures = new ArrayList<>();
         extensionDisplayFeatures.add(foldFeature);
@@ -71,7 +72,8 @@
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
 
         List<DisplayFeature> expectedFeatures = new ArrayList<>();
-        expectedFeatures.add(new DisplayFeature(foldFeature.getBounds(), DisplayFeature.TYPE_FOLD));
+        expectedFeatures.add(new FoldingFeature(foldFeature.getBounds(), FoldingFeature.TYPE_FOLD,
+                FoldingFeature.STATE_FLAT));
         WindowLayoutInfo expected = new WindowLayoutInfo(expectedFeatures);
 
         ExtensionCallbackInterface mockCallback = mock(ExtensionCallbackInterface.class);
@@ -86,59 +88,16 @@
     }
 
     @Test
-    public void testOnWindowLayoutChange_filterRemovesEmptyBoundsFeature() {
-        List<ExtensionDisplayFeature> extensionDisplayFeatures = new ArrayList<>();
-        extensionDisplayFeatures.add(
-                new ExtensionDisplayFeature(new Rect(), ExtensionDisplayFeature.TYPE_FOLD));
-
-        ExtensionCallbackInterface mockCallback = mock(ExtensionCallbackInterface.class);
-        ExtensionTranslatingCallback extensionTranslatingCallback =
-                new ExtensionTranslatingCallback(mockCallback, new ExtensionAdapter());
-        ExtensionWindowLayoutInfo windowLayoutInfo =
-                new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
-        Activity mockActivity = mock(Activity.class);
-
-        extensionTranslatingCallback.onWindowLayoutChanged(mockActivity, windowLayoutInfo);
-
-        verify(mockCallback).onWindowLayoutChanged(eq(mockActivity),
-                argThat((layoutInfo) -> layoutInfo.getDisplayFeatures().isEmpty()));
-    }
-
-
-    @Test
-    public void testOnWindowLayoutChange_filterRemovesNonEmptyAreaFoldFeature() {
-        List<ExtensionDisplayFeature> extensionDisplayFeatures = new ArrayList<>();
-        Rect fullWidthBounds = new Rect(0, 1, WINDOW_BOUNDS.width(), 2);
-        Rect fullHeightBounds = new Rect(1, 0, 2, WINDOW_BOUNDS.height());
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullWidthBounds,
-                ExtensionDisplayFeature.TYPE_FOLD));
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullHeightBounds,
-                ExtensionDisplayFeature.TYPE_FOLD));
-
-        ExtensionCallbackInterface mockCallback = mock(ExtensionCallbackInterface.class);
-        ExtensionTranslatingCallback extensionTranslatingCallback =
-                new ExtensionTranslatingCallback(mockCallback, new ExtensionAdapter());
-        ExtensionWindowLayoutInfo windowLayoutInfo =
-                new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
-        Activity mockActivity = mock(Activity.class);
-
-        extensionTranslatingCallback.onWindowLayoutChanged(mockActivity, windowLayoutInfo);
-
-        verify(mockCallback).onWindowLayoutChanged(eq(mockActivity),
-                argThat((layoutInfo) -> layoutInfo.getDisplayFeatures().isEmpty()));
-    }
-
-    @Test
     public void testOnWindowLayoutChange_filterRemovesHingeFeatureNotSpanningFullDimension() {
         List<ExtensionDisplayFeature> extensionDisplayFeatures = new ArrayList<>();
         Rect fullWidthBounds = new Rect(WINDOW_BOUNDS.left, WINDOW_BOUNDS.top,
                 WINDOW_BOUNDS.right / 2, 2);
         Rect fullHeightBounds = new Rect(WINDOW_BOUNDS.left, WINDOW_BOUNDS.top, 2,
                 WINDOW_BOUNDS.bottom / 2);
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullWidthBounds,
-                ExtensionDisplayFeature.TYPE_HINGE));
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullHeightBounds,
-                ExtensionDisplayFeature.TYPE_HINGE));
+        extensionDisplayFeatures.add(new ExtensionFoldingFeature(fullWidthBounds,
+                ExtensionFoldingFeature.TYPE_HINGE, ExtensionFoldingFeature.STATE_FLAT));
+        extensionDisplayFeatures.add(new ExtensionFoldingFeature(fullHeightBounds,
+                ExtensionFoldingFeature.TYPE_HINGE, ExtensionFoldingFeature.STATE_FLAT));
 
         ExtensionCallbackInterface mockCallback = mock(ExtensionCallbackInterface.class);
         ExtensionTranslatingCallback extensionTranslatingCallback =
@@ -161,10 +120,10 @@
                 WINDOW_BOUNDS.right / 2, WINDOW_BOUNDS.top);
         Rect fullHeightBounds = new Rect(WINDOW_BOUNDS.left, WINDOW_BOUNDS.top, WINDOW_BOUNDS.left,
                 WINDOW_BOUNDS.bottom / 2);
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullWidthBounds,
-                ExtensionDisplayFeature.TYPE_HINGE));
-        extensionDisplayFeatures.add(new ExtensionDisplayFeature(fullHeightBounds,
-                ExtensionDisplayFeature.TYPE_HINGE));
+        extensionDisplayFeatures.add(new ExtensionFoldingFeature(fullWidthBounds,
+                ExtensionFoldingFeature.TYPE_HINGE, ExtensionFoldingFeature.STATE_FLAT));
+        extensionDisplayFeatures.add(new ExtensionFoldingFeature(fullHeightBounds,
+                ExtensionFoldingFeature.TYPE_HINGE, ExtensionFoldingFeature.STATE_FLAT));
 
         ExtensionCallbackInterface mockCallback = mock(ExtensionCallbackInterface.class);
         ExtensionTranslatingCallback extensionTranslatingCallback =
@@ -187,10 +146,6 @@
                 new ExtensionTranslatingCallback(mockCallback, new ExtensionAdapter());
 
         extensionTranslatingCallback.onDeviceStateChanged(new ExtensionDeviceState(
-                ExtensionDeviceState.POSTURE_UNKNOWN));
-        extensionTranslatingCallback.onDeviceStateChanged(new ExtensionDeviceState(
-                ExtensionDeviceState.POSTURE_CLOSED));
-        extensionTranslatingCallback.onDeviceStateChanged(new ExtensionDeviceState(
                 ExtensionDeviceState.POSTURE_HALF_OPENED));
         extensionTranslatingCallback.onDeviceStateChanged(new ExtensionDeviceState(
                 ExtensionDeviceState.POSTURE_OPENED));
@@ -201,11 +156,9 @@
         verify(mockCallback, atLeastOnce()).onDeviceStateChanged(captor.capture());
 
         List<DeviceState> values = captor.getAllValues();
-        assertEquals(DeviceState.POSTURE_UNKNOWN, values.get(0).getPosture());
-        assertEquals(DeviceState.POSTURE_CLOSED, values.get(1).getPosture());
-        assertEquals(DeviceState.POSTURE_HALF_OPENED, values.get(2).getPosture());
-        assertEquals(DeviceState.POSTURE_OPENED, values.get(3).getPosture());
-        assertEquals(DeviceState.POSTURE_FLIPPED, values.get(4).getPosture());
-        assertEquals(5, values.size());
+        assertEquals(DeviceState.POSTURE_HALF_OPENED, values.get(0).getPosture());
+        assertEquals(DeviceState.POSTURE_OPENED, values.get(1).getPosture());
+        assertEquals(DeviceState.POSTURE_FLIPPED, values.get(2).getPosture());
+        assertEquals(3, values.size());
     }
 }
diff --git a/window/window/src/androidTest/java/androidx/window/ExtensionWindowBackendTest.java b/window/window/src/androidTest/java/androidx/window/ExtensionWindowBackendTest.java
index 2361ed1..52a190b 100644
--- a/window/window/src/androidTest/java/androidx/window/ExtensionWindowBackendTest.java
+++ b/window/window/src/androidTest/java/androidx/window/ExtensionWindowBackendTest.java
@@ -52,6 +52,7 @@
 import java.util.List;
 
 /** Tests for {@link ExtensionWindowBackend} class. */
+@SuppressWarnings({"deprecation", "unchecked"}) // TODO(b/173739071) remove DeviceState
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public final class ExtensionWindowBackendTest extends WindowTestBase {
@@ -132,7 +133,7 @@
         backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
 
         // Check registering the layout change callback
-        Consumer<WindowLayoutInfo> consumer = mock(Consumer.class);
+        Consumer<WindowLayoutInfo> consumer = mock(WindowLayoutInfoConsumer.class);
         TestActivity activity = mActivityTestRule.launchActivity(new Intent());
         backend.registerLayoutChangeCallback(activity, Runnable::run, consumer);
 
@@ -152,10 +153,11 @@
         backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
 
         // Check registering the layout change callback
-        Consumer<WindowLayoutInfo> consumer = mock(Consumer.class);
+        Consumer<WindowLayoutInfo> consumer = mock(WindowLayoutInfoConsumer.class);
         TestActivity activity = mActivityTestRule.launchActivity(new Intent());
         backend.registerLayoutChangeCallback(activity, Runnable::run, consumer);
-        backend.registerLayoutChangeCallback(activity, Runnable::run, mock(Consumer.class));
+        backend.registerLayoutChangeCallback(activity, Runnable::run,
+                mock(WindowLayoutInfoConsumer.class));
 
         assertEquals(2, backend.mWindowLayoutChangeCallbacks.size());
         verify(backend.mWindowExtension).onWindowLayoutChangeListenerAdded(activity);
@@ -174,8 +176,8 @@
         backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
 
         // Check registering the layout change callback
-        Consumer<WindowLayoutInfo> firstConsumer = mock(Consumer.class);
-        Consumer<WindowLayoutInfo> secondConsumer = mock(Consumer.class);
+        Consumer<WindowLayoutInfo> firstConsumer = mock(WindowLayoutInfoConsumer.class);
+        Consumer<WindowLayoutInfo> secondConsumer = mock(WindowLayoutInfoConsumer.class);
         TestActivity activity = mActivityTestRule.launchActivity(new Intent());
         backend.registerLayoutChangeCallback(activity, Runnable::run, firstConsumer);
         backend.registerLayoutChangeCallback(activity, Runnable::run, secondConsumer);
@@ -192,9 +194,10 @@
     public void testRegisterLayoutChangeCallback_relayLastEmittedValue() {
         WindowLayoutInfo expectedWindowLayoutInfo = newTestWindowLayoutInfo();
         ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
-        Consumer<WindowLayoutInfo> consumer = mock(Consumer.class);
+        Consumer<WindowLayoutInfo> consumer = mock(WindowLayoutInfoConsumer.class);
         TestActivity activity = mActivityTestRule.launchActivity(new Intent());
-        backend.registerLayoutChangeCallback(activity, Runnable::run, mock(Consumer.class));
+        backend.registerLayoutChangeCallback(activity, Runnable::run,
+                mock(WindowLayoutInfoConsumer.class));
         backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
         backend.mLastReportedWindowLayouts.put(activity, expectedWindowLayoutInfo);
 
@@ -209,7 +212,7 @@
         backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
 
         // Check that callbacks from the extension are propagated correctly
-        Consumer<WindowLayoutInfo> consumer = mock(Consumer.class);
+        Consumer<WindowLayoutInfo> consumer = mock(WindowLayoutInfoConsumer.class);
         TestActivity activity = mActivityTestRule.launchActivity(new Intent());
 
         backend.registerLayoutChangeCallback(activity, Runnable::run, consumer);
@@ -230,13 +233,13 @@
 
     @Test
     public void testRegisterDeviceChangeCallback() {
-        DeviceState expectedState = newTestDeviceState();
-        ExtensionInterfaceCompat mockInterface = mock(ExtensionInterfaceCompat.class);
+        ExtensionInterfaceCompat mockInterface = mock(
+                ExtensionInterfaceCompat.class);
         ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
         backend.mWindowExtension = mockInterface;
 
         // Check registering the device state change callback
-        Consumer<DeviceState> consumer = mock(Consumer.class);
+        Consumer<DeviceState> consumer = mock(DeviceStateConsumer.class);
         backend.registerDeviceStateChangeCallback(Runnable::run, consumer);
 
         assertEquals(1, backend.mDeviceStateChangeCallbacks.size());
@@ -255,7 +258,7 @@
         backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
 
         // Check that callbacks from the extension are propagated correctly
-        Consumer<DeviceState> consumer = mock(Consumer.class);
+        Consumer<DeviceState> consumer = mock(DeviceStateConsumer.class);
 
         backend.registerDeviceStateChangeCallback(Runnable::run, consumer);
         DeviceState deviceState = newTestDeviceState();
@@ -278,10 +281,10 @@
         backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
 
         // Check registering the layout change callback
-        Consumer<DeviceState> consumer = mock(Consumer.class);
-        TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+        Consumer<DeviceState> consumer = mock(DeviceStateConsumer.class);
+        mActivityTestRule.launchActivity(new Intent());
         backend.registerDeviceStateChangeCallback(Runnable::run, consumer);
-        backend.registerDeviceStateChangeCallback(Runnable::run, mock(Consumer.class));
+        backend.registerDeviceStateChangeCallback(Runnable::run, mock(DeviceStateConsumer.class));
 
         assertEquals(2, backend.mDeviceStateChangeCallbacks.size());
         verify(backend.mWindowExtension).onDeviceStateListenersChanged(false);
@@ -300,9 +303,9 @@
         backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
 
         // Check registering the layout change callback
-        Consumer<DeviceState> firstConsumer = mock(Consumer.class);
-        Consumer<DeviceState> secondConsumer = mock(Consumer.class);
-        TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+        Consumer<DeviceState> firstConsumer = mock(DeviceStateConsumer.class);
+        Consumer<DeviceState> secondConsumer = mock(DeviceStateConsumer.class);
+        mActivityTestRule.launchActivity(new Intent());
         backend.registerDeviceStateChangeCallback(Runnable::run, firstConsumer);
         backend.registerDeviceStateChangeCallback(Runnable::run, secondConsumer);
 
@@ -318,7 +321,7 @@
     public void testDeviceChangeCallback_relayLastEmittedValue() {
         DeviceState expectedState = newTestDeviceState();
         ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
-        Consumer<DeviceState> consumer = mock(Consumer.class);
+        Consumer<DeviceState> consumer = mock(DeviceStateConsumer.class);
         backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
         backend.mLastReportedDeviceState = expectedState;
 
@@ -330,7 +333,7 @@
     @Test
     public void testDeviceChangeCallback_clearLastEmittedValue() {
         ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
-        Consumer<DeviceState> consumer = mock(Consumer.class);
+        Consumer<DeviceState> consumer = mock(DeviceStateConsumer.class);
 
         backend.registerDeviceStateChangeCallback(Runnable::run, consumer);
         backend.unregisterDeviceStateChangeCallback(consumer);
@@ -345,14 +348,10 @@
 
         assertTrue(windowLayoutInfo.getDisplayFeatures().isEmpty());
 
-        DisplayFeature.Builder featureBuilder = new DisplayFeature.Builder();
-        featureBuilder.setType(DisplayFeature.TYPE_HINGE);
-        featureBuilder.setBounds(new Rect(0, 2, 3, 4));
-        DisplayFeature feature1 = featureBuilder.build();
-
-        featureBuilder = new DisplayFeature.Builder();
-        featureBuilder.setBounds(new Rect(0, 1, 5, 1));
-        DisplayFeature feature2 = featureBuilder.build();
+        DisplayFeature feature1 = new FoldingFeature(new Rect(0, 2, 3, 4),
+                FoldingFeature.TYPE_HINGE, FoldingFeature.STATE_FLAT);
+        DisplayFeature feature2 = new FoldingFeature(new Rect(0, 1, 5, 1),
+                FoldingFeature.TYPE_HINGE, FoldingFeature.STATE_FLAT);
 
         List<DisplayFeature> displayFeatures = new ArrayList<>();
         displayFeatures.add(feature1);
@@ -369,8 +368,12 @@
         return builder.build();
     }
 
+    private interface DeviceStateConsumer extends Consumer<DeviceState> { }
+
+    private interface WindowLayoutInfoConsumer extends Consumer<WindowLayoutInfo> { }
+
     private static class SimpleConsumer<T> implements Consumer<T> {
-        private List<T> mValues;
+        private final List<T> mValues;
 
         SimpleConsumer() {
             mValues = new ArrayList<>();
diff --git a/window/window/src/androidTest/java/androidx/window/FoldingFeatureTest.java b/window/window/src/androidTest/java/androidx/window/FoldingFeatureTest.java
new file mode 100644
index 0000000..9339492
--- /dev/null
+++ b/window/window/src/androidTest/java/androidx/window/FoldingFeatureTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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 androidx.window.FoldingFeature.STATE_FLAT;
+import static androidx.window.FoldingFeature.STATE_FLIPPED;
+import static androidx.window.FoldingFeature.STATE_HALF_OPENED;
+import static androidx.window.FoldingFeature.TYPE_FOLD;
+import static androidx.window.FoldingFeature.TYPE_HINGE;
+
+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 FoldingFeature} class. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class FoldingFeatureTest {
+
+    @Test(expected = IllegalArgumentException.class)
+    public void tesEmptyRect() {
+        new FoldingFeature(new Rect(), TYPE_HINGE, STATE_HALF_OPENED);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFoldWithNonZeroArea() {
+        new FoldingFeature(new Rect(0, 0, 20, 30), TYPE_FOLD, STATE_FLIPPED);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testHorizontalHingeWithNonZeroOrigin() {
+        new FoldingFeature(new Rect(1, 10, 20, 10), TYPE_HINGE, STATE_FLIPPED);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testVerticalHingeWithNonZeroOrigin() {
+        new FoldingFeature(new Rect(10, 1, 19, 29), TYPE_HINGE, STATE_FLIPPED);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testHorizontalFoldWithNonZeroOrigin() {
+        new FoldingFeature(new Rect(1, 10, 20, 10), TYPE_FOLD, STATE_FLIPPED);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testVerticalFoldWithNonZeroOrigin() {
+        new FoldingFeature(new Rect(10, 1, 10, 20), TYPE_FOLD, STATE_FLIPPED);
+    }
+
+    @Test
+    public void testSetBoundsAndType() {
+        Rect bounds = new Rect(0, 10, 30, 10);
+        int type = TYPE_HINGE;
+        int state = STATE_HALF_OPENED;
+        FoldingFeature feature = new FoldingFeature(bounds, type, state);
+
+        assertEquals(bounds, feature.getBounds());
+        assertEquals(type, feature.getType());
+        assertEquals(state, feature.getState());
+    }
+
+    @Test
+    public void testEquals_sameAttributes() {
+        Rect bounds = new Rect(1, 0, 1, 10);
+        int type = TYPE_FOLD;
+        int state = STATE_FLAT;
+
+        FoldingFeature original = new FoldingFeature(bounds, type, state);
+        FoldingFeature copy = new FoldingFeature(bounds, type, state);
+
+        assertEquals(original, copy);
+    }
+
+    @Test
+    public void testEquals_differentRect() {
+        Rect originalRect = new Rect(1, 0, 1, 10);
+        Rect otherRect = new Rect(2, 0, 2, 10);
+        int type = TYPE_FOLD;
+        int state = STATE_FLAT;
+
+        FoldingFeature original = new FoldingFeature(originalRect, type, state);
+        FoldingFeature other = new FoldingFeature(otherRect, type, state);
+
+        assertNotEquals(original, other);
+    }
+
+    @Test
+    public void testEquals_differentType() {
+        Rect rect = new Rect(1, 0, 1, 10);
+        int originalType = TYPE_FOLD;
+        int otherType = TYPE_HINGE;
+        int state = STATE_FLAT;
+
+        FoldingFeature original = new FoldingFeature(rect, originalType, state);
+        FoldingFeature other = new FoldingFeature(rect, otherType, state);
+
+        assertNotEquals(original, other);
+    }
+
+    @Test
+    public void testEquals_differentState() {
+        Rect rect = new Rect(1, 0, 1, 10);
+        int type = TYPE_FOLD;
+        int originalState = STATE_FLAT;
+        int otherState = STATE_FLIPPED;
+
+        FoldingFeature original = new FoldingFeature(rect, type, originalState);
+        FoldingFeature other = new FoldingFeature(rect, type, otherState);
+
+        assertNotEquals(original, other);
+    }
+
+    @Test
+    public void testHashCode_matchesIfEqual() {
+        Rect originalRect = new Rect(1, 0, 1, 10);
+        Rect matchingRect = new Rect(1, 0, 1, 10);
+        int type = TYPE_FOLD;
+        int state = STATE_FLAT;
+
+        FoldingFeature original = new FoldingFeature(originalRect, type, state);
+        FoldingFeature matching = new FoldingFeature(matchingRect, type, state);
+
+        assertEquals(original, matching);
+        assertEquals(original.hashCode(), matching.hashCode());
+    }
+}
diff --git a/window/window/src/androidTest/java/androidx/window/SidecarAdapterTest.java b/window/window/src/androidTest/java/androidx/window/SidecarAdapterTest.java
index 65e2903..8272c3c 100644
--- a/window/window/src/androidTest/java/androidx/window/SidecarAdapterTest.java
+++ b/window/window/src/androidTest/java/androidx/window/SidecarAdapterTest.java
@@ -16,6 +16,9 @@
 
 package androidx.window;
 
+import static androidx.window.SidecarAdapter.setSidecarDevicePosture;
+import static androidx.window.SidecarAdapter.setSidecarDisplayFeatures;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
@@ -62,14 +65,13 @@
     private static SidecarWindowLayoutInfo sidecarWindowLayoutInfo(
             List<SidecarDisplayFeature> features) {
         SidecarWindowLayoutInfo layoutInfo = new SidecarWindowLayoutInfo();
-        layoutInfo.displayFeatures = new ArrayList<>();
-        layoutInfo.displayFeatures.addAll(features);
+        setSidecarDisplayFeatures(layoutInfo, features);
         return layoutInfo;
     }
 
     private static SidecarDeviceState sidecarDeviceState(int posture) {
         SidecarDeviceState deviceState = new SidecarDeviceState();
-        deviceState.posture = posture;
+        setSidecarDevicePosture(deviceState, posture);
         return deviceState;
     }
 
@@ -85,19 +87,21 @@
         sidecarDisplayFeatures.add(foldFeature);
         SidecarWindowLayoutInfo windowLayoutInfo = sidecarWindowLayoutInfo(sidecarDisplayFeatures);
 
+        SidecarDeviceState state = sidecarDeviceState(SidecarDeviceState.POSTURE_OPENED);
+
         List<DisplayFeature> expectedFeatures = new ArrayList<>();
-        expectedFeatures.add(new DisplayFeature(foldFeature.getRect(), DisplayFeature.TYPE_FOLD));
+        expectedFeatures.add(new FoldingFeature(foldFeature.getRect(), FoldingFeature.TYPE_FOLD,
+                FoldingFeature.STATE_FLAT));
         WindowLayoutInfo expected = new WindowLayoutInfo(expectedFeatures);
 
         SidecarAdapter sidecarAdapter = new SidecarAdapter();
 
-        WindowLayoutInfo actual = sidecarAdapter.translate(mockActivity, windowLayoutInfo);
+        WindowLayoutInfo actual = sidecarAdapter.translate(mockActivity, windowLayoutInfo, state);
 
         assertEquals(expected, actual);
     }
 
     @Test
-    @Override
     public void testTranslateWindowLayoutInfo_filterRemovesEmptyBoundsFeature() {
         List<SidecarDisplayFeature> sidecarDisplayFeatures = new ArrayList<>();
         sidecarDisplayFeatures.add(
@@ -106,15 +110,15 @@
         SidecarAdapter sidecarAdapter = new SidecarAdapter();
         SidecarWindowLayoutInfo windowLayoutInfo = sidecarWindowLayoutInfo(sidecarDisplayFeatures);
         Activity mockActivity = mock(Activity.class);
+        SidecarDeviceState state = sidecarDeviceState(SidecarDeviceState.POSTURE_OPENED);
 
-        WindowLayoutInfo actual = sidecarAdapter.translate(mockActivity, windowLayoutInfo);
+        WindowLayoutInfo actual = sidecarAdapter.translate(mockActivity, windowLayoutInfo, state);
 
         assertTrue(actual.getDisplayFeatures().isEmpty());
     }
 
 
     @Test
-    @Override
     public void testTranslateWindowLayoutInfo_filterRemovesNonEmptyAreaFoldFeature() {
         List<SidecarDisplayFeature> sidecarDisplayFeatures = new ArrayList<>();
         Rect fullWidthBounds = new Rect(0, 1, WINDOW_BOUNDS.width(), 2);
@@ -130,7 +134,10 @@
         SidecarWindowLayoutInfo windowLayoutInfo = sidecarWindowLayoutInfo(sidecarDisplayFeatures);
         Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = sidecarCallbackAdapter.translate(mockActivity, windowLayoutInfo);
+        SidecarDeviceState state = sidecarDeviceState(SidecarDeviceState.POSTURE_OPENED);
+
+        WindowLayoutInfo actual = sidecarCallbackAdapter.translate(mockActivity, windowLayoutInfo,
+                state);
 
         assertTrue(actual.getDisplayFeatures().isEmpty());
     }
@@ -151,10 +158,11 @@
 
         SidecarAdapter sidecarAdapter = new SidecarAdapter();
         SidecarWindowLayoutInfo windowLayoutInfo = sidecarWindowLayoutInfo(sidecarDisplayFeatures);
+        SidecarDeviceState state = sidecarDeviceState(SidecarDeviceState.POSTURE_OPENED);
 
         Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = sidecarAdapter.translate(mockActivity, windowLayoutInfo);
+        WindowLayoutInfo actual = sidecarAdapter.translate(mockActivity, windowLayoutInfo, state);
 
         assertTrue(actual.getDisplayFeatures().isEmpty());
     }
@@ -175,10 +183,12 @@
         SidecarAdapter sidecarCallbackAdapter = new SidecarAdapter();
         SidecarWindowLayoutInfo windowLayoutInfo = sidecarWindowLayoutInfo(
                 extensionDisplayFeatures);
+        SidecarDeviceState state = sidecarDeviceState(SidecarDeviceState.POSTURE_OPENED);
 
         Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = sidecarCallbackAdapter.translate(mockActivity, windowLayoutInfo);
+        WindowLayoutInfo actual = sidecarCallbackAdapter.translate(mockActivity, windowLayoutInfo,
+                state);
 
         assertTrue(actual.getDisplayFeatures().isEmpty());
     }
@@ -186,8 +196,6 @@
     @Test
     @Override
     public void testTranslateDeviceState() {
-        ExtensionInterfaceCompat.ExtensionCallbackInterface mockCallback = mock(
-                ExtensionInterfaceCompat.ExtensionCallbackInterface.class);
         SidecarAdapter sidecarCallbackAdapter = new SidecarAdapter();
         List<DeviceState> values = new ArrayList<>();
 
diff --git a/window/window/src/androidTest/java/androidx/window/SidecarCompatDeviceTest.java b/window/window/src/androidTest/java/androidx/window/SidecarCompatDeviceTest.java
index 954ed4a..cfc6286 100644
--- a/window/window/src/androidTest/java/androidx/window/SidecarCompatDeviceTest.java
+++ b/window/window/src/androidTest/java/androidx/window/SidecarCompatDeviceTest.java
@@ -17,12 +17,15 @@
 package androidx.window;
 
 import static androidx.window.ExtensionInterfaceCompat.ExtensionCallbackInterface;
+import static androidx.window.SidecarAdapter.getSidecarDevicePosture;
+import static androidx.window.SidecarAdapter.getSidecarDisplayFeatures;
 import static androidx.window.Version.VERSION_0_1;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
@@ -42,6 +45,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentMatcher;
 
+import java.util.List;
+
 /**
  * Tests for {@link SidecarCompat} implementation of {@link ExtensionInterfaceCompat} that are
  * executed with Sidecar implementation provided on the device (and only if one is available).
@@ -66,8 +71,8 @@
         mSidecarCompat.onDeviceStateListenersChanged(false);
 
 
-        verify(callbackInterface).onDeviceStateChanged(argThat(
-                deviceState -> deviceState.getPosture() == sidecarDeviceState.posture));
+        verify(callbackInterface, atLeastOnce()).onDeviceStateChanged(argThat(deviceState ->
+                deviceState.getPosture() == getSidecarDevicePosture(sidecarDeviceState)));
     }
 
     @Test
@@ -83,7 +88,7 @@
         SidecarWindowLayoutInfo sidecarWindowLayoutInfo =
                 mSidecarCompat.mSidecar.getWindowLayoutInfo(windowToken);
 
-        verify(callbackInterface).onWindowLayoutChanged(any(),
+        verify(callbackInterface, atLeastOnce()).onWindowLayoutChanged(any(),
                 argThat(new SidecarMatcher(sidecarWindowLayoutInfo)));
     }
 
@@ -102,14 +107,16 @@
 
         @Override
         public boolean matches(WindowLayoutInfo windowLayoutInfo) {
-            if (windowLayoutInfo.getDisplayFeatures().size()
-                    != mSidecarWindowLayoutInfo.displayFeatures.size()) {
+            List<SidecarDisplayFeature> sidecarDisplayFeatures =
+                    getSidecarDisplayFeatures(mSidecarWindowLayoutInfo);
+            if (windowLayoutInfo.getDisplayFeatures().size() != sidecarDisplayFeatures.size()) {
                 return false;
             }
             for (int i = 0; i < windowLayoutInfo.getDisplayFeatures().size(); i++) {
-                DisplayFeature feature = windowLayoutInfo.getDisplayFeatures().get(i);
-                SidecarDisplayFeature sidecarDisplayFeature =
-                        mSidecarWindowLayoutInfo.displayFeatures.get(i);
+                // Sidecar only has folding features
+                FoldingFeature feature = (FoldingFeature) windowLayoutInfo.getDisplayFeatures()
+                        .get(i);
+                SidecarDisplayFeature sidecarDisplayFeature = sidecarDisplayFeatures.get(i);
 
                 if (feature.getType() != sidecarDisplayFeature.getType()) {
                     return false;
diff --git a/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java b/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java
index 05f6c7d..5acba49 100644
--- a/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java
+++ b/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java
@@ -16,16 +16,23 @@
 
 package androidx.window;
 
-import static androidx.window.TestBoundUtil.invalidFoldBounds;
-import static androidx.window.TestBoundUtil.invalidHingeBounds;
-import static androidx.window.TestBoundUtil.validFoldBound;
+import static androidx.window.SidecarAdapter.getSidecarDisplayFeatures;
+import static androidx.window.SidecarAdapter.setSidecarDevicePosture;
+import static androidx.window.SidecarAdapter.setSidecarDisplayFeatures;
+import static androidx.window.TestBoundsUtil.invalidFoldBounds;
+import static androidx.window.TestBoundsUtil.invalidHingeBounds;
+import static androidx.window.TestBoundsUtil.validFoldBound;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -54,6 +61,7 @@
 import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -89,14 +97,15 @@
 
         // Setup mocked sidecar responses
         SidecarDeviceState defaultDeviceState = new SidecarDeviceState();
-        defaultDeviceState.posture = SidecarDeviceState.POSTURE_HALF_OPENED;
+        setSidecarDevicePosture(defaultDeviceState, SidecarDeviceState.POSTURE_HALF_OPENED);
         when(mSidecarCompat.mSidecar.getDeviceState()).thenReturn(defaultDeviceState);
 
         SidecarDisplayFeature sidecarDisplayFeature = newDisplayFeature(
                 new Rect(0, 1, WINDOW_BOUNDS.width(), 1), SidecarDisplayFeature.TYPE_HINGE);
         SidecarWindowLayoutInfo sidecarWindowLayoutInfo = new SidecarWindowLayoutInfo();
-        sidecarWindowLayoutInfo.displayFeatures = new ArrayList<>();
-        sidecarWindowLayoutInfo.displayFeatures.add(sidecarDisplayFeature);
+        List<SidecarDisplayFeature> displayFeatures = new ArrayList<>();
+        displayFeatures.add(sidecarDisplayFeature);
+        setSidecarDisplayFeatures(sidecarWindowLayoutInfo, displayFeatures);
         when(mSidecarCompat.mSidecar.getWindowLayoutInfo(any()))
                 .thenReturn(sidecarWindowLayoutInfo);
     }
@@ -109,23 +118,27 @@
     @Test
     @Override
     public void testGetDeviceState() {
-        FakeExtensionImp fakeSidecarImp = new FakeExtensionImp();
+        FakeExtensionImp fakeSidecarImp = new FakeExtensionImp(
+                newDeviceState(SidecarDeviceState.POSTURE_OPENED),
+                newWindowLayoutInfo(Collections.emptyList()));
         SidecarCompat compat = new SidecarCompat(fakeSidecarImp, new SidecarAdapter());
         ExtensionInterfaceCompat.ExtensionCallbackInterface mockCallback = mock(
                 ExtensionInterfaceCompat.ExtensionCallbackInterface.class);
         compat.setExtensionCallback(mockCallback);
         compat.onDeviceStateListenersChanged(false);
-        SidecarDeviceState deviceState = newDeviceState(SidecarDeviceState.POSTURE_OPENED);
+        SidecarDeviceState deviceState = newDeviceState(SidecarDeviceState.POSTURE_HALF_OPENED);
 
         fakeSidecarImp.triggerDeviceState(deviceState);
 
-        verify(mockCallback).onDeviceStateChanged(any());
+        verify(mockCallback, atLeastOnce()).onDeviceStateChanged(any());
     }
 
     @Test
     @Override
     public void testGetWindowLayout() {
-        FakeExtensionImp fakeSidecarImp = new FakeExtensionImp();
+        FakeExtensionImp fakeSidecarImp = new FakeExtensionImp(
+                newDeviceState(SidecarDeviceState.POSTURE_OPENED),
+                newWindowLayoutInfo(Collections.emptyList()));
         SidecarCompat compat = new SidecarCompat(fakeSidecarImp, new SidecarAdapter());
         ExtensionInterfaceCompat.ExtensionCallbackInterface mockCallback = mock(
                 ExtensionInterfaceCompat.ExtensionCallbackInterface.class);
@@ -134,7 +147,8 @@
 
         fakeSidecarImp.triggerGoodSignal();
 
-        verify(mockCallback).onWindowLayoutChanged(any(), any());
+        verify(mockCallback, atLeastOnce()).onWindowLayoutChanged(eq(mActivity),
+                any(WindowLayoutInfo.class));
     }
 
     @Test
@@ -143,7 +157,7 @@
         SidecarWindowLayoutInfo originalWindowLayoutInfo =
                 mSidecarCompat.mSidecar.getWindowLayoutInfo(getActivityWindowToken(mActivity));
         List<SidecarDisplayFeature> sidecarDisplayFeatures =
-                originalWindowLayoutInfo.displayFeatures;
+                getSidecarDisplayFeatures(originalWindowLayoutInfo);
         SidecarDisplayFeature newFeature = new SidecarDisplayFeature();
         newFeature.setRect(new Rect());
         sidecarDisplayFeatures.add(newFeature);
@@ -160,7 +174,7 @@
         SidecarWindowLayoutInfo originalWindowLayoutInfo =
                 mSidecarCompat.mSidecar.getWindowLayoutInfo(mock(IBinder.class));
         List<SidecarDisplayFeature> sidecarDisplayFeatures =
-                originalWindowLayoutInfo.displayFeatures;
+                getSidecarDisplayFeatures(originalWindowLayoutInfo);
         // Horizontal fold.
         sidecarDisplayFeatures.add(
                 newDisplayFeature(new Rect(0, 1, WINDOW_BOUNDS.width(), 2),
@@ -183,7 +197,7 @@
         SidecarWindowLayoutInfo originalWindowLayoutInfo =
                 mSidecarCompat.mSidecar.getWindowLayoutInfo(mock(IBinder.class));
         List<SidecarDisplayFeature> sidecarDisplayFeatures =
-                originalWindowLayoutInfo.displayFeatures;
+                getSidecarDisplayFeatures(originalWindowLayoutInfo);
         // Horizontal hinge.
         sidecarDisplayFeatures.add(
                 newDisplayFeature(new Rect(0, 1, WINDOW_BOUNDS.width() - 1, 2),
@@ -206,7 +220,7 @@
         SidecarWindowLayoutInfo originalWindowLayoutInfo =
                 mSidecarCompat.mSidecar.getWindowLayoutInfo(mock(IBinder.class));
         List<SidecarDisplayFeature> sidecarDisplayFeatures =
-                originalWindowLayoutInfo.displayFeatures;
+                getSidecarDisplayFeatures(originalWindowLayoutInfo);
         // Horizontal fold.
         sidecarDisplayFeatures.add(
                 newDisplayFeature(new Rect(0, 1, WINDOW_BOUNDS.width() - 1, 2),
@@ -226,7 +240,9 @@
 
     @Override
     public void testExtensionCallback_filterRemovesInvalidValues() {
-        FakeExtensionImp fakeSidecarImp = new FakeExtensionImp();
+        FakeExtensionImp fakeSidecarImp = new FakeExtensionImp(
+                newDeviceState(SidecarDeviceState.POSTURE_OPENED),
+                newWindowLayoutInfo(Collections.emptyList()));
         SidecarCompat compat = new SidecarCompat(fakeSidecarImp, new SidecarAdapter());
         ExtensionInterfaceCompat.ExtensionCallbackInterface mockCallback = mock(
                 ExtensionInterfaceCompat.ExtensionCallbackInterface.class);
@@ -254,7 +270,7 @@
 
         // Verify that the callback set for sidecar propagates the device state callback
         SidecarDeviceState sidecarDeviceState = new SidecarDeviceState();
-        sidecarDeviceState.posture = SidecarDeviceState.POSTURE_HALF_OPENED;
+        setSidecarDevicePosture(sidecarDeviceState, SidecarDeviceState.POSTURE_HALF_OPENED);
 
         sidecarCallbackCaptor.getValue().onDeviceStateChanged(sidecarDeviceState);
         ArgumentCaptor<DeviceState> deviceStateCaptor = ArgumentCaptor.forClass(DeviceState.class);
@@ -268,8 +284,9 @@
         SidecarDisplayFeature sidecarDisplayFeature = newDisplayFeature(bounds,
                 SidecarDisplayFeature.TYPE_HINGE);
         SidecarWindowLayoutInfo sidecarWindowLayoutInfo = new SidecarWindowLayoutInfo();
-        sidecarWindowLayoutInfo.displayFeatures = new ArrayList<>();
-        sidecarWindowLayoutInfo.displayFeatures.add(sidecarDisplayFeature);
+        List<SidecarDisplayFeature> displayFeatures = new ArrayList<>();
+        displayFeatures.add(sidecarDisplayFeature);
+        setSidecarDisplayFeatures(sidecarWindowLayoutInfo, displayFeatures);
 
         sidecarCallbackCaptor.getValue().onWindowLayoutChanged(getActivityWindowToken(mActivity),
                 sidecarWindowLayoutInfo);
@@ -281,7 +298,9 @@
         WindowLayoutInfo capturedLayout = windowLayoutInfoCaptor.getValue();
         assertEquals(1, capturedLayout.getDisplayFeatures().size());
         DisplayFeature capturedDisplayFeature = capturedLayout.getDisplayFeatures().get(0);
-        assertEquals(DisplayFeature.TYPE_HINGE, capturedDisplayFeature.getType());
+        FoldingFeature foldingFeature = (FoldingFeature) capturedDisplayFeature;
+        assertNotNull(foldingFeature);
+        assertEquals(FoldingFeature.TYPE_HINGE, foldingFeature.getType());
         assertEquals(bounds, capturedDisplayFeature.getBounds());
     }
 
@@ -304,8 +323,9 @@
         Rect bounds = new Rect(1, 2, 3, 4);
         sidecarDisplayFeature.setRect(bounds);
         SidecarWindowLayoutInfo sidecarWindowLayoutInfo = new SidecarWindowLayoutInfo();
-        sidecarWindowLayoutInfo.displayFeatures = new ArrayList<>();
-        sidecarWindowLayoutInfo.displayFeatures.add(sidecarDisplayFeature);
+        List<SidecarDisplayFeature> displayFeatures = new ArrayList<>();
+        displayFeatures.add(sidecarDisplayFeature);
+        setSidecarDisplayFeatures(sidecarWindowLayoutInfo, displayFeatures);
 
         IBinder windowToken = mock(IBinder.class);
         sidecarCallbackCaptor.getValue().onWindowLayoutChanged(windowToken,
@@ -345,9 +365,8 @@
         when(mSidecarCompat.mSidecar.getWindowLayoutInfo(any())).thenReturn(layoutInfo);
         View fakeView = mock(View.class);
         doAnswer(invocation -> {
-            View.OnAttachStateChangeListener stateChangeListener =
-                    (View.OnAttachStateChangeListener) invocation.getArgument(0);
-            stateChangeListener.onViewAttachedToWindow((View) invocation.getMock());
+            View.OnAttachStateChangeListener stateChangeListener = invocation.getArgument(0);
+            stateChangeListener.onViewAttachedToWindow(fakeView);
             return null;
         }).when(fakeView).addOnAttachStateChangeListener(any());
         Window fakeWindow = new TestWindow(mActivity, fakeView);
@@ -387,6 +406,47 @@
         verify(listener).onDeviceStateChanged(expectedDeviceState);
     }
 
+    @Test
+    public void testOnDeviceStateChangedUpdatesWindowLayout() {
+        FakeExtensionImp fakeSidecarImp = new FakeExtensionImp(
+                newDeviceState(SidecarDeviceState.POSTURE_CLOSED),
+                validWindowLayoutInfo());
+        SidecarCompat compat = new SidecarCompat(fakeSidecarImp, new SidecarAdapter());
+        ExtensionInterfaceCompat.ExtensionCallbackInterface mockCallback = mock(
+                ExtensionInterfaceCompat.ExtensionCallbackInterface.class);
+        compat.setExtensionCallback(mockCallback);
+        compat.onWindowLayoutChangeListenerAdded(mActivity);
+        ArgumentCaptor<WindowLayoutInfo> windowLayoutCaptor = ArgumentCaptor.forClass(
+                WindowLayoutInfo.class);
+
+        reset(mockCallback);
+        fakeSidecarImp.triggerDeviceState(newDeviceState(SidecarDeviceState.POSTURE_OPENED));
+        verify(mockCallback).onWindowLayoutChanged(eq(mActivity), windowLayoutCaptor.capture());
+        FoldingFeature capturedFoldingFeature = (FoldingFeature) windowLayoutCaptor.getValue()
+                .getDisplayFeatures().get(0);
+        assertEquals(FoldingFeature.STATE_FLAT, capturedFoldingFeature.getState());
+
+        reset(mockCallback);
+        fakeSidecarImp.triggerDeviceState(newDeviceState(SidecarDeviceState.POSTURE_HALF_OPENED));
+        verify(mockCallback).onWindowLayoutChanged(eq(mActivity), windowLayoutCaptor.capture());
+        capturedFoldingFeature = (FoldingFeature) windowLayoutCaptor.getValue().getDisplayFeatures()
+                .get(0);
+        assertEquals(FoldingFeature.STATE_HALF_OPENED, capturedFoldingFeature.getState());
+
+        reset(mockCallback);
+        fakeSidecarImp.triggerDeviceState(newDeviceState(SidecarDeviceState.POSTURE_FLIPPED));
+        verify(mockCallback).onWindowLayoutChanged(eq(mActivity), windowLayoutCaptor.capture());
+        capturedFoldingFeature = (FoldingFeature) windowLayoutCaptor.getValue().getDisplayFeatures()
+                .get(0);
+        assertEquals(FoldingFeature.STATE_FLIPPED, capturedFoldingFeature.getState());
+
+        // No display features must be reported in closed state
+        reset(mockCallback);
+        fakeSidecarImp.triggerDeviceState(newDeviceState(SidecarDeviceState.POSTURE_CLOSED));
+        verify(mockCallback).onWindowLayoutChanged(eq(mActivity), windowLayoutCaptor.capture());
+        assertTrue(windowLayoutCaptor.getValue().getDisplayFeatures().isEmpty());
+    }
+
     private static SidecarDisplayFeature newDisplayFeature(Rect rect, int type) {
         SidecarDisplayFeature feature = new SidecarDisplayFeature();
         feature.setRect(rect);
@@ -397,23 +457,36 @@
     private static SidecarWindowLayoutInfo newWindowLayoutInfo(
             List<SidecarDisplayFeature> features) {
         SidecarWindowLayoutInfo info = new SidecarWindowLayoutInfo();
-        info.displayFeatures = new ArrayList<>();
-        info.displayFeatures.addAll(features);
+        setSidecarDisplayFeatures(info, features);
         return info;
     }
 
+    private static SidecarWindowLayoutInfo validWindowLayoutInfo() {
+        List<SidecarDisplayFeature> goodFeatures = new ArrayList<>();
+
+        goodFeatures.add(newDisplayFeature(validFoldBound(WINDOW_BOUNDS),
+                SidecarDisplayFeature.TYPE_FOLD));
+
+        return newWindowLayoutInfo(goodFeatures);
+    }
+
     private static SidecarDeviceState newDeviceState(int posture) {
         SidecarDeviceState state = new SidecarDeviceState();
-        state.posture = posture;
+        setSidecarDevicePosture(state, posture);
         return state;
     }
 
     private static final class FakeExtensionImp implements SidecarInterface {
 
+        private SidecarDeviceState mDeviceState;
+        private SidecarWindowLayoutInfo mInfo;
         private SidecarInterface.SidecarCallback mCallback;
         private List<IBinder> mTokens = new ArrayList<>();
 
-        FakeExtensionImp() {
+        FakeExtensionImp(@NonNull SidecarDeviceState deviceState,
+                @NonNull SidecarWindowLayoutInfo info) {
+            mDeviceState = deviceState;
+            mInfo = info;
             mCallback = new SidecarInterface.SidecarCallback() {
                 @Override
                 public void onDeviceStateChanged(@NonNull SidecarDeviceState newDeviceState) {
@@ -430,29 +503,29 @@
 
         @Override
         public void setSidecarCallback(@NonNull SidecarCallback callback) {
-
+            mCallback = callback;
         }
 
         @NonNull
         @Override
         public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
-            return null;
+            return mInfo;
         }
 
         @Override
         public void onWindowLayoutChangeListenerAdded(@NonNull IBinder windowToken) {
-
+            mTokens.add(windowToken);
         }
 
         @Override
         public void onWindowLayoutChangeListenerRemoved(@NonNull IBinder windowToken) {
-
+            mTokens.remove(windowToken);
         }
 
         @NonNull
         @Override
         public SidecarDeviceState getDeviceState() {
-            return null;
+            return mDeviceState;
         }
 
         @Override
@@ -469,14 +542,15 @@
         }
 
         void triggerSignal(SidecarWindowLayoutInfo info) {
-            for (IBinder token: mTokens) {
+            mInfo = info;
+            for (IBinder token : mTokens) {
                 mCallback.onWindowLayoutChanged(token, info);
             }
         }
 
         public void triggerDeviceState(SidecarDeviceState state) {
+            mDeviceState = state;
             mCallback.onDeviceStateChanged(state);
-
         }
 
         private SidecarWindowLayoutInfo malformedWindowLayoutInfo() {
@@ -494,14 +568,5 @@
 
             return newWindowLayoutInfo(malformedFeatures);
         }
-
-        private SidecarWindowLayoutInfo validWindowLayoutInfo() {
-            List<SidecarDisplayFeature> goodFeatures = new ArrayList<>();
-
-            goodFeatures.add(newDisplayFeature(validFoldBound(WINDOW_BOUNDS),
-                    SidecarDisplayFeature.TYPE_FOLD));
-
-            return newWindowLayoutInfo(goodFeatures);
-        }
     }
 }
diff --git a/window/window/src/androidTest/java/androidx/window/TestActivity.java b/window/window/src/androidTest/java/androidx/window/TestActivity.java
index ee73aac..65d1a81 100644
--- a/window/window/src/androidTest/java/androidx/window/TestActivity.java
+++ b/window/window/src/androidTest/java/androidx/window/TestActivity.java
@@ -28,7 +28,7 @@
 public class TestActivity extends Activity implements View.OnLayoutChangeListener {
 
     private int mRootViewId;
-    private CountDownLatch mLayoutLatch;
+    private CountDownLatch mLayoutLatch = new CountDownLatch(1);
     private static CountDownLatch sResumeLatch = new CountDownLatch(1);
 
     @Override
@@ -39,18 +39,9 @@
         contentView.setId(mRootViewId);
         setContentView(contentView);
 
-        resetLayoutCounter();
         getWindow().getDecorView().addOnLayoutChangeListener(this);
     }
 
-    int getWidth() {
-        return findViewById(mRootViewId).getWidth();
-    }
-
-    int getHeight() {
-        return findViewById(mRootViewId).getHeight();
-    }
-
     @Override
     public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
             int oldTop, int oldRight, int oldBottom) {
diff --git a/window/window/src/androidTest/java/androidx/window/TestBoundUtil.java b/window/window/src/androidTest/java/androidx/window/TestBoundsUtil.java
similarity index 79%
copy from window/window/src/androidTest/java/androidx/window/TestBoundUtil.java
copy to window/window/src/androidTest/java/androidx/window/TestBoundsUtil.java
index 18447f5..ff353db 100644
--- a/window/window/src/androidTest/java/androidx/window/TestBoundUtil.java
+++ b/window/window/src/androidTest/java/androidx/window/TestBoundsUtil.java
@@ -24,10 +24,11 @@
 /**
  * A utility class to provide bounds for a display feature
  */
-class TestBoundUtil {
+class TestBoundsUtil {
 
     public static Rect validFoldBound(Rect windowBounds) {
-        return new Rect(windowBounds.left, windowBounds.top, windowBounds.right, 0);
+        int verticalMid = windowBounds.top + windowBounds.height() / 2;
+        return new Rect(0, verticalMid, windowBounds.width(), verticalMid);
     }
 
     public static Rect invalidZeroBound() {
@@ -35,11 +36,11 @@
     }
 
     public static Rect invalidBoundShortWidth(Rect windowBounds) {
-        return new Rect(windowBounds.left, windowBounds.top, windowBounds.right / 2, 2);
+        return new Rect(0, 0, windowBounds.width() / 2, 2);
     }
 
-    public static Rect invalidBoundShortHeightHeight(Rect windowBounds) {
-        return new Rect(windowBounds.left, windowBounds.top, 2, windowBounds.bottom / 2);
+    public static Rect invalidBoundShortHeight(Rect windowBounds) {
+        return new Rect(0, 0, 2, windowBounds.height() / 2);
     }
 
     private static List<Rect> coreInvalidBounds(Rect windowBounds) {
@@ -47,7 +48,7 @@
 
         badBounds.add(invalidZeroBound());
         badBounds.add(invalidBoundShortWidth(windowBounds));
-        badBounds.add(invalidBoundShortHeightHeight(windowBounds));
+        badBounds.add(invalidBoundShortHeight(windowBounds));
 
         return badBounds;
     }
diff --git a/window/window/src/androidTest/java/androidx/window/TranslatorTestInterface.java b/window/window/src/androidTest/java/androidx/window/TranslatorTestInterface.java
index 90eb92b..958063b 100644
--- a/window/window/src/androidTest/java/androidx/window/TranslatorTestInterface.java
+++ b/window/window/src/androidTest/java/androidx/window/TranslatorTestInterface.java
@@ -23,8 +23,6 @@
 public interface TranslatorTestInterface {
     void testTranslate_validFeature();
     void testTranslateDeviceState();
-    void testTranslateWindowLayoutInfo_filterRemovesEmptyBoundsFeature();
-    void testTranslateWindowLayoutInfo_filterRemovesNonEmptyAreaFoldFeature();
     void testTranslateWindowLayoutInfo_filterRemovesHingeFeatureNotSpanningFullDimension();
     void testTranslateWindowLayoutInfo_filterRemovesFoldFeatureNotSpanningFullDimension();
 }
diff --git a/window/window/src/androidTest/java/androidx/window/WindowBackendTest.java b/window/window/src/androidTest/java/androidx/window/WindowBackendTest.java
index 6378d7a..1e88185 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowBackendTest.java
+++ b/window/window/src/androidTest/java/androidx/window/WindowBackendTest.java
@@ -38,6 +38,7 @@
 import java.util.concurrent.Executor;
 
 /** Tests for {@link WindowBackend} class. */
+@SuppressWarnings("unchecked")
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public final class WindowBackendTest extends WindowTestBase {
@@ -65,8 +66,9 @@
 
     private WindowLayoutInfo newTestWindowLayout() {
         List<DisplayFeature> displayFeatureList = new ArrayList<>();
-        DisplayFeature displayFeature = new DisplayFeature(
-                new Rect(10, 0, 10, 100), DisplayFeature.TYPE_HINGE);
+        DisplayFeature displayFeature = new FoldingFeature(
+                new Rect(10, 0, 10, 100), FoldingFeature.TYPE_HINGE,
+                FoldingFeature.STATE_FLAT);
         displayFeatureList.add(displayFeature);
         return new WindowLayoutInfo(displayFeatureList);
     }
diff --git a/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java b/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java
index cf6eab3..5db5039 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java
+++ b/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java
@@ -33,6 +33,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 
+import org.junit.AssumptionViolatedException;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,10 +49,9 @@
     @Test
     public void testGetCurrentWindowBounds_matchParentWindowSize_avoidCutouts_preR() {
         assumePlatformBeforeR();
+        assumeNotMultiWindow();
 
         testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
-            assumeFalse(isInMultiWindowMode(activity));
-
             WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
             lp.width = WindowManager.LayoutParams.MATCH_PARENT;
             lp.height = WindowManager.LayoutParams.MATCH_PARENT;
@@ -66,10 +66,9 @@
     @Test
     public void testGetCurrentWindowBounds_fixedWindowSize_avoidCutouts_preR() {
         assumePlatformBeforeR();
+        assumeNotMultiWindow();
 
         testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
-            assumeFalse(isInMultiWindowMode(activity));
-
             WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
             lp.width = 100;
             lp.height = 100;
@@ -84,10 +83,9 @@
     @Test
     public void testGetCurrentWindowBounds_matchParentWindowSize_layoutBehindCutouts_preR() {
         assumePlatformBeforeR();
+        assumeNotMultiWindow();
 
         testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
-            assumeFalse(isInMultiWindowMode(activity));
-
             WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
             lp.width = WindowManager.LayoutParams.MATCH_PARENT;
             lp.height = WindowManager.LayoutParams.MATCH_PARENT;
@@ -102,10 +100,9 @@
     @Test
     public void testGetCurrentWindowBounds_fixedWindowSize_layoutBehindCutouts_preR() {
         assumePlatformBeforeR();
+        assumeNotMultiWindow();
 
         testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
-            assumeFalse(isInMultiWindowMode(activity));
-
             WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
             lp.width = 100;
             lp.height = 100;
@@ -132,10 +129,9 @@
     @Test
     public void testGetMaximumWindowBounds_matchParentWindowSize_avoidCutouts_preR() {
         assumePlatformBeforeR();
+        assumeNotMultiWindow();
 
         testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
-            assumeFalse(isInMultiWindowMode(activity));
-
             WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
             lp.width = WindowManager.LayoutParams.MATCH_PARENT;
             lp.height = WindowManager.LayoutParams.MATCH_PARENT;
@@ -150,10 +146,9 @@
     @Test
     public void testGetMaximumWindowBounds_fixedWindowSize_avoidCutouts_preR() {
         assumePlatformBeforeR();
+        assumeNotMultiWindow();
 
         testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
-            assumeFalse(isInMultiWindowMode(activity));
-
             WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
             lp.width = 100;
             lp.height = 100;
@@ -168,10 +163,9 @@
     @Test
     public void testGetMaximumWindowBounds_matchParentWindowSize_layoutBehindCutouts_preR() {
         assumePlatformBeforeR();
+        assumeNotMultiWindow();
 
         testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
-            assumeFalse(isInMultiWindowMode(activity));
-
             WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
             lp.width = WindowManager.LayoutParams.MATCH_PARENT;
             lp.height = WindowManager.LayoutParams.MATCH_PARENT;
@@ -186,10 +180,9 @@
     @Test
     public void testGetMaximumWindowBounds_fixedWindowSize_layoutBehindCutouts_preR() {
         assumePlatformBeforeR();
+        assumeNotMultiWindow();
 
         testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
-            assumeFalse(isInMultiWindowMode(activity));
-
             WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
             lp.width = 100;
             lp.height = 100;
@@ -250,6 +243,20 @@
         scenario.onActivity(verifyAction);
     }
 
+    private void assumeNotMultiWindow() {
+        ActivityScenario<TestActivity> scenario = mActivityScenarioRule.getScenario();
+        try {
+            scenario.onActivity(activity -> assumeFalse(isInMultiWindowMode(activity)));
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof AssumptionViolatedException) {
+                AssumptionViolatedException failedAssumption =
+                        (AssumptionViolatedException) e.getCause();
+                throw failedAssumption;
+            }
+            throw e;
+        }
+    }
+
     private static boolean isInMultiWindowMode(Activity activity) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
             return activity.isInMultiWindowMode();
diff --git a/window/window/src/androidTest/java/androidx/window/WindowLayoutInfoTest.java b/window/window/src/androidTest/java/androidx/window/WindowLayoutInfoTest.java
index 8c97849..62154b1 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowLayoutInfoTest.java
+++ b/window/window/src/androidTest/java/androidx/window/WindowLayoutInfoTest.java
@@ -48,14 +48,11 @@
 
     @Test
     public void testBuilder_setDisplayFeatures() {
-        DisplayFeature.Builder featureBuilder = new DisplayFeature.Builder();
-        featureBuilder.setType(DisplayFeature.TYPE_HINGE);
-        featureBuilder.setBounds(new Rect(1, 0, 3, 4));
-        DisplayFeature feature1 = featureBuilder.build();
+        DisplayFeature feature1 = new FoldingFeature(new Rect(1, 0, 3, 4),
+                FoldingFeature.TYPE_HINGE, FoldingFeature.STATE_FLAT);
 
-        featureBuilder = new DisplayFeature.Builder();
-        featureBuilder.setBounds(new Rect(1, 0, 1, 4));
-        DisplayFeature feature2 = featureBuilder.build();
+        DisplayFeature feature2 = new FoldingFeature(new Rect(1, 0, 1, 4),
+                FoldingFeature.STATE_FLAT, FoldingFeature.STATE_FLAT);
 
         List<DisplayFeature> displayFeatures = new ArrayList<>();
         displayFeatures.add(feature1);
@@ -83,7 +80,8 @@
         List<DisplayFeature> originalFeatures = new ArrayList<>();
         List<DisplayFeature> differentFeatures = new ArrayList<>();
         Rect rect = new Rect(1, 0, 1, 10);
-        differentFeatures.add(new DisplayFeature(rect, 1));
+        differentFeatures.add(new FoldingFeature(rect, FoldingFeature.TYPE_HINGE,
+                FoldingFeature.STATE_FLAT));
 
         WindowLayoutInfo original = new WindowLayoutInfo(originalFeatures);
         WindowLayoutInfo different = new WindowLayoutInfo(differentFeatures);
@@ -104,13 +102,15 @@
 
     @Test
     public void testHashCode_matchesIfEqualFeatures() {
-        DisplayFeature originalFeature = new DisplayFeature(
-                new Rect(-1, 1, 1, -1),
-                0
+        DisplayFeature originalFeature = new FoldingFeature(
+                new Rect(0, 0, 100, 0),
+                FoldingFeature.TYPE_HINGE,
+                FoldingFeature.STATE_FLAT
         );
-        DisplayFeature matchingFeature = new DisplayFeature(
-                new Rect(-1, 1, 1, -1),
-                0
+        DisplayFeature matchingFeature = new FoldingFeature(
+                new Rect(0, 0, 100, 0),
+                FoldingFeature.TYPE_HINGE,
+                FoldingFeature.STATE_FLAT
         );
         List<DisplayFeature> firstFeatures = Collections.singletonList(originalFeature);
         List<DisplayFeature> secondFeatures = Collections.singletonList(matchingFeature);
diff --git a/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java b/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java
index dddc9ef..83782d0 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java
+++ b/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java
@@ -40,6 +40,7 @@
 import java.util.concurrent.Executor;
 
 /** Tests for {@link WindowManager} class. */
+@SuppressWarnings("unchecked")
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public final class WindowManagerTest extends WindowTestBase {
diff --git a/window/window/src/main/java/androidx/window/DeviceState.java b/window/window/src/main/java/androidx/window/DeviceState.java
index d031629..5584f71 100644
--- a/window/window/src/main/java/androidx/window/DeviceState.java
+++ b/window/window/src/main/java/androidx/window/DeviceState.java
@@ -23,9 +23,12 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * @deprecated Use {@link FoldingFeature} to get the state of the hinge instead. Will be removed in
+ * alpha03.
  * Information about the state of the device.
  * <p>Currently only includes the description of the state for foldable devices.
  */
+@Deprecated
 public final class DeviceState {
 
     @Posture
diff --git a/window/window/src/main/java/androidx/window/DisplayFeature.java b/window/window/src/main/java/androidx/window/DisplayFeature.java
index efa0ceb..09d9491 100644
--- a/window/window/src/main/java/androidx/window/DisplayFeature.java
+++ b/window/window/src/main/java/androidx/window/DisplayFeature.java
@@ -18,12 +18,8 @@
 
 import android.graphics.Rect;
 
-import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * Description of a physical feature on the display.
  *
@@ -31,162 +27,15 @@
  * the device. It can intrude into the application window space and create a visual distortion,
  * visual or touch discontinuity, make some area invisible or create a logical divider or separation
  * in the screen space.
- *
- * @see #TYPE_FOLD
- * @see #TYPE_HINGE
  */
-public final class DisplayFeature {
-    private final Rect mBounds;
-    @Type
-    private int mType;
-
-    DisplayFeature(@NonNull Rect bounds, @Type int type) {
-        assertValidBounds(bounds, type);
-        mBounds = new Rect(bounds);
-        mType = type;
-    }
+public interface DisplayFeature {
 
     /**
-     * Gets bounding rectangle of the physical display feature in the coordinate space of the
-     * window. The rectangle provides information about the location of the feature in the window
-     * and its size.
+     * The bounding rectangle of the feature within the application window
+     * in the window coordinate space.
      *
-     * <p>The bounds for features of type {@link #TYPE_FOLD fold} are guaranteed to be zero-high
-     * (for horizontal folds) or zero-wide (for vertical folds) and span the entire window.
-     *
-     * <p>The bounds for features of type {@link #TYPE_HINGE hinge} are guaranteed to span the
-     * entire window but, unlike folds, can have a non-zero area.
+     * @return bounds of display feature.
      */
     @NonNull
-    public Rect getBounds() {
-        return new Rect(mBounds);
-    }
-
-    /**
-     * Gets type of the physical display feature.
-     */
-    @Type
-    public int getType() {
-        return mType;
-    }
-
-    /**
-     * A fold in the flexible screen without a physical gap.
-     */
-    public static final int TYPE_FOLD = 1;
-
-    /**
-     * A physical separation with a hinge that allows two display panels to fold.
-     */
-    public static final int TYPE_HINGE = 2;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            TYPE_FOLD,
-            TYPE_HINGE,
-    })
-    @interface Type{}
-
-    private static String typeToString(@Type int type) {
-        switch (type) {
-            case TYPE_FOLD:
-                return "FOLD";
-            case TYPE_HINGE:
-                return "HINGE";
-            default:
-                return "Unknown feature type (" + type + ")";
-        }
-    }
-
-    @NonNull
-    @Override
-    public String toString() {
-        return "DisplayFeature{ bounds=" + mBounds + ", type=" + typeToString(mType) + " }";
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        DisplayFeature that = (DisplayFeature) o;
-
-        return mType == that.mType && mBounds.equals(that.mBounds);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = mBounds.hashCode();
-        result = 31 * result + mType;
-        return result;
-    }
-
-    /**
-     * Builder for {@link DisplayFeature} objects.
-     */
-    public static final class Builder {
-        private Rect mBounds = new Rect();
-        @Type
-        private int mType = TYPE_FOLD;
-
-        /**
-         * Creates an initially empty builder.
-         */
-        public Builder() {
-        }
-
-        /**
-         * Sets the bounds for the {@link DisplayFeature} instance.
-         */
-        @NonNull
-        public Builder setBounds(@NonNull Rect bounds) {
-            mBounds = bounds;
-            return this;
-        }
-
-        /**
-         * Sets the type for the {@link DisplayFeature} instance.
-         */
-        @NonNull
-        public Builder setType(@Type int type) {
-            mType = type;
-            return this;
-        }
-
-        /**
-         * Creates a {@link DisplayFeature} instance with the specified fields.
-         * @return A DisplayFeature instance.
-         */
-        @NonNull
-        public DisplayFeature build() {
-            return new DisplayFeature(mBounds, mType);
-        }
-    }
-
-    /**
-     * Throws runtime exceptions if the bounds are invalid or incompatible with the supplied type.
-     */
-    private static void assertValidBounds(Rect bounds, @Type int type) {
-        if (bounds.height() == 0 && bounds.width() == 0) {
-            throw new IllegalArgumentException("Bounding rectangle must not be empty: " + bounds);
-        }
-
-        if (type == TYPE_FOLD) {
-            if (bounds.width() != 0 && bounds.height() != 0) {
-                throw new IllegalArgumentException("Bounding rectangle must be either zero-wide "
-                        + "or zero-high for features of type " + typeToString(type));
-            }
-
-            if ((bounds.width() != 0 && bounds.left != 0)
-                    || (bounds.height() != 0 && bounds.top != 0)) {
-                throw new IllegalArgumentException("Bounding rectangle must span the entire "
-                        + "window space for features of type " + typeToString(type));
-            }
-        } else if (type == TYPE_HINGE) {
-            if (bounds.left != 0 && bounds.top != 0) {
-                throw new IllegalArgumentException("Bounding rectangle must span the entire "
-                        + "window space for features of type " + typeToString(type));
-            }
-        }
-    }
+    Rect getBounds();
 }
diff --git a/window/window/src/main/java/androidx/window/ExtensionAdapter.java b/window/window/src/main/java/androidx/window/ExtensionAdapter.java
index c2e5549..d3150e2 100644
--- a/window/window/src/main/java/androidx/window/ExtensionAdapter.java
+++ b/window/window/src/main/java/androidx/window/ExtensionAdapter.java
@@ -23,6 +23,7 @@
 import androidx.annotation.Nullable;
 import androidx.window.extensions.ExtensionDeviceState;
 import androidx.window.extensions.ExtensionDisplayFeature;
+import androidx.window.extensions.ExtensionFoldingFeature;
 import androidx.window.extensions.ExtensionWindowLayoutInfo;
 
 import java.util.ArrayList;
@@ -37,9 +38,6 @@
     DeviceState translate(ExtensionDeviceState deviceState) {
         final int posture;
         switch (deviceState.getPosture()) {
-            case ExtensionDeviceState.POSTURE_CLOSED:
-                posture = DeviceState.POSTURE_CLOSED;
-                break;
             case ExtensionDeviceState.POSTURE_FLIPPED:
                 posture = DeviceState.POSTURE_FLIPPED;
                 break;
@@ -49,7 +47,6 @@
             case ExtensionDeviceState.POSTURE_OPENED:
                 posture = DeviceState.POSTURE_OPENED;
                 break;
-            case ExtensionDeviceState.POSTURE_UNKNOWN:
             default:
                 posture = DeviceState.POSTURE_UNKNOWN;
         }
@@ -79,39 +76,62 @@
     }
 
     @Nullable
-    DisplayFeature translate(Activity activity, ExtensionDisplayFeature feature) {
-        final Rect windowBounds = WindowBoundsHelper.getInstance()
-                .computeCurrentWindowBounds(activity);
-        if (!isValid(feature, windowBounds)) {
+    DisplayFeature translate(Activity activity, ExtensionDisplayFeature displayFeature) {
+        if (!(displayFeature instanceof ExtensionFoldingFeature)) {
             return null;
         }
-        int type = DisplayFeature.TYPE_FOLD;
-        switch (feature.getType()) {
-            case ExtensionDisplayFeature.TYPE_FOLD:
-                type = DisplayFeature.TYPE_FOLD;
-                break;
-            case ExtensionDisplayFeature.TYPE_HINGE:
-                type = DisplayFeature.TYPE_HINGE;
-                break;
-        }
-        return new DisplayFeature(feature.getBounds(), type);
+        ExtensionFoldingFeature feature = (ExtensionFoldingFeature) displayFeature;
+        final Rect windowBounds = WindowBoundsHelper.getInstance()
+                .computeCurrentWindowBounds(activity);
+        return translateFoldFeature(windowBounds, feature);
     }
 
-    boolean isValid(ExtensionDisplayFeature feature, Rect windowBounds) {
+    @Nullable
+    private static DisplayFeature translateFoldFeature(@NonNull Rect windowBounds,
+            @NonNull ExtensionFoldingFeature feature) {
+        if (!isValid(windowBounds, feature)) {
+            return null;
+        }
+        int type = FoldingFeature.TYPE_FOLD;
+        switch (feature.getType()) {
+            case ExtensionFoldingFeature.TYPE_FOLD:
+                type = FoldingFeature.TYPE_FOLD;
+                break;
+            case ExtensionFoldingFeature.TYPE_HINGE:
+                type = FoldingFeature.TYPE_HINGE;
+                break;
+        }
+        int state = FoldingFeature.STATE_FLAT;
+        switch (feature.getState()) {
+            case ExtensionFoldingFeature.STATE_FLAT:
+                state = FoldingFeature.STATE_FLAT;
+                break;
+            case ExtensionFoldingFeature.STATE_FLIPPED:
+                state = FoldingFeature.STATE_FLIPPED;
+                break;
+            case ExtensionFoldingFeature.STATE_HALF_OPENED:
+                state = FoldingFeature.STATE_HALF_OPENED;
+                break;
+        }
+        return new FoldingFeature(feature.getBounds(), type, state);
+    }
+
+    private static boolean isValid(Rect windowBounds, ExtensionFoldingFeature feature) {
         if (feature.getBounds().width() == 0 && feature.getBounds().height() == 0) {
             return false;
         }
-        if (feature.getType() == ExtensionDisplayFeature.TYPE_FOLD
+        if (feature.getType() == ExtensionFoldingFeature.TYPE_FOLD
                 && !feature.getBounds().isEmpty()) {
             return false;
         }
-        if (!hasMatchingDimension(feature.getBounds(), windowBounds)) {
+        if (feature.getType() != ExtensionFoldingFeature.TYPE_FOLD
+                && feature.getType() != ExtensionFoldingFeature.TYPE_HINGE) {
             return false;
         }
-        return true;
+        return hasMatchingDimension(feature.getBounds(), windowBounds);
     }
 
-    private boolean hasMatchingDimension(Rect lhs, Rect rhs) {
+    private static boolean hasMatchingDimension(Rect lhs, Rect rhs) {
         boolean matchesWidth = lhs.left == rhs.left && lhs.right == rhs.right;
         boolean matchesHeight = lhs.top == rhs.top && lhs.bottom == rhs.bottom;
         return matchesWidth || matchesHeight;
diff --git a/window/window/src/main/java/androidx/window/ExtensionCompat.java b/window/window/src/main/java/androidx/window/ExtensionCompat.java
index 8a09172..ca97067 100644
--- a/window/window/src/main/java/androidx/window/ExtensionCompat.java
+++ b/window/window/src/main/java/androidx/window/ExtensionCompat.java
@@ -25,8 +25,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
-import androidx.window.extensions.ExtensionDeviceState;
 import androidx.window.extensions.ExtensionDisplayFeature;
+import androidx.window.extensions.ExtensionFoldingFeature;
 import androidx.window.extensions.ExtensionInterface;
 import androidx.window.extensions.ExtensionProvider;
 import androidx.window.extensions.ExtensionWindowLayoutInfo;
@@ -137,23 +137,20 @@
                                 + rtUnregisterWindowLayoutChangeListener);
             }
 
-            // ExtensionDeviceState constructor
-            ExtensionDeviceState deviceState = new ExtensionDeviceState(
-                    ExtensionDeviceState.POSTURE_UNKNOWN);
+            // {@link ExtensionFoldingFeature} constructor
+            ExtensionFoldingFeature displayFoldingFeature =
+                    new ExtensionFoldingFeature(new Rect(0, 0, 100, 0),
+                            ExtensionFoldingFeature.TYPE_FOLD,
+                            ExtensionFoldingFeature.STATE_FLAT);
 
-            // deviceState.getPosture();
-            int tmpPosture = deviceState.getPosture();
+            // displayFoldFeature.getBounds()
+            Rect tmpRect = displayFoldingFeature.getBounds();
 
-            // ExtensionDisplayFeature constructor
-            ExtensionDisplayFeature displayFeature =
-                    new ExtensionDisplayFeature(new Rect(0, 0, 0, 0),
-                            ExtensionDisplayFeature.TYPE_FOLD);
+            // displayFoldFeature.getState()
+            int tmpState = displayFoldingFeature.getState();
 
-            // displayFeature.getBounds()
-            Rect tmpRect = displayFeature.getBounds();
-
-            // displayFeature.getType()
-            int tmpType = displayFeature.getType();
+            // displayFoldFeature.getType()
+            int tmpType = displayFoldingFeature.getType();
 
             // ExtensionWindowLayoutInfo constructor
             ExtensionWindowLayoutInfo windowLayoutInfo =
@@ -163,10 +160,10 @@
             List<ExtensionDisplayFeature> tmpDisplayFeatures =
                     windowLayoutInfo.getDisplayFeatures();
             return true;
-        } catch (Exception e) {
+        } catch (Throwable t) {
             if (DEBUG) {
                 Log.e(TAG, "Extension implementation doesn't conform to interface version "
-                        + getExtensionVersion() + ", error: " + e);
+                        + getExtensionVersion() + ", error: " + t);
             }
             return false;
         }
diff --git a/window/window/src/main/java/androidx/window/ExtensionWindowBackend.java b/window/window/src/main/java/androidx/window/ExtensionWindowBackend.java
index 44e0c47..6488073 100644
--- a/window/window/src/main/java/androidx/window/ExtensionWindowBackend.java
+++ b/window/window/src/main/java/androidx/window/ExtensionWindowBackend.java
@@ -77,8 +77,12 @@
 
     private static final String TAG = "WindowServer";
 
-    private ExtensionWindowBackend() {
-        // Empty
+    @VisibleForTesting
+    ExtensionWindowBackend(@Nullable ExtensionInterfaceCompat windowExtension) {
+        mWindowExtension = windowExtension;
+        if (mWindowExtension != null) {
+            mWindowExtension.setExtensionCallback(new ExtensionListenerImpl());
+        }
     }
 
     /**
@@ -89,25 +93,14 @@
         if (sInstance == null) {
             synchronized (sLock) {
                 if (sInstance == null) {
-                    sInstance = new ExtensionWindowBackend();
-                    sInstance.initExtension(context.getApplicationContext());
+                    ExtensionInterfaceCompat windowExtension = initAndVerifyExtension(context);
+                    sInstance = new ExtensionWindowBackend(windowExtension);
                 }
             }
         }
         return sInstance;
     }
 
-    /** Tries to initialize Extension, returns early if it's not available. */
-    @SuppressLint("SyntheticAccessor")
-    @GuardedBy("sLock")
-    private void initExtension(Context context) {
-        mWindowExtension = initAndVerifyExtension(context);
-        if (mWindowExtension == null) {
-            return;
-        }
-        mWindowExtension.setExtensionCallback(new ExtensionListenerImpl());
-    }
-
     @Override
     public void registerLayoutChangeCallback(@NonNull Activity activity,
             @NonNull Executor executor, @NonNull Consumer<WindowLayoutInfo> callback) {
@@ -126,10 +119,12 @@
             WindowLayoutChangeCallbackWrapper callbackWrapper =
                     new WindowLayoutChangeCallbackWrapper(activity, executor, callback);
             mWindowLayoutChangeCallbacks.add(callbackWrapper);
+            // Read value before registering in case the extension updates synchronously.
+            // A synchronous update would result in two values emitted.
+            WindowLayoutInfo lastReportedValue = mLastReportedWindowLayouts.get(activity);
             if (!isActivityRegistered) {
                 mWindowExtension.onWindowLayoutChangeListenerAdded(activity);
             }
-            WindowLayoutInfo lastReportedValue = mLastReportedWindowLayouts.get(activity);
             if (lastReportedValue != null) {
                 callbackWrapper.accept(lastReportedValue);
             }
diff --git a/window/window/src/main/java/androidx/window/FoldingFeature.java b/window/window/src/main/java/androidx/window/FoldingFeature.java
new file mode 100644
index 0000000..34f2b68
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/FoldingFeature.java
@@ -0,0 +1,201 @@
+/*
+ * 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.Rect;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A feature that describes a fold in the flexible display
+ * or a hinge between two physical display panels.
+ */
+public class FoldingFeature implements DisplayFeature {
+
+    /**
+     * A fold in the flexible screen without a physical gap.
+     */
+    public static final int TYPE_FOLD = 1;
+
+    /**
+     * A physical separation with a hinge that allows two display panels to fold.
+     */
+    public static final int TYPE_HINGE = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            TYPE_FOLD,
+            TYPE_HINGE,
+    })
+    @interface Type{}
+
+    /**
+     * The foldable device is completely open, the screen space that is presented to the user is
+     * flat. See the
+     * <a href="https://developer.android.com/guide/topics/ui/foldables#postures">Posture</a>
+     * section in the official documentation for visual samples and references.
+     */
+    public static final int STATE_FLAT = 1;
+
+    /**
+     * The foldable device's hinge is in an intermediate position between opened and closed state,
+     * there is a non-flat angle between parts of the flexible screen or between physical screen
+     * panels. See the
+     * <a href="https://developer.android.com/guide/topics/ui/foldables#postures">Posture</a>
+     * section in the official documentation for visual samples and references.
+     */
+    public static final int STATE_HALF_OPENED = 2;
+
+    /**
+     * The foldable device is flipped with the flexible screen parts or physical screens facing
+     * opposite directions. See the
+     * <a href="https://developer.android.com/guide/topics/ui/foldables#postures">Posture</a>
+     * section in the official documentation for visual samples and references.
+     */
+    public static final int STATE_FLIPPED = 3;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            STATE_HALF_OPENED,
+            STATE_FLAT,
+            STATE_FLIPPED,
+    })
+    @interface State {}
+
+    /**
+     * The bounding rectangle of the feature within the application window in the window
+     * coordinate space.
+     */
+    @NonNull
+    private final Rect mBounds;
+
+    /**
+     * The physical type of the feature.
+     */
+    @Type
+    private final int mType;
+
+    /**
+     * The state of the feature.
+     */
+    @State
+    private final int mState;
+
+    public FoldingFeature(@NonNull Rect bounds, @Type int type, @State int state) {
+        validateFeatureBounds(bounds, type);
+        mBounds = new Rect(bounds);
+        mType = type;
+        mState = state;
+    }
+
+    @NonNull
+    @Override
+    public Rect getBounds() {
+        return new Rect(mBounds);
+    }
+
+    @Type
+    public int getType() {
+        return mType;
+    }
+
+    @State
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Verifies the bounds of the folding feature.
+     */
+    private static void validateFeatureBounds(@NonNull Rect bounds, int type) {
+        if (bounds.width() == 0 && bounds.height() == 0) {
+            throw new IllegalArgumentException("Bounds must be non zero");
+        }
+        if (type == TYPE_FOLD) {
+            if (bounds.width() != 0 && bounds.height() != 0) {
+                throw new IllegalArgumentException("Bounding rectangle must be either zero-wide "
+                        + "or zero-high for features of type " + typeToString(type));
+            }
+
+            if ((bounds.width() != 0 && bounds.left != 0)
+                    || (bounds.height() != 0 && bounds.top != 0)) {
+                throw new IllegalArgumentException("Bounding rectangle must span the entire "
+                        + "window space for features of type " + typeToString(type));
+            }
+        } else if (type == TYPE_HINGE) {
+            if (bounds.left != 0 && bounds.top != 0) {
+                throw new IllegalArgumentException("Bounding rectangle must span the entire "
+                        + "window space for features of type " + typeToString(type));
+            }
+        }
+    }
+
+    @NonNull
+    private static String typeToString(int type) {
+        switch (type) {
+            case TYPE_FOLD:
+                return "FOLD";
+            case TYPE_HINGE:
+                return "HINGE";
+            default:
+                return "Unknown feature type (" + type + ")";
+        }
+    }
+
+    @NonNull
+    private static String stateToString(int state) {
+        switch (state) {
+            case STATE_FLAT:
+                return "FLAT";
+            case STATE_FLIPPED:
+                return "FLIPPED";
+            case STATE_HALF_OPENED:
+                return "HALF_OPENED";
+            default:
+                return "Unknown feature state (" + state + ")";
+        }
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return FoldingFeature.class.getSimpleName() + " { " + mBounds + ", type="
+                + typeToString(getType()) + ", state=" + stateToString(mState) + " }";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof FoldingFeature)) return false;
+        FoldingFeature that = (FoldingFeature) o;
+        return mType == that.mType
+            && mState == that.mState
+            && mBounds.equals(that.mBounds);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mBounds.hashCode();
+        result = 31 * result + mType;
+        result = 31 * result + mState;
+        return result;
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/SidecarAdapter.java b/window/window/src/main/java/androidx/window/SidecarAdapter.java
index 1a5ceb2..b7dcbdc 100644
--- a/window/window/src/main/java/androidx/window/SidecarAdapter.java
+++ b/window/window/src/main/java/androidx/window/SidecarAdapter.java
@@ -20,16 +20,20 @@
 import static androidx.window.DeviceState.POSTURE_UNKNOWN;
 import static androidx.window.ExtensionCompat.DEBUG;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.graphics.Rect;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.window.sidecar.SidecarDeviceState;
 import androidx.window.sidecar.SidecarDisplayFeature;
 import androidx.window.sidecar.SidecarWindowLayoutInfo;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -42,14 +46,17 @@
 
     @NonNull
     List<DisplayFeature> translate(SidecarWindowLayoutInfo sidecarWindowLayoutInfo,
-            Rect windowBounds) {
+            SidecarDeviceState deviceState, Rect windowBounds) {
         List<DisplayFeature> displayFeatures = new ArrayList<>();
-        if (sidecarWindowLayoutInfo.displayFeatures == null) {
+        List<SidecarDisplayFeature> sidecarDisplayFeatures =
+                getSidecarDisplayFeatures(sidecarWindowLayoutInfo);
+        if (sidecarDisplayFeatures == null) {
             return displayFeatures;
         }
 
-        for (SidecarDisplayFeature sidecarFeature : sidecarWindowLayoutInfo.displayFeatures) {
-            final DisplayFeature displayFeature = translate(sidecarFeature, windowBounds);
+        for (SidecarDisplayFeature sidecarFeature : sidecarDisplayFeatures) {
+            final DisplayFeature displayFeature = translate(sidecarFeature, deviceState,
+                    windowBounds);
             if (displayFeature != null) {
                 displayFeatures.add(displayFeature);
             }
@@ -57,32 +64,89 @@
         return displayFeatures;
     }
 
+    // TODO(b/172620880): Workaround for Sidecar API implementation issue.
+    @SuppressLint("BanUncheckedReflection")
+    @SuppressWarnings("unchecked")
+    @VisibleForTesting
+    @Nullable
+    static List<SidecarDisplayFeature> getSidecarDisplayFeatures(SidecarWindowLayoutInfo info) {
+        try {
+            return info.displayFeatures;
+        } catch (NoSuchFieldError error) {
+            try {
+                Method methodGetFeatures = SidecarWindowLayoutInfo.class.getMethod(
+                        "getDisplayFeatures");
+                return (List<SidecarDisplayFeature>) methodGetFeatures.invoke(info);
+            } catch (NoSuchMethodException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            } catch (IllegalAccessException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            } catch (InvocationTargetException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return null;
+    }
+
+    // TODO(b/172620880): Workaround for Sidecar API implementation issue.
+    @SuppressLint("BanUncheckedReflection")
+    @VisibleForTesting
+    static void setSidecarDisplayFeatures(SidecarWindowLayoutInfo info,
+            List<SidecarDisplayFeature> displayFeatures) {
+        try {
+            info.displayFeatures = displayFeatures;
+        } catch (NoSuchFieldError error) {
+            try {
+                Method methodSetFeatures = SidecarWindowLayoutInfo.class.getMethod(
+                        "setDisplayFeatures", List.class);
+                methodSetFeatures.invoke(info, displayFeatures);
+            } catch (NoSuchMethodException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            } catch (IllegalAccessException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            } catch (InvocationTargetException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
     @NonNull
     WindowLayoutInfo translate(@NonNull Activity activity,
-            @Nullable SidecarWindowLayoutInfo extensionInfo) {
+            @Nullable SidecarWindowLayoutInfo extensionInfo, @NonNull SidecarDeviceState state) {
         if (extensionInfo == null) {
             return new WindowLayoutInfo(new ArrayList<>());
         }
 
+        SidecarDeviceState deviceState = new SidecarDeviceState();
+        int devicePosture = getSidecarDevicePosture(state);
+        setSidecarDevicePosture(deviceState, devicePosture);
+
         Rect windowBounds = WindowBoundsHelper.getInstance().computeCurrentWindowBounds(activity);
-        List<DisplayFeature> displayFeatures = translate(extensionInfo, windowBounds);
+        List<DisplayFeature> displayFeatures = translate(extensionInfo, deviceState, windowBounds);
         return new WindowLayoutInfo(displayFeatures);
     }
 
     @NonNull
-    DeviceState translate(@Nullable SidecarDeviceState sidecarDeviceState) {
-        if (sidecarDeviceState == null) {
-            return new DeviceState(POSTURE_UNKNOWN);
-        }
-
+    DeviceState translate(@NonNull SidecarDeviceState sidecarDeviceState) {
         int posture = postureFromSidecar(sidecarDeviceState);
         return new DeviceState(posture);
     }
 
-
     @DeviceState.Posture
     private static int postureFromSidecar(SidecarDeviceState sidecarDeviceState) {
-        int sidecarPosture = sidecarDeviceState.posture;
+        int sidecarPosture = getSidecarDevicePosture(sidecarDeviceState);
         if (sidecarPosture > POSTURE_MAX_KNOWN) {
             if (DEBUG) {
                 Log.d(TAG, "Unknown posture reported, WindowManager library should be updated");
@@ -92,12 +156,67 @@
         return sidecarPosture;
     }
 
+    // TODO(b/172620880): Workaround for Sidecar API implementation issue.
+    @SuppressLint("BanUncheckedReflection")
+    @VisibleForTesting
+    static int getSidecarDevicePosture(SidecarDeviceState sidecarDeviceState) {
+        try {
+            return sidecarDeviceState.posture;
+        } catch (NoSuchFieldError error) {
+            try {
+                Method methodGetPosture = SidecarDeviceState.class.getMethod("getPosture");
+                return (int) methodGetPosture.invoke(sidecarDeviceState);
+            } catch (NoSuchMethodException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            } catch (IllegalAccessException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            } catch (InvocationTargetException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return SidecarDeviceState.POSTURE_UNKNOWN;
+    }
+
+    // TODO(b/172620880): Workaround for Sidecar API implementation issue.
+    @SuppressLint("BanUncheckedReflection")
+    @VisibleForTesting
+    static void setSidecarDevicePosture(SidecarDeviceState sidecarDeviceState, int posture) {
+        try {
+            sidecarDeviceState.posture = posture;
+        } catch (NoSuchFieldError error) {
+            try {
+                Method methodSetPosture = SidecarDeviceState.class.getMethod("setPosture",
+                        int.class);
+                methodSetPosture.invoke(sidecarDeviceState, posture);
+            } catch (NoSuchMethodException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            } catch (IllegalAccessException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            } catch (InvocationTargetException e) {
+                if (DEBUG) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
     /**
      * Converts the display feature from extension. Can return {@code null} if there is an issue
      * with the value passed from extension.
      */
     @Nullable
-    private static DisplayFeature translate(SidecarDisplayFeature feature, Rect windowBounds) {
+    private static DisplayFeature translate(SidecarDisplayFeature feature,
+            SidecarDeviceState deviceState, Rect windowBounds) {
         Rect bounds = feature.getRect();
         if (bounds.width() == 0 && bounds.height() == 0) {
             if (DEBUG) {
@@ -131,6 +250,31 @@
             }
         }
 
-        return new DisplayFeature(feature.getRect(), feature.getType());
+        final int type;
+        if (feature.getType() == SidecarDisplayFeature.TYPE_HINGE) {
+            type = FoldingFeature.TYPE_HINGE;
+        } else {
+            type = FoldingFeature.TYPE_FOLD;
+        }
+
+        final int state;
+        final int devicePosture = getSidecarDevicePosture(deviceState);
+        switch (devicePosture) {
+            case SidecarDeviceState.POSTURE_CLOSED:
+            case SidecarDeviceState.POSTURE_UNKNOWN:
+                return null;
+            case SidecarDeviceState.POSTURE_FLIPPED:
+                state = FoldingFeature.STATE_FLIPPED;
+                break;
+            case SidecarDeviceState.POSTURE_HALF_OPENED:
+                state = FoldingFeature.STATE_HALF_OPENED;
+                break;
+            case SidecarDeviceState.POSTURE_OPENED:
+            default:
+                state = FoldingFeature.STATE_FLAT;
+                break;
+        }
+
+        return new FoldingFeature(feature.getRect(), type, state);
     }
 }
diff --git a/window/window/src/main/java/androidx/window/SidecarCompat.java b/window/window/src/main/java/androidx/window/SidecarCompat.java
index 7de2b77..295151b 100644
--- a/window/window/src/main/java/androidx/window/SidecarCompat.java
+++ b/window/window/src/main/java/androidx/window/SidecarCompat.java
@@ -32,7 +32,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.collection.SimpleArrayMap;
-import androidx.core.util.Consumer;
 import androidx.window.sidecar.SidecarDeviceState;
 import androidx.window.sidecar.SidecarDisplayFeature;
 import androidx.window.sidecar.SidecarInterface;
@@ -40,6 +39,7 @@
 import androidx.window.sidecar.SidecarWindowLayoutInfo;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.List;
 
 /** Extension interface compatibility wrapper for v0.1 sidecar. */
@@ -53,7 +53,7 @@
             new SimpleArrayMap<>();
 
     private ExtensionCallbackInterface mExtensionCallback;
-    private SidecarAdapter mSidecarAdapter;
+    private final SidecarAdapter mSidecarAdapter;
 
     @VisibleForTesting
     final SidecarInterface mSidecar;
@@ -92,6 +92,17 @@
             @SuppressLint("SyntheticAccessor")
             public void onDeviceStateChanged(@NonNull SidecarDeviceState newDeviceState) {
                 extensionCallback.onDeviceStateChanged(mSidecarAdapter.translate(newDeviceState));
+
+                for (int i = 0; i < mWindowListenerRegisteredContexts.size(); i++) {
+                    Activity activity = mWindowListenerRegisteredContexts.valueAt(i);
+                    IBinder windowToken = getActivityWindowToken(activity);
+                    if (windowToken == null) {
+                        continue;
+                    }
+                    SidecarWindowLayoutInfo layoutInfo = mSidecar.getWindowLayoutInfo(windowToken);
+                    extensionCallback.onWindowLayoutChanged(activity,
+                            mSidecarAdapter.translate(activity, layoutInfo, newDeviceState));
+                }
             }
 
             @Override
@@ -106,7 +117,7 @@
                 }
 
                 extensionCallback.onWindowLayoutChanged(activity,
-                        mSidecarAdapter.translate(activity, newLayout));
+                        mSidecarAdapter.translate(activity, newLayout, mSidecar.getDeviceState()));
             }
         });
     }
@@ -117,7 +128,7 @@
         IBinder windowToken = getActivityWindowToken(activity);
 
         SidecarWindowLayoutInfo windowLayoutInfo = mSidecar.getWindowLayoutInfo(windowToken);
-        return mSidecarAdapter.translate(activity, windowLayoutInfo);
+        return mSidecarAdapter.translate(activity, windowLayoutInfo, mSidecar.getDeviceState());
     }
 
     @Override
@@ -127,7 +138,8 @@
         if (windowToken != null) {
             register(windowToken, activity);
         } else {
-            FirstAttachAdapter attachAdapter = new FirstAttachAdapter((token) -> {
+            FirstAttachAdapter attachAdapter = new FirstAttachAdapter(() -> {
+                IBinder token = getActivityWindowToken(activity);
                 register(token, activity);
             });
             activity.getWindow().getDecorView().addOnAttachStateChangeListener(attachAdapter);
@@ -159,6 +171,7 @@
         mSidecar.onDeviceStateListenersChanged(isEmpty);
     }
 
+    @SuppressLint("BanUncheckedReflection")
     @Override
     @SuppressWarnings("unused")
     public boolean validateExtensionInterface() {
@@ -215,7 +228,24 @@
             tmpDeviceState = new SidecarDeviceState();
 
             // deviceState.posture
-            tmpDeviceState.posture = SidecarDeviceState.POSTURE_OPENED;
+            // TODO(b/172620880): Workaround for Sidecar API implementation issue.
+            try {
+                tmpDeviceState.posture = SidecarDeviceState.POSTURE_OPENED;
+            } catch (NoSuchFieldError error) {
+                if (DEBUG) {
+                    Log.w(TAG, "Sidecar implementation doesn't conform to primary interface "
+                            + "version, continue to check for the secondary one "
+                            + VERSION_0_1 + ", error: " + error);
+                }
+                Method methodSetPosture = SidecarDeviceState.class.getMethod("setPosture",
+                        int.class);
+                methodSetPosture.invoke(tmpDeviceState, SidecarDeviceState.POSTURE_OPENED);
+                Method methodGetPosture = SidecarDeviceState.class.getMethod("getPosture");
+                int posture = (int) methodGetPosture.invoke(tmpDeviceState);
+                if (posture != SidecarDeviceState.POSTURE_OPENED) {
+                    throw new Exception("Invalid device posture getter/setter");
+                }
+            }
 
             // SidecarDisplayFeature constructor
             SidecarDisplayFeature displayFeature = new SidecarDisplayFeature();
@@ -232,13 +262,36 @@
             SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
 
             // windowLayoutInfo.displayFeatures
-            final List<SidecarDisplayFeature> tmpDisplayFeatures = windowLayoutInfo.displayFeatures;
+            try {
+                final List<SidecarDisplayFeature> tmpDisplayFeatures =
+                        windowLayoutInfo.displayFeatures;
+                // TODO(b/172620880): Workaround for Sidecar API implementation issue.
+            } catch (NoSuchFieldError error) {
+                if (DEBUG) {
+                    Log.w(TAG, "Sidecar implementation doesn't conform to primary interface "
+                            + "version, continue to check for the secondary one "
+                            + VERSION_0_1 + ", error: " + error);
+                }
+                List<SidecarDisplayFeature> featureList = new ArrayList<>();
+                featureList.add(displayFeature);
+                Method methodSetFeatures = SidecarWindowLayoutInfo.class.getMethod(
+                        "setDisplayFeatures", List.class);
+                methodSetFeatures.invoke(windowLayoutInfo, featureList);
+                Method methodGetFeatures = SidecarWindowLayoutInfo.class.getMethod(
+                        "getDisplayFeatures");
+                @SuppressWarnings("unchecked")
+                final List<SidecarDisplayFeature> resultDisplayFeatures =
+                        (List<SidecarDisplayFeature>) methodGetFeatures.invoke(windowLayoutInfo);
+                if (!featureList.equals(resultDisplayFeatures)) {
+                    throw new Exception("Invalid display feature getter/setter");
+                }
+            }
 
             return true;
-        } catch (Exception e) {
+        } catch (Throwable t) {
             if (DEBUG) {
-                Log.e(TAG, "Extension implementation doesn't conform to interface version "
-                        + VERSION_0_1 + ", error: " + e);
+                Log.e(TAG, "Sidecar implementation doesn't conform to interface version "
+                        + VERSION_0_1 + ", error: " + t);
             }
             return false;
         }
@@ -273,15 +326,15 @@
      */
     private static class FirstAttachAdapter implements View.OnAttachStateChangeListener {
 
-        private final Consumer<IBinder> mCallback;
+        private final Runnable mCallback;
 
-        FirstAttachAdapter(Consumer<IBinder> callback) {
+        FirstAttachAdapter(Runnable callback) {
             mCallback = callback;
         }
 
         @Override
         public void onViewAttachedToWindow(View view) {
-            mCallback.accept(view.getWindowToken());
+            mCallback.run();
             view.removeOnAttachStateChangeListener(this);
         }
 
diff --git a/window/window/src/main/java/androidx/window/WindowManager.java b/window/window/src/main/java/androidx/window/WindowManager.java
index 6d83906..56d2988 100644
--- a/window/window/src/main/java/androidx/window/WindowManager.java
+++ b/window/window/src/main/java/androidx/window/WindowManager.java
@@ -50,6 +50,7 @@
      * Gets an instance of the class initialized with and connected to the provided {@link Context}.
      * All methods of this class will return information that is associated with this visual
      * context.
+     *
      * @param context A visual context, such as an {@link Activity} or a {@link ContextWrapper}
      *                around one, to use for initialization.
      */
@@ -61,8 +62,10 @@
      * Gets an instance of the class initialized with and connected to the provided {@link Context}.
      * All methods of this class will return information that is associated with this visual
      * context.
-     * @param context A visual context, such as an {@link Activity} or a {@link ContextWrapper}
-     *                around one, to use for initialization.
+     *
+     * @param context       A visual context, such as an {@link Activity} or a
+     * {@link ContextWrapper}
+     *                      around one, to use for initialization.
      * @param windowBackend Backing server class that will provide information for this instance.
      *                      Pass a custom {@link WindowBackend} implementation for testing.
      */
@@ -80,6 +83,7 @@
     /**
      * Registers a callback for layout changes of the window of the current visual {@link Context}.
      * Must be called only after the it is attached to the window.
+     *
      * @see Activity#onAttachedToWindow()
      */
     public void registerLayoutChangeCallback(@NonNull Executor executor,
@@ -95,16 +99,20 @@
     }
 
     /**
+     * @deprecated {@link DeviceState} information has been merged into {@link WindowLayoutInfo}
      * Registers a callback for device state changes.
      */
+    @Deprecated
     public void registerDeviceStateChangeCallback(@NonNull Executor executor,
             @NonNull Consumer<DeviceState> callback) {
         mWindowBackend.registerDeviceStateChangeCallback(executor, callback);
     }
 
     /**
+     * @deprecated {@link DeviceState} information has been merged into {@link WindowLayoutInfo}
      * Unregisters a callback for device state changes.
      */
+    @Deprecated
     public void unregisterDeviceStateChangeCallback(@NonNull Consumer<DeviceState> callback) {
         mWindowBackend.unregisterDeviceStateChangeCallback(callback);
     }
@@ -160,6 +168,7 @@
 
     /**
      * Unwraps the hierarchy of {@link ContextWrapper}-s until {@link Activity} is reached.
+     *
      * @return Base {@link Activity} context or {@code null} if not available.
      */
     @Nullable
diff --git a/car/app/app/src/main/aidl/androidx/car/app/IOnSelectedListener.aidl b/window/window/src/test/java/androidx/window/ActivityTestUtil.java
similarity index 65%
copy from car/app/app/src/main/aidl/androidx/car/app/IOnSelectedListener.aidl
copy to window/window/src/test/java/androidx/window/ActivityTestUtil.java
index 2896a83..2d6d5d7 100644
--- a/car/app/app/src/main/aidl/androidx/car/app/IOnSelectedListener.aidl
+++ b/window/window/src/test/java/androidx/window/ActivityTestUtil.java
@@ -14,11 +14,18 @@
  * limitations under the License.
  */
 
-package androidx.car.app;
+package androidx.window;
 
-import androidx.car.app.IOnDoneCallback;
+import android.app.Activity;
+import android.os.IBinder;
 
-/** @hide */
-oneway interface IOnSelectedListener {
-  void onSelected(int index, IOnDoneCallback callback) = 1;
+import androidx.annotation.NonNull;
+
+public class ActivityTestUtil {
+
+    private ActivityTestUtil() { }
+
+    static IBinder getActivityWindowToken(@NonNull Activity activity) {
+        return activity.getWindow().getAttributes().token;
+    }
 }
diff --git a/window/window/src/test/java/androidx/window/ExtensionWindowBackendUnitTest.java b/window/window/src/test/java/androidx/window/ExtensionWindowBackendUnitTest.java
new file mode 100644
index 0000000..b79864f
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/ExtensionWindowBackendUnitTest.java
@@ -0,0 +1,367 @@
+/*
+ * 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 com.google.common.truth.Truth.assertThat;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Consumer;
+
+import com.google.common.collect.BoundType;
+import com.google.common.collect.Range;
+
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link ExtensionWindowBackend} that run on the JVM.
+ */
+@SuppressWarnings("deprecation") // TODO(b/173739071) Remove DeviceState
+public class ExtensionWindowBackendUnitTest {
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = mock(Context.class);
+        ExtensionWindowBackend.resetInstance();
+    }
+
+    @Test
+    public void testGetInstance() {
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        assertNotNull(backend);
+
+        // Verify that getInstance always returns the same value
+        ExtensionWindowBackend newBackend = ExtensionWindowBackend.getInstance(mContext);
+        assertEquals(backend, newBackend);
+    }
+
+    @Test
+    public void testRegisterDeviceStateChangeCallback_noExtension() {
+        // Verify method with extension
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        assumeTrue(backend.mWindowExtension == null);
+        SimpleConsumer<DeviceState> simpleConsumer = new SimpleConsumer<>();
+
+        backend.registerDeviceStateChangeCallback(directExecutor(), simpleConsumer);
+
+        DeviceState deviceState = simpleConsumer.lastValue();
+        assertNotNull(deviceState);
+        assertThat(deviceState.getPosture()).isIn(Range.range(
+                DeviceState.POSTURE_UNKNOWN, BoundType.CLOSED,
+                DeviceState.POSTURE_MAX_KNOWN, BoundType.CLOSED));
+        DeviceState initialLastReportedState = backend.mLastReportedDeviceState;
+
+        // Verify method without extension
+        backend.mWindowExtension = null;
+        SimpleConsumer<DeviceState> noExtensionConsumer = new SimpleConsumer<>();
+        backend.registerDeviceStateChangeCallback(directExecutor(), noExtensionConsumer);
+        deviceState = noExtensionConsumer.lastValue();
+        assertNotNull(deviceState);
+        assertEquals(DeviceState.POSTURE_UNKNOWN, deviceState.getPosture());
+        // Verify that last reported state does not change when using the getter
+        assertEquals(initialLastReportedState, backend.mLastReportedDeviceState);
+    }
+
+    @Test
+    public void testRegisterLayoutChangeCallback() {
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
+
+        // Check registering the layout change callback
+        Consumer<WindowLayoutInfo> consumer = mock(WindowLayoutInfoConsumer.class);
+        Activity activity = mock(Activity.class);
+        backend.registerLayoutChangeCallback(activity, Runnable::run, consumer);
+
+        assertEquals(1, backend.mWindowLayoutChangeCallbacks.size());
+        verify(backend.mWindowExtension).onWindowLayoutChangeListenerAdded(activity);
+
+        // Check unregistering the layout change callback
+        backend.unregisterLayoutChangeCallback(consumer);
+
+        assertTrue(backend.mWindowLayoutChangeCallbacks.isEmpty());
+        verify(backend.mWindowExtension).onWindowLayoutChangeListenerRemoved(eq(activity));
+    }
+
+    @Test
+    public void testRegisterLayoutChangeCallback_synchronousExtension() {
+        WindowLayoutInfo expectedInfo = newTestWindowLayoutInfo();
+        ExtensionInterfaceCompat extensionInterfaceCompat =
+                new SynchronousExtensionInterface(expectedInfo,
+                newTestDeviceState());
+        ExtensionWindowBackend backend = new ExtensionWindowBackend(extensionInterfaceCompat);
+
+        // Check registering the layout change callback
+        Consumer<WindowLayoutInfo> consumer = mock(WindowLayoutInfoConsumer.class);
+        Activity activity = mock(Activity.class);
+        backend.registerLayoutChangeCallback(activity, Runnable::run, consumer);
+
+        // Check unregistering the layout change callback
+        verify(consumer).accept(expectedInfo);
+    }
+
+    @Test
+    public void testRegisterLayoutChangeCallback_callsExtensionOnce() {
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
+
+        // Check registering the layout change callback
+        Consumer<WindowLayoutInfo> consumer = mock(WindowLayoutInfoConsumer.class);
+        Activity activity = mock(Activity.class);
+        backend.registerLayoutChangeCallback(activity, Runnable::run, consumer);
+        backend.registerLayoutChangeCallback(activity, Runnable::run,
+                mock(WindowLayoutInfoConsumer.class));
+
+        assertEquals(2, backend.mWindowLayoutChangeCallbacks.size());
+        verify(backend.mWindowExtension).onWindowLayoutChangeListenerAdded(activity);
+
+        // Check unregistering the layout change callback
+        backend.unregisterLayoutChangeCallback(consumer);
+
+        assertEquals(1, backend.mWindowLayoutChangeCallbacks.size());
+        verify(backend.mWindowExtension, times(0))
+                .onWindowLayoutChangeListenerRemoved(eq(activity));
+    }
+
+    @Test
+    public void testRegisterLayoutChangeCallback_clearListeners() {
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
+
+        // Check registering the layout change callback
+        Consumer<WindowLayoutInfo> firstConsumer = mock(WindowLayoutInfoConsumer.class);
+        Consumer<WindowLayoutInfo> secondConsumer = mock(WindowLayoutInfoConsumer.class);
+        Activity activity = mock(Activity.class);
+        backend.registerLayoutChangeCallback(activity, Runnable::run, firstConsumer);
+        backend.registerLayoutChangeCallback(activity, Runnable::run, secondConsumer);
+
+        // Check unregistering the layout change callback
+        backend.unregisterLayoutChangeCallback(firstConsumer);
+        backend.unregisterLayoutChangeCallback(secondConsumer);
+
+        assertTrue(backend.mWindowLayoutChangeCallbacks.isEmpty());
+        verify(backend.mWindowExtension).onWindowLayoutChangeListenerRemoved(activity);
+    }
+
+    @Test
+    public void testRegisterDeviceChangeCallback() {
+        ExtensionInterfaceCompat mockInterface = mock(ExtensionInterfaceCompat.class);
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        backend.mWindowExtension = mockInterface;
+
+        // Check registering the device state change callback
+        Consumer<DeviceState> consumer = mock(DeviceStateConsumer.class);
+        backend.registerDeviceStateChangeCallback(Runnable::run, consumer);
+
+        assertEquals(1, backend.mDeviceStateChangeCallbacks.size());
+        verify(backend.mWindowExtension).onDeviceStateListenersChanged(eq(false));
+
+        // Check unregistering the device state change callback
+        backend.unregisterDeviceStateChangeCallback(consumer);
+
+        assertTrue(backend.mDeviceStateChangeCallbacks.isEmpty());
+        verify(backend.mWindowExtension).onDeviceStateListenersChanged(eq(true));
+    }
+
+    @Test
+    public void testDeviceChangeCallback() {
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
+
+        // Check that callbacks from the extension are propagated correctly
+        Consumer<DeviceState> consumer = mock(DeviceStateConsumer.class);
+
+        backend.registerDeviceStateChangeCallback(Runnable::run, consumer);
+        DeviceState deviceState = newTestDeviceState();
+        ExtensionWindowBackend.ExtensionListenerImpl backendListener =
+                backend.new ExtensionListenerImpl();
+        backendListener.onDeviceStateChanged(deviceState);
+
+        verify(consumer, times(1)).accept(eq(deviceState));
+        assertEquals(deviceState, backend.mLastReportedDeviceState);
+
+        // Test that the same value wouldn't be reported again
+        backendListener.onDeviceStateChanged(deviceState);
+        verify(consumer, times(1)).accept(any());
+    }
+
+    @Test
+    public void testDeviceChangeChangeCallback_callsExtensionOnce() {
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
+
+        // Check registering the layout change callback
+        Consumer<DeviceState> consumer = mock(DeviceStateConsumer.class);
+        backend.registerDeviceStateChangeCallback(Runnable::run, consumer);
+        backend.registerDeviceStateChangeCallback(Runnable::run, mock(DeviceStateConsumer.class));
+
+        assertEquals(2, backend.mDeviceStateChangeCallbacks.size());
+        verify(backend.mWindowExtension).onDeviceStateListenersChanged(false);
+
+        // Check unregistering the layout change callback
+        backend.unregisterDeviceStateChangeCallback(consumer);
+
+        assertEquals(1, backend.mDeviceStateChangeCallbacks.size());
+        verify(backend.mWindowExtension, times(0))
+                .onDeviceStateListenersChanged(true);
+    }
+
+    @Test
+    public void testDeviceChangeChangeCallback_clearListeners() {
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
+
+        // Check registering the layout change callback
+        Consumer<DeviceState> firstConsumer = mock(DeviceStateConsumer.class);
+        Consumer<DeviceState> secondConsumer = mock(DeviceStateConsumer.class);
+        backend.registerDeviceStateChangeCallback(Runnable::run, firstConsumer);
+        backend.registerDeviceStateChangeCallback(Runnable::run, secondConsumer);
+
+        // Check unregistering the layout change callback
+        backend.unregisterDeviceStateChangeCallback(firstConsumer);
+        backend.unregisterDeviceStateChangeCallback(secondConsumer);
+
+        assertTrue(backend.mDeviceStateChangeCallbacks.isEmpty());
+        verify(backend.mWindowExtension).onDeviceStateListenersChanged(true);
+    }
+
+    @Test
+    public void testDeviceChangeCallback_relayLastEmittedValue() {
+        DeviceState expectedState = newTestDeviceState();
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        Consumer<DeviceState> consumer = mock(DeviceStateConsumer.class);
+        backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
+        backend.mLastReportedDeviceState = expectedState;
+
+        backend.registerDeviceStateChangeCallback(Runnable::run, consumer);
+
+        verify(consumer).accept(expectedState);
+    }
+
+    @Test
+    public void testDeviceChangeCallback_clearLastEmittedValue() {
+        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
+        Consumer<DeviceState> consumer = mock(DeviceStateConsumer.class);
+
+        backend.registerDeviceStateChangeCallback(Runnable::run, consumer);
+        backend.unregisterDeviceStateChangeCallback(consumer);
+
+        assertTrue(backend.mDeviceStateChangeCallbacks.isEmpty());
+        assertNull(backend.mLastReportedDeviceState);
+    }
+
+    private static WindowLayoutInfo newTestWindowLayoutInfo() {
+        WindowLayoutInfo.Builder builder = new WindowLayoutInfo.Builder();
+        return builder.build();
+    }
+
+    private static DeviceState newTestDeviceState() {
+        DeviceState.Builder builder = new DeviceState.Builder();
+        builder.setPosture(DeviceState.POSTURE_OPENED);
+        return builder.build();
+    }
+
+    private interface DeviceStateConsumer extends Consumer<DeviceState> { }
+
+    private interface WindowLayoutInfoConsumer extends Consumer<WindowLayoutInfo> { }
+
+    private static class SimpleConsumer<T> implements Consumer<T> {
+        private final List<T> mValues;
+
+        SimpleConsumer() {
+            mValues = new ArrayList<>();
+        }
+
+        @Override
+        public void accept(T t) {
+            mValues.add(t);
+        }
+
+        T lastValue() {
+            return mValues.get(mValues.size() - 1);
+        }
+    }
+
+    private static class SynchronousExtensionInterface implements ExtensionInterfaceCompat {
+
+        private ExtensionCallbackInterface mInterface;
+        private final DeviceState mDeviceState;
+        private final WindowLayoutInfo mWindowLayoutInfo;
+
+        SynchronousExtensionInterface(WindowLayoutInfo windowLayoutInfo, DeviceState deviceState) {
+            mInterface = new ExtensionCallbackInterface() {
+                @Override
+                public void onDeviceStateChanged(@NonNull @NotNull DeviceState newDeviceState) {
+
+                }
+
+                @Override
+                public void onWindowLayoutChanged(@NonNull @NotNull Activity activity,
+                        @NonNull @NotNull WindowLayoutInfo newLayout) {
+
+                }
+            };
+            mWindowLayoutInfo = windowLayoutInfo;
+            mDeviceState = deviceState;
+        }
+
+        @Override
+        public boolean validateExtensionInterface() {
+            return true;
+        }
+
+        @Override
+        public void setExtensionCallback(
+                @NonNull @NotNull ExtensionCallbackInterface extensionCallback) {
+            mInterface = extensionCallback;
+        }
+
+        @Override
+        public void onWindowLayoutChangeListenerAdded(@NonNull @NotNull Activity activity) {
+            mInterface.onWindowLayoutChanged(activity, mWindowLayoutInfo);
+        }
+
+        @Override
+        public void onWindowLayoutChangeListenerRemoved(@NonNull @NotNull Activity activity) {
+
+        }
+
+        @Override
+        public void onDeviceStateListenersChanged(boolean isEmpty) {
+            mInterface.onDeviceStateChanged(mDeviceState);
+        }
+    }
+}
diff --git a/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java b/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java
new file mode 100644
index 0000000..a7b9da2
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java
@@ -0,0 +1,376 @@
+/*
+ * 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 androidx.window.ActivityTestUtil.getActivityWindowToken;
+import static androidx.window.TestBoundsUtil.invalidFoldBounds;
+import static androidx.window.TestBoundsUtil.invalidHingeBounds;
+import static androidx.window.TestBoundsUtil.validFoldBound;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.window.sidecar.SidecarDeviceState;
+import androidx.window.sidecar.SidecarDisplayFeature;
+import androidx.window.sidecar.SidecarInterface;
+import androidx.window.sidecar.SidecarWindowLayoutInfo;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link SidecarCompat} that run on the JVM.
+ */
+public final class SidecarCompatUnitTest {
+
+    private static final Rect WINDOW_BOUNDS = new Rect(1, 1, 50, 100);
+
+    private Activity mActivity;
+    private SidecarCompat mSidecarCompat;
+
+    @Before
+    public void setUp() {
+        TestWindowBoundsHelper mWindowBoundsHelper = new TestWindowBoundsHelper();
+        mWindowBoundsHelper.setCurrentBounds(WINDOW_BOUNDS);
+        WindowBoundsHelper.setForTesting(mWindowBoundsHelper);
+
+        mActivity = mock(Activity.class);
+        Window window = spy(new TestWindow(mActivity));
+        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+        doReturn(params).when(window).getAttributes();
+        when(mActivity.getWindow()).thenReturn(window);
+
+        SidecarInterface mockSidecarInterface = mock(SidecarInterface.class);
+        when(mockSidecarInterface.getDeviceState()).thenReturn(
+                newDeviceState(DeviceState.POSTURE_FLIPPED));
+        when(mockSidecarInterface.getWindowLayoutInfo(any())).thenReturn(
+                newWindowLayoutInfo(new ArrayList<>()));
+        mSidecarCompat = new SidecarCompat(mockSidecarInterface, new SidecarAdapter());
+    }
+
+    @After
+    public void tearDown() {
+        WindowBoundsHelper.setForTesting(null);
+    }
+
+    @Test
+    public void testGetDeviceState() {
+        FakeExtensionImp fakeSidecarImp = new FakeExtensionImp();
+        SidecarCompat compat = new SidecarCompat(fakeSidecarImp, new SidecarAdapter());
+        ExtensionInterfaceCompat.ExtensionCallbackInterface mockCallback = mock(
+                ExtensionInterfaceCompat.ExtensionCallbackInterface.class);
+        compat.setExtensionCallback(mockCallback);
+        compat.onDeviceStateListenersChanged(false);
+        SidecarDeviceState deviceState = newDeviceState(SidecarDeviceState.POSTURE_OPENED);
+
+        fakeSidecarImp.triggerDeviceState(deviceState);
+
+        verify(mockCallback).onDeviceStateChanged(any());
+    }
+
+    @Test
+    public void testGetWindowLayout_featureWithEmptyBounds() {
+        // Add a feature with an empty bounds to the reported list
+        SidecarWindowLayoutInfo originalWindowLayoutInfo =
+                mSidecarCompat.mSidecar.getWindowLayoutInfo(getActivityWindowToken(mActivity));
+        List<SidecarDisplayFeature> sidecarDisplayFeatures =
+                originalWindowLayoutInfo.displayFeatures;
+        SidecarDisplayFeature newFeature = new SidecarDisplayFeature();
+        newFeature.setRect(new Rect());
+        sidecarDisplayFeatures.add(newFeature);
+
+        // Verify that this feature is skipped.
+        WindowLayoutInfo windowLayoutInfo = mSidecarCompat.getWindowLayoutInfo(mActivity);
+
+        assertEquals(sidecarDisplayFeatures.size() - 1,
+                windowLayoutInfo.getDisplayFeatures().size());
+    }
+
+    @Test
+    public void testGetWindowLayout_foldWithNonZeroArea() {
+        SidecarWindowLayoutInfo originalWindowLayoutInfo =
+                mSidecarCompat.mSidecar.getWindowLayoutInfo(mock(IBinder.class));
+        List<SidecarDisplayFeature> sidecarDisplayFeatures =
+                originalWindowLayoutInfo.displayFeatures;
+        // Horizontal fold.
+        sidecarDisplayFeatures.add(
+                newDisplayFeature(new Rect(0, 1, WINDOW_BOUNDS.width(), 2),
+                        SidecarDisplayFeature.TYPE_FOLD));
+        // Vertical fold.
+        sidecarDisplayFeatures.add(
+                newDisplayFeature(new Rect(1, 0, 2, WINDOW_BOUNDS.height()),
+                        SidecarDisplayFeature.TYPE_FOLD));
+
+        // Verify that these features are skipped.
+        WindowLayoutInfo windowLayoutInfo =
+                mSidecarCompat.getWindowLayoutInfo(mActivity);
+
+        assertEquals(sidecarDisplayFeatures.size() - 2,
+                windowLayoutInfo.getDisplayFeatures().size());
+    }
+
+    @Test
+    public void testGetWindowLayout_hingeNotSpanningEntireWindow() {
+        SidecarWindowLayoutInfo originalWindowLayoutInfo =
+                mSidecarCompat.mSidecar.getWindowLayoutInfo(mock(IBinder.class));
+        List<SidecarDisplayFeature> sidecarDisplayFeatures =
+                originalWindowLayoutInfo.displayFeatures;
+        // Horizontal hinge.
+        sidecarDisplayFeatures.add(
+                newDisplayFeature(new Rect(0, 1, WINDOW_BOUNDS.width() - 1, 2),
+                        SidecarDisplayFeature.TYPE_FOLD));
+        // Vertical hinge.
+        sidecarDisplayFeatures.add(
+                newDisplayFeature(new Rect(1, 0, 2, WINDOW_BOUNDS.height() - 1),
+                        SidecarDisplayFeature.TYPE_FOLD));
+
+        // Verify that these features are skipped.
+        WindowLayoutInfo windowLayoutInfo =
+                mSidecarCompat.getWindowLayoutInfo(mActivity);
+
+        assertEquals(sidecarDisplayFeatures.size() - 2,
+                windowLayoutInfo.getDisplayFeatures().size());
+    }
+
+    @Test
+    public void testGetWindowLayout_foldNotSpanningEntireWindow() {
+        SidecarWindowLayoutInfo originalWindowLayoutInfo =
+                mSidecarCompat.mSidecar.getWindowLayoutInfo(mock(IBinder.class));
+        List<SidecarDisplayFeature> sidecarDisplayFeatures =
+                originalWindowLayoutInfo.displayFeatures;
+        // Horizontal fold.
+        sidecarDisplayFeatures.add(
+                newDisplayFeature(new Rect(0, 1, WINDOW_BOUNDS.width() - 1, 2),
+                        SidecarDisplayFeature.TYPE_FOLD));
+        // Vertical fold.
+        sidecarDisplayFeatures.add(
+                newDisplayFeature(new Rect(1, 0, 2, WINDOW_BOUNDS.height() - 1),
+                        SidecarDisplayFeature.TYPE_FOLD));
+
+        // Verify that these features are skipped.
+        WindowLayoutInfo windowLayoutInfo =
+                mSidecarCompat.getWindowLayoutInfo(mActivity);
+
+        assertEquals(sidecarDisplayFeatures.size() - 2,
+                windowLayoutInfo.getDisplayFeatures().size());
+    }
+
+    @Test
+    public void testOnWindowLayoutChangeListenerAdded() {
+        IBinder expectedToken = mock(IBinder.class);
+        mActivity.getWindow().getAttributes().token = expectedToken;
+        mSidecarCompat.onWindowLayoutChangeListenerAdded(mActivity);
+        verify(mSidecarCompat.mSidecar).onWindowLayoutChangeListenerAdded(eq(expectedToken));
+    }
+
+    @Test
+    public void testOnWindowLayoutChangeListenerAdded_emitInitialValueDelayed() {
+        SidecarWindowLayoutInfo layoutInfo = new SidecarWindowLayoutInfo();
+        WindowLayoutInfo expectedLayoutInfo = new WindowLayoutInfo(new ArrayList<>());
+        ExtensionInterfaceCompat.ExtensionCallbackInterface listener =
+                mock(ExtensionInterfaceCompat.ExtensionCallbackInterface.class);
+        mSidecarCompat.setExtensionCallback(listener);
+        when(mSidecarCompat.mSidecar.getWindowLayoutInfo(any())).thenReturn(layoutInfo);
+        View fakeView = mock(View.class);
+        Window mockWindow = mock(Window.class);
+        when(mockWindow.getAttributes()).thenReturn(new WindowManager.LayoutParams());
+        doAnswer(invocation -> {
+            View.OnAttachStateChangeListener stateChangeListener = invocation.getArgument(0);
+            mockWindow.getAttributes().token = mock(IBinder.class);
+            stateChangeListener.onViewAttachedToWindow(fakeView);
+            return null;
+        }).when(fakeView).addOnAttachStateChangeListener(any());
+        when(mockWindow.getDecorView()).thenReturn(fakeView);
+        when(mActivity.getWindow()).thenReturn(mockWindow);
+
+        mSidecarCompat.onWindowLayoutChangeListenerAdded(mActivity);
+
+        verify(listener).onWindowLayoutChanged(mActivity, expectedLayoutInfo);
+        verify(mSidecarCompat.mSidecar).onWindowLayoutChangeListenerAdded(
+                getActivityWindowToken(mActivity));
+        verify(fakeView).addOnAttachStateChangeListener(any());
+    }
+
+    @Test
+    public void testOnWindowLayoutChangeListenerRemoved() {
+        IBinder windowToken = getActivityWindowToken(mActivity);
+        mSidecarCompat.onWindowLayoutChangeListenerRemoved(mActivity);
+        verify(mSidecarCompat.mSidecar).onWindowLayoutChangeListenerRemoved(eq(windowToken));
+    }
+
+    @Test
+    public void testOnDeviceStateListenersChanged() {
+        mSidecarCompat.onDeviceStateListenersChanged(true);
+        verify(mSidecarCompat.mSidecar).onDeviceStateListenersChanged(eq(true));
+    }
+
+    @Test
+    public void testOnDeviceStateListenersAdded_emitInitialValue() {
+        SidecarDeviceState deviceState = new SidecarDeviceState();
+        DeviceState expectedDeviceState = new DeviceState(DeviceState.POSTURE_UNKNOWN);
+        ExtensionInterfaceCompat.ExtensionCallbackInterface listener =
+                mock(ExtensionInterfaceCompat.ExtensionCallbackInterface.class);
+        mSidecarCompat.setExtensionCallback(listener);
+        when(mSidecarCompat.mSidecar.getDeviceState()).thenReturn(deviceState);
+
+        mSidecarCompat.onDeviceStateListenersChanged(false);
+
+        verify(listener).onDeviceStateChanged(expectedDeviceState);
+    }
+
+    private static SidecarDisplayFeature newDisplayFeature(Rect rect, int type) {
+        SidecarDisplayFeature feature = new SidecarDisplayFeature();
+        feature.setRect(rect);
+        feature.setType(type);
+        return feature;
+    }
+
+    private static SidecarWindowLayoutInfo newWindowLayoutInfo(
+            List<SidecarDisplayFeature> features) {
+        SidecarWindowLayoutInfo info = new SidecarWindowLayoutInfo();
+        info.displayFeatures = new ArrayList<>();
+        info.displayFeatures.addAll(features);
+        return info;
+    }
+
+    private static SidecarDeviceState newDeviceState(int posture) {
+        SidecarDeviceState state = new SidecarDeviceState();
+        state.posture = posture;
+        return state;
+    }
+
+    private static final class FakeExtensionImp implements SidecarInterface {
+
+        private SidecarInterface.SidecarCallback mCallback;
+        private List<IBinder> mTokens = new ArrayList<>();
+
+        FakeExtensionImp() {
+            mCallback = new SidecarInterface.SidecarCallback() {
+                @Override
+                public void onDeviceStateChanged(@NonNull SidecarDeviceState newDeviceState) {
+
+                }
+
+                @Override
+                public void onWindowLayoutChanged(@NonNull IBinder windowToken,
+                        @NonNull SidecarWindowLayoutInfo newLayout) {
+
+                }
+            };
+        }
+
+        @Override
+        public void setSidecarCallback(@NonNull SidecarCallback callback) {
+
+        }
+
+        @NonNull
+        @Override
+        public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
+            return null;
+        }
+
+        @Override
+        public void onWindowLayoutChangeListenerAdded(@NonNull IBinder windowToken) {
+
+        }
+
+        @Override
+        public void onWindowLayoutChangeListenerRemoved(@NonNull IBinder windowToken) {
+
+        }
+
+        @NonNull
+        @Override
+        public SidecarDeviceState getDeviceState() {
+            SidecarDeviceState state = new SidecarDeviceState();
+            return state;
+        }
+
+        @Override
+        public void onDeviceStateListenersChanged(boolean isEmpty) {
+
+        }
+
+        void triggerMalformedSignal() {
+            triggerSignal(malformedWindowLayoutInfo());
+        }
+
+        void triggerGoodSignal() {
+            triggerSignal(validWindowLayoutInfo());
+        }
+
+        void triggerSignal(SidecarWindowLayoutInfo info) {
+            for (IBinder token: mTokens) {
+                triggerSignal(token, info);
+            }
+        }
+
+        void triggerSignal(IBinder token, SidecarWindowLayoutInfo info) {
+            mCallback.onWindowLayoutChanged(token, info);
+        }
+
+        public void triggerDeviceState(SidecarDeviceState state) {
+            mCallback.onDeviceStateChanged(state);
+
+        }
+
+        private SidecarWindowLayoutInfo malformedWindowLayoutInfo() {
+            List<SidecarDisplayFeature> malformedFeatures = new ArrayList<>();
+
+            for (Rect malformedBound : invalidFoldBounds(WINDOW_BOUNDS)) {
+                malformedFeatures.add(newDisplayFeature(malformedBound,
+                        SidecarDisplayFeature.TYPE_FOLD));
+            }
+
+            for (Rect malformedBound : invalidHingeBounds(WINDOW_BOUNDS)) {
+                malformedFeatures.add(newDisplayFeature(malformedBound,
+                        SidecarDisplayFeature.TYPE_HINGE));
+            }
+
+            return newWindowLayoutInfo(malformedFeatures);
+        }
+
+        private SidecarWindowLayoutInfo validWindowLayoutInfo() {
+            List<SidecarDisplayFeature> goodFeatures = new ArrayList<>();
+
+            goodFeatures.add(newDisplayFeature(validFoldBound(WINDOW_BOUNDS),
+                    SidecarDisplayFeature.TYPE_FOLD));
+
+            return newWindowLayoutInfo(goodFeatures);
+        }
+    }
+}
diff --git a/window/window/src/androidTest/java/androidx/window/TestBoundUtil.java b/window/window/src/test/java/androidx/window/TestBoundsUtil.java
similarity index 91%
rename from window/window/src/androidTest/java/androidx/window/TestBoundUtil.java
rename to window/window/src/test/java/androidx/window/TestBoundsUtil.java
index 18447f5..4988e40 100644
--- a/window/window/src/androidTest/java/androidx/window/TestBoundUtil.java
+++ b/window/window/src/test/java/androidx/window/TestBoundsUtil.java
@@ -24,7 +24,7 @@
 /**
  * A utility class to provide bounds for a display feature
  */
-class TestBoundUtil {
+class TestBoundsUtil {
 
     public static Rect validFoldBound(Rect windowBounds) {
         return new Rect(windowBounds.left, windowBounds.top, windowBounds.right, 0);
@@ -38,7 +38,7 @@
         return new Rect(windowBounds.left, windowBounds.top, windowBounds.right / 2, 2);
     }
 
-    public static Rect invalidBoundShortHeightHeight(Rect windowBounds) {
+    public static Rect invalidBoundShortHeight(Rect windowBounds) {
         return new Rect(windowBounds.left, windowBounds.top, 2, windowBounds.bottom / 2);
     }
 
@@ -47,7 +47,7 @@
 
         badBounds.add(invalidZeroBound());
         badBounds.add(invalidBoundShortWidth(windowBounds));
-        badBounds.add(invalidBoundShortHeightHeight(windowBounds));
+        badBounds.add(invalidBoundShortHeight(windowBounds));
 
         return badBounds;
     }
diff --git a/window/window/src/test/java/androidx/window/TestWindow.java b/window/window/src/test/java/androidx/window/TestWindow.java
new file mode 100644
index 0000000..8e3591f
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/TestWindow.java
@@ -0,0 +1,293 @@
+/*
+ * 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.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.InputQueue;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class TestWindow extends Window {
+
+    private View mDecorView;
+
+    public TestWindow(Context context) {
+        this(context, mock(View.class));
+    }
+
+    public TestWindow(Context context, View decorView) {
+        super(context);
+        mDecorView = decorView;
+    }
+
+    @Override
+    public void takeSurface(SurfaceHolder.Callback2 callback) {
+
+    }
+
+    @Override
+    public void takeInputQueue(InputQueue.Callback callback) {
+
+    }
+
+    @Override
+    public boolean isFloating() {
+        return false;
+    }
+
+    @Override
+    public void setContentView(int layoutResID) {
+
+    }
+
+    @Override
+    public void setContentView(View view) {
+
+    }
+
+    @Override
+    public void setContentView(View view, ViewGroup.LayoutParams params) {
+
+    }
+
+    @Override
+    public void addContentView(View view, ViewGroup.LayoutParams params) {
+
+    }
+
+    @Nullable
+    @Override
+    public View getCurrentFocus() {
+        return null;
+    }
+
+    @NonNull
+    @Override
+    public LayoutInflater getLayoutInflater() {
+        return null;
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+
+    }
+
+    @Override
+    public void setTitleColor(int textColor) {
+
+    }
+
+    @Override
+    public void openPanel(int featureId, KeyEvent event) {
+
+    }
+
+    @Override
+    public void closePanel(int featureId) {
+
+    }
+
+    @Override
+    public void togglePanel(int featureId, KeyEvent event) {
+
+    }
+
+    @Override
+    public void invalidatePanelMenu(int featureId) {
+
+    }
+
+    @Override
+    public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
+        return false;
+    }
+
+    @Override
+    public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
+        return false;
+    }
+
+    @Override
+    public void closeAllPanels() {
+
+    }
+
+    @Override
+    public boolean performContextMenuIdentifierAction(int id, int flags) {
+        return false;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+
+    }
+
+    @Override
+    public void setBackgroundDrawable(Drawable drawable) {
+
+    }
+
+    @Override
+    public void setFeatureDrawableResource(int featureId, int resId) {
+
+    }
+
+    @Override
+    public void setFeatureDrawableUri(int featureId, Uri uri) {
+
+    }
+
+    @Override
+    public void setFeatureDrawable(int featureId, Drawable drawable) {
+
+    }
+
+    @Override
+    public void setFeatureDrawableAlpha(int featureId, int alpha) {
+
+    }
+
+    @Override
+    public void setFeatureInt(int featureId, int value) {
+
+    }
+
+    @Override
+    public void takeKeyEvents(boolean get) {
+
+    }
+
+    @Override
+    public boolean superDispatchKeyEvent(KeyEvent event) {
+        return false;
+    }
+
+    @Override
+    public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
+        return false;
+    }
+
+    @Override
+    public boolean superDispatchTouchEvent(MotionEvent event) {
+        return false;
+    }
+
+    @Override
+    public boolean superDispatchTrackballEvent(MotionEvent event) {
+        return false;
+    }
+
+    @Override
+    public boolean superDispatchGenericMotionEvent(MotionEvent event) {
+        return false;
+    }
+
+    @NonNull
+    @Override
+    public View getDecorView() {
+        return mDecorView;
+    }
+
+    @Override
+    public View peekDecorView() {
+        return null;
+    }
+
+    @Override
+    public Bundle saveHierarchyState() {
+        return null;
+    }
+
+    @Override
+    public void restoreHierarchyState(Bundle savedInstanceState) {
+
+    }
+
+    @Override
+    protected void onActive() {
+
+    }
+
+    @Override
+    public void setChildDrawable(int featureId, Drawable drawable) {
+
+    }
+
+    @Override
+    public void setChildInt(int featureId, int value) {
+
+    }
+
+    @Override
+    public boolean isShortcutKey(int keyCode, KeyEvent event) {
+        return false;
+    }
+
+    @Override
+    public void setVolumeControlStream(int streamType) {
+
+    }
+
+    @Override
+    public int getVolumeControlStream() {
+        return 0;
+    }
+
+    @Override
+    public int getStatusBarColor() {
+        return 0;
+    }
+
+    @Override
+    public void setStatusBarColor(int color) {
+
+    }
+
+    @Override
+    public int getNavigationBarColor() {
+        return 0;
+    }
+
+    @Override
+    public void setNavigationBarColor(int color) {
+
+    }
+
+    @Override
+    public void setDecorCaptionShade(int decorCaptionShade) {
+
+    }
+
+    @Override
+    public void setResizingCaptionDrawable(Drawable drawable) {
+
+    }
+}
diff --git a/window/window/src/test/java/androidx/window/TestWindowBoundsHelper.java b/window/window/src/test/java/androidx/window/TestWindowBoundsHelper.java
new file mode 100644
index 0000000..15a069e
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/TestWindowBoundsHelper.java
@@ -0,0 +1,100 @@
+/*
+ * 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.app.Activity;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.HashMap;
+
+/**
+ * Subclass of {@link WindowBoundsHelper} used to override the results for testing.
+ *
+ * @see WindowBoundsHelper
+ * @see WindowBoundsHelper#setForTesting(WindowBoundsHelper)
+ */
+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
+     * bounds} has the effect of clearing the bounds override.
+     * <p>
+     * Note: A global override set as a result of {@link #setCurrentBounds(Rect)} takes precedence
+     * over the value set with this method.
+     */
+    void setCurrentBoundsForActivity(@NonNull Activity activity, @Nullable Rect bounds) {
+        mOverriddenBounds.put(activity, bounds);
+    }
+
+    /**
+     * 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.
+     */
+    void setCurrentBounds(@Nullable Rect bounds) {
+        mGlobalOverriddenBounds = bounds;
+    }
+
+    @Override
+    @NonNull
+    Rect computeCurrentWindowBounds(Activity activity) {
+        if (mGlobalOverriddenBounds != null) {
+            return mGlobalOverriddenBounds;
+        }
+
+        Rect bounds = mOverriddenBounds.get(activity);
+        if (bounds != null) {
+            return bounds;
+        }
+
+        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)}.
+     */
+    void reset() {
+        mGlobalOverriddenBounds = null;
+        mOverriddenBounds.clear();
+        mOverriddenMaximumBounds.clear();
+    }
+}
diff --git a/work/README.md b/work/README.md
index faa3127..17860ca 100644
--- a/work/README.md
+++ b/work/README.md
@@ -8,7 +8,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/work)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/work)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/work)
 
 [Reference documentation](https://developer.android.com/reference/androidx/work/package-summary)
 
@@ -16,4 +16,4 @@
 
 [File a new bug](https://issuetracker.google.com/issues/new?component=409906)
 
-[Contributing from GitHub](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/CONTRIBUTING.md)
+[Contributing from GitHub](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/CONTRIBUTING.md)
diff --git a/work/workmanager-inspection/src/androidTest/java/androidx/work/inspection/WorkInfoTest.kt b/work/workmanager-inspection/src/androidTest/java/androidx/work/inspection/WorkInfoTest.kt
index f1d084b..26c2af0 100644
--- a/work/workmanager-inspection/src/androidTest/java/androidx/work/inspection/WorkInfoTest.kt
+++ b/work/workmanager-inspection/src/androidTest/java/androidx/work/inspection/WorkInfoTest.kt
@@ -30,7 +30,6 @@
 import androidx.work.inspection.worker.IdleWorker
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.runBlocking
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -56,7 +55,6 @@
             }
     }
 
-    @Ignore("b/172833278")
     @Test
     fun addAndRemoveWork() = runBlocking {
         inspectWorkManager()
@@ -75,7 +73,6 @@
         }
     }
 
-    @Ignore("b/172833278")
     @Test
     fun sendWorkAddedEvent() = runBlocking {
         inspectWorkManager()
@@ -89,7 +86,6 @@
         }
     }
 
-    @Ignore("b/172833278")
     @Test
     fun updateWorkInfoState() = runBlocking {
         inspectWorkManager()
@@ -102,7 +98,6 @@
         }
     }
 
-    @Ignore("b/172833278")
     @Test
     fun updateWorkInfoRetryCount() = runBlocking {
         inspectWorkManager()
@@ -115,7 +110,6 @@
         }
     }
 
-    @Ignore("b/172833278")
     @Test
     fun updateWorkInfoOutputData() = runBlocking {
         inspectWorkManager()
@@ -135,7 +129,6 @@
         }
     }
 
-    @Ignore("b/172833278")
     @Test
     fun updateWorkInfoScheduleRequestedAt() = runBlocking {
         inspectWorkManager()
@@ -149,7 +142,6 @@
         }
     }
 
-    @Ignore("b/172833278")
     @Test
     fun runEntryHook_getCallStackWithWorkAddedEvent() = runBlocking {
         inspectWorkManager()
@@ -174,7 +166,6 @@
         }
     }
 
-    @Ignore("b/172833278")
     @Test
     fun addChainingWorkWithUniqueName() = runBlocking {
         inspectWorkManager()
@@ -203,7 +194,6 @@
         }
     }
 
-    @Ignore("b/172833278")
     @Test
     fun cancelWork() = runBlocking {
         inspectWorkManager()
diff --git a/work/workmanager-inspection/src/androidTest/java/androidx/work/inspection/WorkManagerInspectorTestEnvironment.kt b/work/workmanager-inspection/src/androidTest/java/androidx/work/inspection/WorkManagerInspectorTestEnvironment.kt
index 4a0f566..f60b1d2 100644
--- a/work/workmanager-inspection/src/androidTest/java/androidx/work/inspection/WorkManagerInspectorTestEnvironment.kt
+++ b/work/workmanager-inspection/src/androidTest/java/androidx/work/inspection/WorkManagerInspectorTestEnvironment.kt
@@ -69,9 +69,9 @@
             workManager.cancelAllWork()
             workManager.pruneWork()
             application.executor.runAllCommands()
+            inspectorTester.dispose()
             job.cancelAndJoin()
         }
-        inspectorTester.dispose()
     }
 
     @OptIn(ExperimentalCoroutinesApi::class)
diff --git a/work/workmanager-inspection/src/main/java/androidx/work/inspection/WorkManagerInspector.kt b/work/workmanager-inspection/src/main/java/androidx/work/inspection/WorkManagerInspector.kt
index 6a41f13..1d63f91 100644
--- a/work/workmanager-inspection/src/main/java/androidx/work/inspection/WorkManagerInspector.kt
+++ b/work/workmanager-inspection/src/main/java/androidx/work/inspection/WorkManagerInspector.kt
@@ -42,6 +42,7 @@
 import androidx.work.inspection.WorkManagerInspectorProtocol.WorkRemovedEvent
 import androidx.work.inspection.WorkManagerInspectorProtocol.WorkUpdatedEvent
 import java.util.UUID
+import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
 
 /**
@@ -267,9 +268,14 @@
 
     override fun onDispose() {
         super.onDispose()
+        val latch = CountDownLatch(1)
         mainHandler.post {
             lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
+            latch.countDown()
         }
+        // await to make sure that all observers that registered by inspector are gone
+        // otherwise they can post message to "disposed" inspector
+        latch.await()
     }
 
     override fun getLifecycle(): Lifecycle {
diff --git a/work/workmanager-multiprocess/src/main/java/androidx/work/multiprocess/RemoteCallback.java b/work/workmanager-multiprocess/src/main/java/androidx/work/multiprocess/RemoteCallback.java
index 0f99eabb..f274a86 100644
--- a/work/workmanager-multiprocess/src/main/java/androidx/work/multiprocess/RemoteCallback.java
+++ b/work/workmanager-multiprocess/src/main/java/androidx/work/multiprocess/RemoteCallback.java
@@ -25,6 +25,8 @@
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.util.NoSuchElementException;
+
 /**
  * Manages callbacks from {@link IWorkManagerImpl}.
  *
@@ -81,7 +83,14 @@
 
     private void unlinkToDeath() {
         if (mBinder != null) {
-            mBinder.unlinkToDeath(mRecipient, 0);
+            try {
+                mBinder.unlinkToDeath(mRecipient, 0);
+            } catch (NoSuchElementException ignore) {
+                // Sometimes trying to link a death recipient to a binder itself might fail
+                // because the designated process might have crashed.
+                // In such cases trying to unlink will fail because there may not be a registered
+                // recipient
+            }
         }
     }
 
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/WorkConstraintsTrackerTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/WorkConstraintsTrackerTest.java
index 711f1e67..08dc032 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/WorkConstraintsTrackerTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/WorkConstraintsTrackerTest.java
@@ -74,6 +74,7 @@
         mWorkConstraintsTracker = new WorkConstraintsTracker(mCallback, controllers);
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void testReplace() {
         List<WorkSpec> emptyList = Collections.emptyList();
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/controllers/ConstraintControllerTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/controllers/ConstraintControllerTest.java
index 5e48bef..5043e34 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/controllers/ConstraintControllerTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/controllers/ConstraintControllerTest.java
@@ -46,6 +46,7 @@
 @SdkSuppress(minSdkVersion = 23)
 public class ConstraintControllerTest extends WorkManagerTest {
     private TestDeviceIdleConstraintController mTestIdleController;
+    @SuppressWarnings("unchecked")
     private ConstraintTracker<Boolean> mMockTracker = mock(ConstraintTracker.class);
     private ConstraintController.OnConstraintUpdatedCallback mCallback =
             mock(ConstraintController.OnConstraintUpdatedCallback.class);
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/BatteryChargingTrackerTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/BatteryChargingTrackerTest.java
index c59a654..f8c9da7 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/BatteryChargingTrackerTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/BatteryChargingTrackerTest.java
@@ -54,6 +54,7 @@
     private ConstraintListener<Boolean> mListener;
     private Context mMockContext;
 
+    @SuppressWarnings("unchecked")
     @Before
     public void setUp() {
         mMockContext = mock(Context.class);
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/BatteryNotLowTrackerTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/BatteryNotLowTrackerTest.java
index 8a593d5..f37c684 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/BatteryNotLowTrackerTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/BatteryNotLowTrackerTest.java
@@ -54,6 +54,7 @@
     private BatteryNotLowTracker mTracker;
     private ConstraintListener<Boolean> mListener;
 
+    @SuppressWarnings("unchecked")
     @Before
     public void setUp() {
         mMockContext = mock(Context.class);
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/ConstraintTrackerTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/ConstraintTrackerTest.java
index fc04e38..b3ee2dc 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/ConstraintTrackerTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/ConstraintTrackerTest.java
@@ -35,6 +35,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+@SuppressWarnings("unchecked")
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ConstraintTrackerTest {
@@ -79,6 +80,7 @@
         assertThat(mTracker.mGetInitialStateCount, is(1));
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void testAddListener_sameListener_doesNotCallGetInitialStateAndStartTrackingTwice() {
         ConstraintListener<Boolean> constraintListener = mock(ConstraintListener.class);
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/NetworkStateTrackerTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/NetworkStateTrackerTest.java
index 38a03b9..0313e43 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/NetworkStateTrackerTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/NetworkStateTrackerTest.java
@@ -49,6 +49,7 @@
     private Context mMockContext;
     private ConnectivityManager mMockConnectivityManager;
 
+    @SuppressWarnings("unchecked")
     @Before
     public void setUp() {
         mMockContext = mock(Context.class);
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/StorageNotLowTrackerTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/StorageNotLowTrackerTest.java
index 59efc3f..af162d8 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/StorageNotLowTrackerTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/constraints/trackers/StorageNotLowTrackerTest.java
@@ -47,6 +47,7 @@
     private ConstraintListener<Boolean> mListener;
     private Context mMockContext;
 
+    @SuppressWarnings("unchecked")
     @Before
     public void setUp() {
         mMockContext = mock(Context.class);
diff --git a/work/workmanager/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java b/work/workmanager/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java
index fcfea94..f763f24 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java
@@ -176,7 +176,11 @@
                 }
             }
         }
-        if (removedInfo != null && mCallback != null) {
+        // Keep track of the reference and use that when cancelling Notification. This is because
+        // the work-testing library uses a direct executor and does *not* call this method
+        // on the main thread.
+        Callback callback = mCallback;
+        if (removedInfo != null && callback != null) {
             // Explicitly decrement the reference count for the notification
 
             // We are doing this without having to wait for the handleStop() to clean up
@@ -191,7 +195,7 @@
                             workSpecId,
                             removedInfo.getForegroundServiceType())
             );
-            mCallback.cancelNotification(removedInfo.getNotificationId());
+            callback.cancelNotification(removedInfo.getNotificationId());
         }
     }